13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
18
from cStringIO import StringIO
21
20
from bzrlib.lazy_import import lazy_import
22
21
lazy_import(globals(), """
23
from itertools import chain
22
from copy import deepcopy
23
from unittest import TestSuite
24
from warnings import warn
24
27
from bzrlib import (
27
30
config as _mod_config,
34
35
revision as _mod_revision,
42
from bzrlib.config import BranchConfig, TransportConfig
43
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
42
from bzrlib.config import BranchConfig, TreeConfig
43
from bzrlib.lockable_files import LockableFiles, TransportLock
44
44
from bzrlib.tag import (
50
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
51
from bzrlib.hooks import HookPoint, Hooks
52
from bzrlib.inter import InterObject
53
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
54
from bzrlib import registry
55
from bzrlib.symbol_versioning import (
59
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
50
from bzrlib.decorators import needs_read_lock, needs_write_lock
51
from bzrlib.errors import (BzrError, BzrCheckError, DivergedBranches,
52
HistoryMissing, InvalidRevisionId,
53
InvalidRevisionNumber, LockError, NoSuchFile,
54
NoSuchRevision, NoWorkingTree, NotVersionedError,
55
NotBranchError, UninitializableFormat,
56
UnlistableStore, UnlistableBranch,
58
from bzrlib.hooks import Hooks
59
from bzrlib.symbol_versioning import (deprecated_function,
63
zero_eight, zero_nine, zero_sixteen,
65
from bzrlib.trace import mutter, note
62
68
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
122
117
if master is not None:
123
118
master.break_lock()
125
def _check_stackable_repo(self):
126
if not self.repository._format.supports_external_lookups:
127
raise errors.UnstackableRepositoryFormat(self.repository._format,
128
self.repository.base)
130
def _extend_partial_history(self, stop_index=None, stop_revision=None):
131
"""Extend the partial history to include a given index
133
If a stop_index is supplied, stop when that index has been reached.
134
If a stop_revision is supplied, stop when that revision is
135
encountered. Otherwise, stop when the beginning of history is
138
:param stop_index: The index which should be present. When it is
139
present, history extension will stop.
140
:param stop_revision: The revision id which should be present. When
141
it is encountered, history extension will stop.
143
if len(self._partial_revision_history_cache) == 0:
144
self._partial_revision_history_cache = [self.last_revision()]
145
repository._iter_for_revno(
146
self.repository, self._partial_revision_history_cache,
147
stop_index=stop_index, stop_revision=stop_revision)
148
if self._partial_revision_history_cache[-1] == _mod_revision.NULL_REVISION:
149
self._partial_revision_history_cache.pop()
151
def _get_check_refs(self):
152
"""Get the references needed for check().
156
revid = self.last_revision()
157
return [('revision-existence', revid), ('lefthand-distance', revid)]
160
def open(base, _unsupported=False, possible_transports=None):
121
@deprecated_method(zero_eight)
122
def open_downlevel(base):
123
"""Open a branch which may be of an old format."""
124
return Branch.open(base, _unsupported=True)
127
def open(base, _unsupported=False):
161
128
"""Open the branch rooted at base.
163
130
For instance, if the branch is at URL/.bzr/branch,
164
131
Branch.open(URL) -> a Branch instance.
166
control = bzrdir.BzrDir.open(base, _unsupported,
167
possible_transports=possible_transports)
168
return control.open_branch(unsupported=_unsupported)
171
def open_from_transport(transport, name=None, _unsupported=False):
172
"""Open the branch rooted at transport"""
173
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
174
return control.open_branch(name=name, unsupported=_unsupported)
177
def open_containing(url, possible_transports=None):
133
control = bzrdir.BzrDir.open(base, _unsupported)
134
return control.open_branch(_unsupported)
137
def open_containing(url):
178
138
"""Open an existing branch which contains url.
180
140
This probes for a branch at url, and searches upwards from there.
182
142
Basically we keep looking up until we find the control directory or
183
143
run into the root. If there isn't one, raises NotBranchError.
184
If there is one and it is either an unrecognised format or an unsupported
144
If there is one and it is either an unrecognised format or an unsupported
185
145
format, UnknownFormatError or UnsupportedFormatError are raised.
186
146
If there is one, it is returned, along with the unused portion of url.
188
control, relpath = bzrdir.BzrDir.open_containing(url,
148
control, relpath = bzrdir.BzrDir.open_containing(url)
190
149
return control.open_branch(), relpath
192
def _push_should_merge_tags(self):
193
"""Should _basic_push merge this branch's tags into the target?
195
The default implementation returns False if this branch has no tags,
196
and True the rest of the time. Subclasses may override this.
198
return self.supports_tags() and self.tags.get_tag_dict()
152
@deprecated_function(zero_eight)
153
def initialize(base):
154
"""Create a new working tree and branch, rooted at 'base' (url)
156
NOTE: This will soon be deprecated in favour of creation
159
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
161
@deprecated_function(zero_eight)
162
def setup_caching(self, cache_root):
163
"""Subclasses that care about caching should override this, and set
164
up cached stores located under cache_root.
166
NOTE: This is unused.
200
170
def get_config(self):
201
"""Get a bzrlib.config.BranchConfig for this Branch.
203
This can then be used to get and set configuration options for the
206
:return: A bzrlib.config.BranchConfig.
208
171
return BranchConfig(self)
210
def _get_config(self):
211
"""Get the concrete config for just the config in this branch.
213
This is not intended for client use; see Branch.get_config for the
218
:return: An object supporting get_option and set_option.
220
raise NotImplementedError(self._get_config)
222
def _get_fallback_repository(self, url):
223
"""Get the repository we fallback to at url."""
224
url = urlutils.join(self.base, url)
225
a_branch = Branch.open(url,
226
possible_transports=[self.bzrdir.root_transport])
227
return a_branch.repository
229
def _get_tags_bytes(self):
230
"""Get the bytes of a serialised tags dict.
232
Note that not all branches support tags, nor do all use the same tags
233
logic: this method is specific to BasicTags. Other tag implementations
234
may use the same method name and behave differently, safely, because
235
of the double-dispatch via
236
format.make_tags->tags_instance->get_tags_dict.
238
:return: The bytes of the tags file.
239
:seealso: Branch._set_tags_bytes.
241
return self._transport.get_bytes('tags')
243
def _get_nick(self, local=False, possible_transports=None):
244
config = self.get_config()
245
# explicit overrides master, but don't look for master if local is True
246
if not local and not config.has_explicit_nickname():
248
master = self.get_master_branch(possible_transports)
249
if master and self.user_url == master.user_url:
250
raise errors.RecursiveBind(self.user_url)
251
if master is not None:
252
# return the master branch value
254
except errors.RecursiveBind, e:
256
except errors.BzrError, e:
257
# Silently fall back to local implicit nick if the master is
259
mutter("Could not connect to bound branch, "
260
"falling back to local nick.\n " + str(e))
261
return config.get_nickname()
174
return self.get_config().get_nickname()
263
176
def _set_nick(self, nick):
264
self.get_config().set_user_option('nickname', nick, warn_masked=True)
177
self.get_config().set_user_option('nickname', nick)
266
179
nick = property(_get_nick, _set_nick)
268
181
def is_locked(self):
269
182
raise NotImplementedError(self.is_locked)
271
def _lefthand_history(self, revision_id, last_rev=None,
273
if 'evil' in debug.debug_flags:
274
mutter_callsite(4, "_lefthand_history scales with history.")
275
# stop_revision must be a descendant of last_revision
276
graph = self.repository.get_graph()
277
if last_rev is not None:
278
if not graph.is_ancestor(last_rev, revision_id):
279
# our previous tip is not merged into stop_revision
280
raise errors.DivergedBranches(self, other_branch)
281
# make a new revision history from the graph
282
parents_map = graph.get_parent_map([revision_id])
283
if revision_id not in parents_map:
284
raise errors.NoSuchRevision(self, revision_id)
285
current_rev_id = revision_id
287
check_not_reserved_id = _mod_revision.check_not_reserved_id
288
# Do not include ghosts or graph origin in revision_history
289
while (current_rev_id in parents_map and
290
len(parents_map[current_rev_id]) > 0):
291
check_not_reserved_id(current_rev_id)
292
new_history.append(current_rev_id)
293
current_rev_id = parents_map[current_rev_id][0]
294
parents_map = graph.get_parent_map([current_rev_id])
295
new_history.reverse()
298
def lock_write(self, token=None):
299
"""Lock the branch for write operations.
301
:param token: A token to permit reacquiring a previously held and
303
:return: A BranchWriteLockResult.
184
def lock_write(self):
305
185
raise NotImplementedError(self.lock_write)
307
187
def lock_read(self):
308
"""Lock the branch for read operations.
310
:return: A bzrlib.lock.LogicalLockResult.
312
188
raise NotImplementedError(self.lock_read)
314
190
def unlock(self):
416
228
:return: A dictionary mapping revision_id => dotted revno.
230
last_revision = self.last_revision()
231
revision_graph = self.repository.get_revision_graph(last_revision)
232
merge_sorted_revisions = tsort.merge_sort(
418
237
revision_id_to_revno = dict((rev_id, revno)
419
for rev_id, depth, revno, end_of_merge
420
in self.iter_merge_sorted_revisions())
238
for seq_num, rev_id, depth, revno, end_of_merge
239
in merge_sorted_revisions)
421
240
return revision_id_to_revno
424
def iter_merge_sorted_revisions(self, start_revision_id=None,
425
stop_revision_id=None, stop_rule='exclude', direction='reverse'):
426
"""Walk the revisions for a branch in merge sorted order.
428
Merge sorted order is the output from a merge-aware,
429
topological sort, i.e. all parents come before their
430
children going forward; the opposite for reverse.
432
:param start_revision_id: the revision_id to begin walking from.
433
If None, the branch tip is used.
434
:param stop_revision_id: the revision_id to terminate the walk
435
after. If None, the rest of history is included.
436
:param stop_rule: if stop_revision_id is not None, the precise rule
437
to use for termination:
438
* 'exclude' - leave the stop revision out of the result (default)
439
* 'include' - the stop revision is the last item in the result
440
* 'with-merges' - include the stop revision and all of its
441
merged revisions in the result
442
* 'with-merges-without-common-ancestry' - filter out revisions
443
that are in both ancestries
444
:param direction: either 'reverse' or 'forward':
445
* reverse means return the start_revision_id first, i.e.
446
start at the most recent revision and go backwards in history
447
* forward returns tuples in the opposite order to reverse.
448
Note in particular that forward does *not* do any intelligent
449
ordering w.r.t. depth as some clients of this API may like.
450
(If required, that ought to be done at higher layers.)
452
:return: an iterator over (revision_id, depth, revno, end_of_merge)
455
* revision_id: the unique id of the revision
456
* depth: How many levels of merging deep this node has been
458
* revno_sequence: This field provides a sequence of
459
revision numbers for all revisions. The format is:
460
(REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
461
branch that the revno is on. From left to right the REVNO numbers
462
are the sequence numbers within that branch of the revision.
463
* end_of_merge: When True the next node (earlier in history) is
464
part of a different merge.
466
# Note: depth and revno values are in the context of the branch so
467
# we need the full graph to get stable numbers, regardless of the
469
if self._merge_sorted_revisions_cache is None:
470
last_revision = self.last_revision()
471
known_graph = self.repository.get_known_graph_ancestry(
473
self._merge_sorted_revisions_cache = known_graph.merge_sort(
475
filtered = self._filter_merge_sorted_revisions(
476
self._merge_sorted_revisions_cache, start_revision_id,
477
stop_revision_id, stop_rule)
478
# Make sure we don't return revisions that are not part of the
479
# start_revision_id ancestry.
480
filtered = self._filter_start_non_ancestors(filtered)
481
if direction == 'reverse':
483
if direction == 'forward':
484
return reversed(list(filtered))
486
raise ValueError('invalid direction %r' % direction)
488
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
489
start_revision_id, stop_revision_id, stop_rule):
490
"""Iterate over an inclusive range of sorted revisions."""
491
rev_iter = iter(merge_sorted_revisions)
492
if start_revision_id is not None:
493
for node in rev_iter:
494
rev_id = node.key[-1]
495
if rev_id != start_revision_id:
498
# The decision to include the start or not
499
# depends on the stop_rule if a stop is provided
500
# so pop this node back into the iterator
501
rev_iter = chain(iter([node]), rev_iter)
503
if stop_revision_id is None:
505
for node in rev_iter:
506
rev_id = node.key[-1]
507
yield (rev_id, node.merge_depth, node.revno,
509
elif stop_rule == 'exclude':
510
for node in rev_iter:
511
rev_id = node.key[-1]
512
if rev_id == stop_revision_id:
514
yield (rev_id, node.merge_depth, node.revno,
516
elif stop_rule == 'include':
517
for node in rev_iter:
518
rev_id = node.key[-1]
519
yield (rev_id, node.merge_depth, node.revno,
521
if rev_id == stop_revision_id:
523
elif stop_rule == 'with-merges-without-common-ancestry':
524
# We want to exclude all revisions that are already part of the
525
# stop_revision_id ancestry.
526
graph = self.repository.get_graph()
527
ancestors = graph.find_unique_ancestors(start_revision_id,
529
for node in rev_iter:
530
rev_id = node.key[-1]
531
if rev_id not in ancestors:
533
yield (rev_id, node.merge_depth, node.revno,
535
elif stop_rule == 'with-merges':
536
stop_rev = self.repository.get_revision(stop_revision_id)
537
if stop_rev.parent_ids:
538
left_parent = stop_rev.parent_ids[0]
540
left_parent = _mod_revision.NULL_REVISION
541
# left_parent is the actual revision we want to stop logging at,
542
# since we want to show the merged revisions after the stop_rev too
543
reached_stop_revision_id = False
544
revision_id_whitelist = []
545
for node in rev_iter:
546
rev_id = node.key[-1]
547
if rev_id == left_parent:
548
# reached the left parent after the stop_revision
550
if (not reached_stop_revision_id or
551
rev_id in revision_id_whitelist):
552
yield (rev_id, node.merge_depth, node.revno,
554
if reached_stop_revision_id or rev_id == stop_revision_id:
555
# only do the merged revs of rev_id from now on
556
rev = self.repository.get_revision(rev_id)
558
reached_stop_revision_id = True
559
revision_id_whitelist.extend(rev.parent_ids)
561
raise ValueError('invalid stop_rule %r' % stop_rule)
563
def _filter_start_non_ancestors(self, rev_iter):
564
# If we started from a dotted revno, we want to consider it as a tip
565
# and don't want to yield revisions that are not part of its
566
# ancestry. Given the order guaranteed by the merge sort, we will see
567
# uninteresting descendants of the first parent of our tip before the
569
first = rev_iter.next()
570
(rev_id, merge_depth, revno, end_of_merge) = first
573
# We start at a mainline revision so by definition, all others
574
# revisions in rev_iter are ancestors
575
for node in rev_iter:
580
pmap = self.repository.get_parent_map([rev_id])
581
parents = pmap.get(rev_id, [])
583
whitelist.update(parents)
585
# If there is no parents, there is nothing of interest left
587
# FIXME: It's hard to test this scenario here as this code is never
588
# called in that case. -- vila 20100322
591
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
593
if rev_id in whitelist:
594
pmap = self.repository.get_parent_map([rev_id])
595
parents = pmap.get(rev_id, [])
596
whitelist.remove(rev_id)
597
whitelist.update(parents)
599
# We've reached the mainline, there is nothing left to
603
# A revision that is not part of the ancestry of our
606
yield (rev_id, merge_depth, revno, end_of_merge)
608
242
def leave_lock_in_place(self):
609
243
"""Tell this branch object not to release the physical lock when this
610
244
object is unlocked.
612
246
If lock_write doesn't return a token, then this method is not supported.
614
248
self.control_files.leave_in_place()
720
352
The delta is relative to its mainline predecessor, or the
721
353
empty tree for revision 1.
355
assert isinstance(revno, int)
723
356
rh = self.revision_history()
724
357
if not (1 <= revno <= len(rh)):
725
raise errors.InvalidRevisionNumber(revno)
358
raise InvalidRevisionNumber(revno)
726
359
return self.repository.get_revision_delta(rh[revno-1])
728
def get_stacked_on_url(self):
729
"""Get the URL this branch is stacked against.
361
@deprecated_method(zero_sixteen)
362
def get_root_id(self):
363
"""Return the id of this branches root
731
:raises NotStacked: If the branch is not stacked.
732
:raises UnstackableBranchFormat: If the branch does not support
365
Deprecated: branches don't have root ids-- trees do.
366
Use basis_tree().get_root_id() instead.
735
raise NotImplementedError(self.get_stacked_on_url)
368
raise NotImplementedError(self.get_root_id)
737
370
def print_file(self, file, revision_id):
738
371
"""Print `file` to stdout."""
739
372
raise NotImplementedError(self.print_file)
374
def append_revision(self, *revision_ids):
375
raise NotImplementedError(self.append_revision)
741
377
def set_revision_history(self, rev_history):
742
378
raise NotImplementedError(self.set_revision_history)
745
def set_parent(self, url):
746
"""See Branch.set_parent."""
747
# TODO: Maybe delete old location files?
748
# URLs should never be unicode, even on the local fs,
749
# FIXUP this and get_parent in a future branch format bump:
750
# read and rewrite the file. RBC 20060125
752
if isinstance(url, unicode):
754
url = url.encode('ascii')
755
except UnicodeEncodeError:
756
raise errors.InvalidURL(url,
757
"Urls must be 7-bit ascii, "
758
"use bzrlib.urlutils.escape")
759
url = urlutils.relative_url(self.base, url)
760
self._set_parent_location(url)
763
def set_stacked_on_url(self, url):
764
"""Set the URL this branch is stacked against.
766
:raises UnstackableBranchFormat: If the branch does not support
768
:raises UnstackableRepositoryFormat: If the repository does not support
771
if not self._format.supports_stacking():
772
raise errors.UnstackableBranchFormat(self._format, self.user_url)
773
# XXX: Changing from one fallback repository to another does not check
774
# that all the data you need is present in the new fallback.
775
# Possibly it should.
776
self._check_stackable_repo()
779
old_url = self.get_stacked_on_url()
780
except (errors.NotStacked, errors.UnstackableBranchFormat,
781
errors.UnstackableRepositoryFormat):
785
self._activate_fallback_location(url)
786
# write this out after the repository is stacked to avoid setting a
787
# stacked config that doesn't work.
788
self._set_config_location('stacked_on_location', url)
791
"""Change a branch to be unstacked, copying data as needed.
793
Don't call this directly, use set_stacked_on_url(None).
795
pb = ui.ui_factory.nested_progress_bar()
797
pb.update("Unstacking")
798
# The basic approach here is to fetch the tip of the branch,
799
# including all available ghosts, from the existing stacked
800
# repository into a new repository object without the fallbacks.
802
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
803
# correct for CHKMap repostiories
804
old_repository = self.repository
805
if len(old_repository._fallback_repositories) != 1:
806
raise AssertionError("can't cope with fallback repositories "
807
"of %r" % (self.repository,))
808
# Open the new repository object.
809
# Repositories don't offer an interface to remove fallback
810
# repositories today; take the conceptually simpler option and just
811
# reopen it. We reopen it starting from the URL so that we
812
# get a separate connection for RemoteRepositories and can
813
# stream from one of them to the other. This does mean doing
814
# separate SSH connection setup, but unstacking is not a
815
# common operation so it's tolerable.
816
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
817
new_repository = new_bzrdir.find_repository()
818
if new_repository._fallback_repositories:
819
raise AssertionError("didn't expect %r to have "
820
"fallback_repositories"
821
% (self.repository,))
822
# Replace self.repository with the new repository.
823
# Do our best to transfer the lock state (i.e. lock-tokens and
824
# lock count) of self.repository to the new repository.
825
lock_token = old_repository.lock_write().repository_token
826
self.repository = new_repository
827
if isinstance(self, remote.RemoteBranch):
828
# Remote branches can have a second reference to the old
829
# repository that need to be replaced.
830
if self._real_branch is not None:
831
self._real_branch.repository = new_repository
832
self.repository.lock_write(token=lock_token)
833
if lock_token is not None:
834
old_repository.leave_lock_in_place()
835
old_repository.unlock()
836
if lock_token is not None:
837
# XXX: self.repository.leave_lock_in_place() before this
838
# function will not be preserved. Fortunately that doesn't
839
# affect the current default format (2a), and would be a
840
# corner-case anyway.
841
# - Andrew Bennetts, 2010/06/30
842
self.repository.dont_leave_lock_in_place()
846
old_repository.unlock()
847
except errors.LockNotHeld:
850
if old_lock_count == 0:
851
raise AssertionError(
852
'old_repository should have been locked at least once.')
853
for i in range(old_lock_count-1):
854
self.repository.lock_write()
855
# Fetch from the old repository into the new.
856
old_repository.lock_read()
858
# XXX: If you unstack a branch while it has a working tree
859
# with a pending merge, the pending-merged revisions will no
860
# longer be present. You can (probably) revert and remerge.
862
# XXX: This only fetches up to the tip of the repository; it
863
# doesn't bring across any tags. That's fairly consistent
864
# with how branch works, but perhaps not ideal.
865
self.repository.fetch(old_repository,
866
revision_id=self.last_revision(),
869
old_repository.unlock()
873
def _set_tags_bytes(self, bytes):
874
"""Mirror method for _get_tags_bytes.
876
:seealso: Branch._get_tags_bytes.
878
return _run_with_write_locked_target(self, self._transport.put_bytes,
881
380
def _cache_revision_history(self, rev_history):
882
381
"""Set the cached revision history to rev_history.
992
486
common_index = min(self_len, other_len) -1
993
487
if common_index >= 0 and \
994
488
self_history[common_index] != other_history[common_index]:
995
raise errors.DivergedBranches(self, other)
489
raise DivergedBranches(self, other)
997
491
if stop_revision is None:
998
492
stop_revision = other_len
494
assert isinstance(stop_revision, int)
1000
495
if stop_revision > other_len:
1001
496
raise errors.NoSuchRevision(self, stop_revision)
1002
497
return other_history[self_len:stop_revision]
1004
def update_revisions(self, other, stop_revision=None, overwrite=False,
499
def update_revisions(self, other, stop_revision=None):
1006
500
"""Pull in new perfect-fit revisions.
1008
502
:param other: Another Branch to pull from
1009
503
:param stop_revision: Updated until the given revision
1010
:param overwrite: Always set the branch pointer, rather than checking
1011
to see if it is a proper descendant.
1012
:param graph: A Graph object that can be used to query history
1013
information. This can be None.
1016
return InterBranch.get(other, self).update_revisions(stop_revision,
1019
def import_last_revision_info(self, source_repo, revno, revid):
1020
"""Set the last revision info, importing from another repo if necessary.
1022
This is used by the bound branch code to upload a revision to
1023
the master branch first before updating the tip of the local branch.
1025
:param source_repo: Source repository to optionally fetch from
1026
:param revno: Revision number of the new tip
1027
:param revid: Revision id of the new tip
1029
if not self.repository.has_same_location(source_repo):
1030
self.repository.fetch(source_repo, revision_id=revid)
1031
self.set_last_revision_info(revno, revid)
506
raise NotImplementedError(self.update_revisions)
1033
508
def revision_id_to_revno(self, revision_id):
1034
509
"""Given a revision id, return its revno"""
1035
if _mod_revision.is_null(revision_id):
510
if revision_id is None:
512
revision_id = osutils.safe_revision_id(revision_id)
1037
513
history = self.revision_history()
1039
515
return history.index(revision_id) + 1
1040
516
except ValueError:
1041
517
raise errors.NoSuchRevision(self, revision_id)
1044
519
def get_rev_id(self, revno, history=None):
1045
520
"""Find the revision id of the specified revno."""
1047
return _mod_revision.NULL_REVISION
1048
last_revno, last_revid = self.last_revision_info()
1049
if revno == last_revno:
1051
if revno <= 0 or revno > last_revno:
524
history = self.revision_history()
525
if revno <= 0 or revno > len(history):
1052
526
raise errors.NoSuchRevision(self, revno)
1053
distance_from_last = last_revno - revno
1054
if len(self._partial_revision_history_cache) <= distance_from_last:
1055
self._extend_partial_history(distance_from_last)
1056
return self._partial_revision_history_cache[distance_from_last]
527
return history[revno - 1]
1058
def pull(self, source, overwrite=False, stop_revision=None,
1059
possible_transports=None, *args, **kwargs):
529
def pull(self, source, overwrite=False, stop_revision=None):
1060
530
"""Mirror source into this branch.
1062
532
This branch is considered to be 'local', having low latency.
1064
534
:returns: PullResult instance
1066
return InterBranch.get(source, self).pull(overwrite=overwrite,
1067
stop_revision=stop_revision,
1068
possible_transports=possible_transports, *args, **kwargs)
536
raise NotImplementedError(self.pull)
1070
def push(self, target, overwrite=False, stop_revision=None, *args,
538
def push(self, target, overwrite=False, stop_revision=None):
1072
539
"""Mirror this branch into target.
1074
541
This branch is considered to be 'local', having low latency.
1076
return InterBranch.get(self, target).push(overwrite, stop_revision,
1079
def lossy_push(self, target, stop_revision=None):
1080
"""Push deltas into another branch.
1082
:note: This does not, like push, retain the revision ids from
1083
the source branch and will, rather than adding bzr-specific
1084
metadata, push only those semantics of the revision that can be
1085
natively represented by this branch' VCS.
1087
:param target: Target branch
1088
:param stop_revision: Revision to push, defaults to last revision.
1089
:return: BranchPushResult with an extra member revidmap:
1090
A dictionary mapping revision ids from the target branch
1091
to new revision ids in the target branch, for each
1092
revision that was pushed.
1094
inter = InterBranch.get(self, target)
1095
lossy_push = getattr(inter, "lossy_push", None)
1096
if lossy_push is None:
1097
raise errors.LossyPushToSameVCS(self, target)
1098
return lossy_push(stop_revision)
543
raise NotImplementedError(self.push)
1100
545
def basis_tree(self):
1101
546
"""Return `Tree` object for last revision."""
1102
547
return self.repository.revision_tree(self.last_revision())
549
def rename_one(self, from_rel, to_rel):
552
This can change the directory or the filename or both.
554
raise NotImplementedError(self.rename_one)
556
def move(self, from_paths, to_name):
559
to_name must exist as a versioned directory.
561
If to_name exists and is a directory, the files are moved into
562
it, keeping their old names. If it is a directory,
564
Note that to_name is only the last component of the new name;
565
this doesn't change the directory.
567
This returns a list of (from_path, to_path) pairs for each
570
raise NotImplementedError(self.move)
1104
572
def get_parent(self):
1105
573
"""Return the parent location of the branch.
1107
This is the default location for pull/missing. The usual
575
This is the default location for push/pull/missing. The usual
1108
576
pattern is that the user can override it by specifying a
1111
parent = self._get_parent_location()
1114
# This is an old-format absolute path to a local branch
1115
# turn it into a url
1116
if parent.startswith('/'):
1117
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1119
return urlutils.join(self.base[:-1], parent)
1120
except errors.InvalidURLJoin, e:
1121
raise errors.InaccessibleParent(parent, self.user_url)
1123
def _get_parent_location(self):
1124
raise NotImplementedError(self._get_parent_location)
579
raise NotImplementedError(self.get_parent)
1126
581
def _set_config_location(self, name, url, config=None,
1127
582
make_relative=False):
1228
659
self.check_real_revno(revno)
1230
661
def check_real_revno(self, revno):
1232
663
Check whether a revno corresponds to a real revision.
1233
664
Zero (the NULL revision) is considered invalid
1235
666
if revno < 1 or revno > self.revno():
1236
raise errors.InvalidRevisionNumber(revno)
667
raise InvalidRevisionNumber(revno)
1238
669
@needs_read_lock
1239
def clone(self, to_bzrdir, revision_id=None, repository_policy=None):
670
def clone(self, to_bzrdir, revision_id=None):
1240
671
"""Clone this branch into to_bzrdir preserving all semantic values.
1242
Most API users will want 'create_clone_on_transport', which creates a
1243
new bzrdir and branch on the fly.
1245
673
revision_id: if not None, the revision history in the new branch will
1246
674
be truncated to end with revision_id.
1248
result = to_bzrdir.create_branch()
1251
if repository_policy is not None:
1252
repository_policy.configure_branch(result)
1253
self.copy_content_into(result, revision_id=revision_id)
676
result = self._format.initialize(to_bzrdir)
677
self.copy_content_into(result, revision_id=revision_id)
1258
680
@needs_read_lock
1259
def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
681
def sprout(self, to_bzrdir, revision_id=None):
1260
682
"""Create a new line of development from the branch, into to_bzrdir.
1262
to_bzrdir controls the branch format.
1264
684
revision_id: if not None, the revision history in the new branch will
1265
685
be truncated to end with revision_id.
1267
if (repository_policy is not None and
1268
repository_policy.requires_stacking()):
1269
to_bzrdir._format.require_stacking(_skip_repo=True)
1270
result = to_bzrdir.create_branch()
1273
if repository_policy is not None:
1274
repository_policy.configure_branch(result)
1275
self.copy_content_into(result, revision_id=revision_id)
1276
result.set_parent(self.bzrdir.root_transport.base)
687
result = self._format.initialize(to_bzrdir)
688
self.copy_content_into(result, revision_id=revision_id)
689
result.set_parent(self.bzrdir.root_transport.base)
1281
692
def _synchronize_history(self, destination, revision_id):
1282
693
"""Synchronize last revision and revision history between branches.
1284
695
This version is most efficient when the destination is also a
1285
BzrBranch6, but works for BzrBranch5, as long as the destination's
1286
repository contains all the lefthand ancestors of the intended
1287
last_revision. If not, set_last_revision_info will fail.
696
BzrBranch5, but works for BzrBranch6 as long as the revision
697
history is the true lefthand parent history, and all of the revisions
698
are in the destination's repository. If not, set_revision_history
1289
701
:param destination: The branch to copy the history into
1290
702
:param revision_id: The revision-id to truncate history at. May
1291
703
be None to copy complete history.
1293
source_revno, source_revision_id = self.last_revision_info()
1294
if revision_id is None:
1295
revno, revision_id = source_revno, source_revision_id
1297
graph = self.repository.get_graph()
705
new_history = self.revision_history()
706
if revision_id is not None:
707
revision_id = osutils.safe_revision_id(revision_id)
1299
revno = graph.find_distance_to_null(revision_id,
1300
[(source_revision_id, source_revno)])
1301
except errors.GhostRevisionsHaveNoRevno:
1302
# Default to 1, if we can't find anything else
1304
destination.set_last_revision_info(revno, revision_id)
709
new_history = new_history[:new_history.index(revision_id) + 1]
711
rev = self.repository.get_revision(revision_id)
712
new_history = rev.get_history(self.repository)[1:]
713
destination.set_revision_history(new_history)
1306
716
def copy_content_into(self, destination, revision_id=None):
1307
717
"""Copy the content of self into destination.
1309
719
revision_id: if not None, the revision history in the new branch will
1310
720
be truncated to end with revision_id.
1312
return InterBranch.get(self, destination).copy_content_into(
1313
revision_id=revision_id)
1315
def update_references(self, target):
1316
if not getattr(self._format, 'supports_reference_locations', False):
1318
reference_dict = self._get_all_reference_info()
1319
if len(reference_dict) == 0:
1321
old_base = self.base
1322
new_base = target.base
1323
target_reference_dict = target._get_all_reference_info()
1324
for file_id, (tree_path, branch_location) in (
1325
reference_dict.items()):
1326
branch_location = urlutils.rebase_url(branch_location,
1328
target_reference_dict.setdefault(
1329
file_id, (tree_path, branch_location))
1330
target._set_all_reference_info(target_reference_dict)
722
self._synchronize_history(destination, revision_id)
724
parent = self.get_parent()
725
except errors.InaccessibleParent, e:
726
mutter('parent was not accessible to copy: %s', e)
729
destination.set_parent(parent)
730
self.tags.merge_to(destination.tags)
1332
732
@needs_read_lock
1333
def check(self, refs):
1334
734
"""Check consistency of the branch.
1336
736
In particular this checks that revisions given in the revision-history
1337
do actually match up in the revision graph, and that they're all
737
do actually match up in the revision graph, and that they're all
1338
738
present in the repository.
1340
740
Callers will typically also want to check the repository.
1342
:param refs: Calculated refs for this branch as specified by
1343
branch._get_check_refs()
1344
742
:return: A BranchCheckResult.
1346
result = BranchCheckResult(self)
1347
last_revno, last_revision_id = self.last_revision_info()
1348
actual_revno = refs[('lefthand-distance', last_revision_id)]
1349
if actual_revno != last_revno:
1350
result.errors.append(errors.BzrCheckError(
1351
'revno does not match len(mainline) %s != %s' % (
1352
last_revno, actual_revno)))
1353
# TODO: We should probably also check that self.revision_history
1354
# matches the repository for older branch formats.
1355
# If looking for the code that cross-checks repository parents against
1356
# the iter_reverse_revision_history output, that is now a repository
744
mainline_parent_id = None
745
for revision_id in self.revision_history():
747
revision = self.repository.get_revision(revision_id)
748
except errors.NoSuchRevision, e:
749
raise errors.BzrCheckError("mainline revision {%s} not in repository"
751
# In general the first entry on the revision history has no parents.
752
# But it's not illegal for it to have parents listed; this can happen
753
# in imports from Arch when the parents weren't reachable.
754
if mainline_parent_id is not None:
755
if mainline_parent_id not in revision.parent_ids:
756
raise errors.BzrCheckError("previous revision {%s} not listed among "
758
% (mainline_parent_id, revision_id))
759
mainline_parent_id = revision_id
760
return BranchCheckResult(self)
1360
762
def _get_checkout_format(self):
1361
763
"""Return the most suitable metadir for a checkout of this branch.
1443
810
basis_tree.unlock()
1447
def reconcile(self, thorough=True):
1448
"""Make sure the data stored in this branch is consistent."""
1449
from bzrlib.reconcile import BranchReconciler
1450
reconciler = BranchReconciler(self, thorough=thorough)
1451
reconciler.reconcile()
1454
def reference_parent(self, file_id, path, possible_transports=None):
813
def reference_parent(self, file_id, path):
1455
814
"""Return the parent branch for a tree-reference file_id
1456
815
:param file_id: The file_id of the tree reference
1457
816
:param path: The path of the file_id in the tree
1458
817
:return: A branch associated with the file_id
1460
819
# FIXME should provide multiple branches, based on config
1461
return Branch.open(self.bzrdir.root_transport.clone(path).base,
1462
possible_transports=possible_transports)
820
return Branch.open(self.bzrdir.root_transport.clone(path).base)
1464
822
def supports_tags(self):
1465
823
return self._format.supports_tags()
1467
def automatic_tag_name(self, revision_id):
1468
"""Try to automatically find the tag name for a revision.
1470
:param revision_id: Revision id of the revision.
1471
:return: A tag name or None if no tag name could be determined.
1473
for hook in Branch.hooks['automatic_tag_name']:
1474
ret = hook(self, revision_id)
1479
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1481
"""Ensure that revision_b is a descendant of revision_a.
1483
This is a helper function for update_revisions.
1485
:raises: DivergedBranches if revision_b has diverged from revision_a.
1486
:returns: True if revision_b is a descendant of revision_a.
1488
relation = self._revision_relations(revision_a, revision_b, graph)
1489
if relation == 'b_descends_from_a':
1491
elif relation == 'diverged':
1492
raise errors.DivergedBranches(self, other_branch)
1493
elif relation == 'a_descends_from_b':
1496
raise AssertionError("invalid relation: %r" % (relation,))
1498
def _revision_relations(self, revision_a, revision_b, graph):
1499
"""Determine the relationship between two revisions.
1501
:returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
1503
heads = graph.heads([revision_a, revision_b])
1504
if heads == set([revision_b]):
1505
return 'b_descends_from_a'
1506
elif heads == set([revision_a, revision_b]):
1507
# These branches have diverged
1509
elif heads == set([revision_a]):
1510
return 'a_descends_from_b'
1512
raise AssertionError("invalid heads: %r" % (heads,))
1515
826
class BranchFormat(object):
1516
827
"""An encapsulation of the initialization and open routines for a format.
1644
912
control_files = lockable_files.LockableFiles(branch_transport,
1645
913
lock_name, lock_class)
1646
914
control_files.create_lock()
1648
control_files.lock_write()
1649
except errors.LockContention:
1650
if lock_type != 'branch4':
915
control_files.lock_write()
1656
utf8_files += [('format', self.get_format_string())]
917
control_files.put_utf8('format', self.get_format_string())
1658
for (filename, content) in utf8_files:
1659
branch_transport.put_bytes(
1661
mode=a_bzrdir._get_file_mode())
919
for file, content in utf8_files:
920
control_files.put_utf8(file, content)
1664
control_files.unlock()
1665
branch = self.open(a_bzrdir, name, _found=True)
1666
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
922
control_files.unlock()
923
return self.open(a_bzrdir, _found=True)
1669
def initialize(self, a_bzrdir, name=None):
1670
"""Create a branch of this format in a_bzrdir.
1672
:param name: Name of the colocated branch to create.
925
def initialize(self, a_bzrdir):
926
"""Create a branch of this format in a_bzrdir."""
1674
927
raise NotImplementedError(self.initialize)
1676
929
def is_supported(self):
1677
930
"""Is this format supported?
1679
932
Supported formats can be initialized and opened.
1680
Unsupported formats may not support initialization or committing or
933
Unsupported formats may not support initialization or committing or
1681
934
some other features depending on the reason for not being supported.
1685
def make_tags(self, branch):
1686
"""Create a tags object for branch.
1688
This method is on BranchFormat, because BranchFormats are reflected
1689
over the wire via network_name(), whereas full Branch instances require
1690
multiple VFS method calls to operate at all.
1692
The default implementation returns a disabled-tags instance.
1694
Note that it is normal for branch to be a RemoteBranch when using tags
1697
return DisabledTags(branch)
1699
def network_name(self):
1700
"""A simple byte string uniquely identifying this format for RPC calls.
1702
MetaDir branch formats use their disk format string to identify the
1703
repository over the wire. All in one formats such as bzr < 0.8, and
1704
foreign formats like svn/git and hg should use some marker which is
1705
unique and immutable.
1707
raise NotImplementedError(self.network_name)
1709
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
938
def open(self, a_bzrdir, _found=False):
1710
939
"""Return the branch object for a_bzrdir
1712
:param a_bzrdir: A BzrDir that contains a branch.
1713
:param name: Name of colocated branch to open
1714
:param _found: a private parameter, do not use it. It is used to
1715
indicate if format probing has already be done.
1716
:param ignore_fallbacks: when set, no fallback branches will be opened
1717
(if there are any). Default is to open fallbacks.
941
_found is a private parameter, do not use it. It is used to indicate
942
if format probing has already be done.
1719
944
raise NotImplementedError(self.open)
1722
947
def register_format(klass, format):
1723
"""Register a metadir format.
1725
See MetaDirBranchFormatFactory for the ability to register a format
1726
without loading the code the format needs until it is actually used.
1728
948
klass._formats[format.get_format_string()] = format
1729
# Metadir formats have a network name of their format string, and get
1730
# registered as factories.
1731
if isinstance(format, MetaDirBranchFormatFactory):
1732
network_format_registry.register(format.get_format_string(), format)
1734
network_format_registry.register(format.get_format_string(),
1738
951
def set_default_format(klass, format):
1739
952
klass._default_format = format
1741
def supports_set_append_revisions_only(self):
1742
"""True if this format supports set_append_revisions_only."""
1745
def supports_stacking(self):
1746
"""True if this format records a stacked-on branch."""
1750
955
def unregister_format(klass, format):
956
assert klass._formats[format.get_format_string()] is format
1751
957
del klass._formats[format.get_format_string()]
1753
959
def __str__(self):
1754
return self.get_format_description().rstrip()
960
return self.get_format_string().rstrip()
1756
962
def supports_tags(self):
1757
963
"""True if this format supports tags stored in the branch"""
1758
964
return False # by default
1761
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1762
"""A factory for a BranchFormat object, permitting simple lazy registration.
1764
While none of the built in BranchFormats are lazy registered yet,
1765
bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
1766
use it, and the bzr-loom plugin uses it as well (see
1767
bzrlib.plugins.loom.formats).
1770
def __init__(self, format_string, module_name, member_name):
1771
"""Create a MetaDirBranchFormatFactory.
1773
:param format_string: The format string the format has.
1774
:param module_name: Module to load the format class from.
1775
:param member_name: Attribute name within the module for the format class.
1777
registry._LazyObjectGetter.__init__(self, module_name, member_name)
1778
self._format_string = format_string
1780
def get_format_string(self):
1781
"""See BranchFormat.get_format_string."""
1782
return self._format_string
1785
"""Used for network_format_registry support."""
1786
return self.get_obj()()
966
# XXX: Probably doesn't really belong here -- mbp 20070212
967
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
969
branch_transport = a_bzrdir.get_branch_transport(self)
970
control_files = lockable_files.LockableFiles(branch_transport,
971
lock_filename, lock_class)
972
control_files.create_lock()
973
control_files.lock_write()
975
for filename, content in utf8_files:
976
control_files.put_utf8(filename, content)
978
control_files.unlock()
1789
981
class BranchHooks(Hooks):
1790
982
"""A dictionary mapping hook name to a list of callables for branch hooks.
1792
984
e.g. ['set_rh'] Is the list of items to be called when the
1793
985
set_revision_history function is invoked.
1802
994
Hooks.__init__(self)
1803
self.create_hook(HookPoint('set_rh',
1804
"Invoked whenever the revision history has been set via "
1805
"set_revision_history. The api signature is (branch, "
1806
"revision_history), and the branch will be write-locked. "
1807
"The set_rh hook can be expensive for bzr to trigger, a better "
1808
"hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1809
self.create_hook(HookPoint('open',
1810
"Called with the Branch object that has been opened after a "
1811
"branch is opened.", (1, 8), None))
1812
self.create_hook(HookPoint('post_push',
1813
"Called after a push operation completes. post_push is called "
1814
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1815
"bzr client.", (0, 15), None))
1816
self.create_hook(HookPoint('post_pull',
1817
"Called after a pull operation completes. post_pull is called "
1818
"with a bzrlib.branch.PullResult object and only runs in the "
1819
"bzr client.", (0, 15), None))
1820
self.create_hook(HookPoint('pre_commit',
1821
"Called after a commit is calculated but before it is is "
1822
"completed. pre_commit is called with (local, master, old_revno, "
1823
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1824
"). old_revid is NULL_REVISION for the first commit to a branch, "
1825
"tree_delta is a TreeDelta object describing changes from the "
1826
"basis revision. hooks MUST NOT modify this delta. "
1827
" future_tree is an in-memory tree obtained from "
1828
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1829
"tree.", (0,91), None))
1830
self.create_hook(HookPoint('post_commit',
1831
"Called in the bzr client after a commit has completed. "
1832
"post_commit is called with (local, master, old_revno, old_revid, "
1833
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1834
"commit to a branch.", (0, 15), None))
1835
self.create_hook(HookPoint('post_uncommit',
1836
"Called in the bzr client after an uncommit completes. "
1837
"post_uncommit is called with (local, master, old_revno, "
1838
"old_revid, new_revno, new_revid) where local is the local branch "
1839
"or None, master is the target branch, and an empty branch "
1840
"receives new_revno of 0, new_revid of None.", (0, 15), None))
1841
self.create_hook(HookPoint('pre_change_branch_tip',
1842
"Called in bzr client and server before a change to the tip of a "
1843
"branch is made. pre_change_branch_tip is called with a "
1844
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1845
"commit, uncommit will all trigger this hook.", (1, 6), None))
1846
self.create_hook(HookPoint('post_change_branch_tip',
1847
"Called in bzr client and server after a change to the tip of a "
1848
"branch is made. post_change_branch_tip is called with a "
1849
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1850
"commit, uncommit will all trigger this hook.", (1, 4), None))
1851
self.create_hook(HookPoint('transform_fallback_location',
1852
"Called when a stacked branch is activating its fallback "
1853
"locations. transform_fallback_location is called with (branch, "
1854
"url), and should return a new url. Returning the same url "
1855
"allows it to be used as-is, returning a different one can be "
1856
"used to cause the branch to stack on a closer copy of that "
1857
"fallback_location. Note that the branch cannot have history "
1858
"accessing methods called on it during this hook because the "
1859
"fallback locations have not been activated. When there are "
1860
"multiple hooks installed for transform_fallback_location, "
1861
"all are called with the url returned from the previous hook."
1862
"The order is however undefined.", (1, 9), None))
1863
self.create_hook(HookPoint('automatic_tag_name',
1864
"Called to determine an automatic tag name for a revision. "
1865
"automatic_tag_name is called with (branch, revision_id) and "
1866
"should return a tag name or None if no tag name could be "
1867
"determined. The first non-None tag name returned will be used.",
1869
self.create_hook(HookPoint('post_branch_init',
1870
"Called after new branch initialization completes. "
1871
"post_branch_init is called with a "
1872
"bzrlib.branch.BranchInitHookParams. "
1873
"Note that init, branch and checkout (both heavyweight and "
1874
"lightweight) will all trigger this hook.", (2, 2), None))
1875
self.create_hook(HookPoint('post_switch',
1876
"Called after a checkout switches branch. "
1877
"post_switch is called with a "
1878
"bzrlib.branch.SwitchHookParams.", (2, 2), None))
995
# Introduced in 0.15:
996
# invoked whenever the revision history has been set
997
# with set_revision_history. The api signature is
998
# (branch, revision_history), and the branch will
1001
# invoked after a push operation completes.
1002
# the api signature is
1004
# containing the members
1005
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1006
# where local is the local target branch or None, master is the target
1007
# master branch, and the rest should be self explanatory. The source
1008
# is read locked and the target branches write locked. Source will
1009
# be the local low-latency branch.
1010
self['post_push'] = []
1011
# invoked after a pull operation completes.
1012
# the api signature is
1014
# containing the members
1015
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1016
# where local is the local branch or None, master is the target
1017
# master branch, and the rest should be self explanatory. The source
1018
# is read locked and the target branches write locked. The local
1019
# branch is the low-latency branch.
1020
self['post_pull'] = []
1021
# invoked after a commit operation completes.
1022
# the api signature is
1023
# (local, master, old_revno, old_revid, new_revno, new_revid)
1024
# old_revid is NULL_REVISION for the first commit to a branch.
1025
self['post_commit'] = []
1026
# invoked after a uncommit operation completes.
1027
# the api signature is
1028
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1029
# local is the local branch or None, master is the target branch,
1030
# and an empty branch recieves new_revno of 0, new_revid of None.
1031
self['post_uncommit'] = []
1882
1034
# install the default hooks into the Branch class.
1883
1035
Branch.hooks = BranchHooks()
1886
class ChangeBranchTipParams(object):
1887
"""Object holding parameters passed to *_change_branch_tip hooks.
1889
There are 5 fields that hooks may wish to access:
1891
:ivar branch: the branch being changed
1892
:ivar old_revno: revision number before the change
1893
:ivar new_revno: revision number after the change
1894
:ivar old_revid: revision id before the change
1895
:ivar new_revid: revision id after the change
1897
The revid fields are strings. The revno fields are integers.
1900
def __init__(self, branch, old_revno, new_revno, old_revid, new_revid):
1901
"""Create a group of ChangeBranchTip parameters.
1903
:param branch: The branch being changed.
1904
:param old_revno: Revision number before the change.
1905
:param new_revno: Revision number after the change.
1906
:param old_revid: Tip revision id before the change.
1907
:param new_revid: Tip revision id after the change.
1909
self.branch = branch
1910
self.old_revno = old_revno
1911
self.new_revno = new_revno
1912
self.old_revid = old_revid
1913
self.new_revid = new_revid
1915
def __eq__(self, other):
1916
return self.__dict__ == other.__dict__
1919
return "<%s of %s from (%s, %s) to (%s, %s)>" % (
1920
self.__class__.__name__, self.branch,
1921
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1924
class BranchInitHookParams(object):
1925
"""Object holding parameters passed to *_branch_init hooks.
1927
There are 4 fields that hooks may wish to access:
1929
:ivar format: the branch format
1930
:ivar bzrdir: the BzrDir where the branch will be/has been initialized
1931
:ivar name: name of colocated branch, if any (or None)
1932
:ivar branch: the branch created
1934
Note that for lightweight checkouts, the bzrdir and format fields refer to
1935
the checkout, hence they are different from the corresponding fields in
1936
branch, which refer to the original branch.
1939
def __init__(self, format, a_bzrdir, name, branch):
1940
"""Create a group of BranchInitHook parameters.
1942
:param format: the branch format
1943
:param a_bzrdir: the BzrDir where the branch will be/has been
1945
:param name: name of colocated branch, if any (or None)
1946
:param branch: the branch created
1948
Note that for lightweight checkouts, the bzrdir and format fields refer
1949
to the checkout, hence they are different from the corresponding fields
1950
in branch, which refer to the original branch.
1952
self.format = format
1953
self.bzrdir = a_bzrdir
1955
self.branch = branch
1957
def __eq__(self, other):
1958
return self.__dict__ == other.__dict__
1961
return "<%s of %s>" % (self.__class__.__name__, self.branch)
1964
class SwitchHookParams(object):
1965
"""Object holding parameters passed to *_switch hooks.
1967
There are 4 fields that hooks may wish to access:
1969
:ivar control_dir: BzrDir of the checkout to change
1970
:ivar to_branch: branch that the checkout is to reference
1971
:ivar force: skip the check for local commits in a heavy checkout
1972
:ivar revision_id: revision ID to switch to (or None)
1975
def __init__(self, control_dir, to_branch, force, revision_id):
1976
"""Create a group of SwitchHook parameters.
1978
:param control_dir: BzrDir of the checkout to change
1979
:param to_branch: branch that the checkout is to reference
1980
:param force: skip the check for local commits in a heavy checkout
1981
:param revision_id: revision ID to switch to (or None)
1983
self.control_dir = control_dir
1984
self.to_branch = to_branch
1986
self.revision_id = revision_id
1988
def __eq__(self, other):
1989
return self.__dict__ == other.__dict__
1992
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1993
self.control_dir, self.to_branch,
1997
1038
class BzrBranchFormat4(BranchFormat):
1998
1039
"""Bzr branch format 4.
2006
1047
"""See BranchFormat.get_format_description()."""
2007
1048
return "Branch format 4"
2009
def initialize(self, a_bzrdir, name=None):
1050
def initialize(self, a_bzrdir):
2010
1051
"""Create a branch of this format in a_bzrdir."""
2011
1052
utf8_files = [('revision-history', ''),
2012
1053
('branch-name', ''),
2014
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1055
return self._initialize_helper(a_bzrdir, utf8_files,
2015
1056
lock_type='branch4', set_format=False)
2017
1058
def __init__(self):
2018
1059
super(BzrBranchFormat4, self).__init__()
2019
1060
self._matchingbzrdir = bzrdir.BzrDirFormat6()
2021
def network_name(self):
2022
"""The network name for this format is the control dirs disk label."""
2023
return self._matchingbzrdir.get_format_string()
1062
def open(self, a_bzrdir, _found=False):
1063
"""Return the branch object for a_bzrdir
2025
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2026
"""See BranchFormat.open()."""
1065
_found is a private parameter, do not use it. It is used to indicate
1066
if format probing has already be done.
2028
1069
# we are being called directly and must probe.
2029
1070
raise NotImplementedError
2030
1071
return BzrBranch(_format=self,
2031
1072
_control_files=a_bzrdir._control_files,
2032
1073
a_bzrdir=a_bzrdir,
2034
1074
_repository=a_bzrdir.open_repository())
2036
1076
def __str__(self):
2037
1077
return "Bazaar-NG branch format 4"
2040
class BranchFormatMetadir(BranchFormat):
2041
"""Common logic for meta-dir based branch formats."""
2043
def _branch_class(self):
2044
"""What class to instantiate on open calls."""
2045
raise NotImplementedError(self._branch_class)
2047
def network_name(self):
2048
"""A simple byte string uniquely identifying this format for RPC calls.
2050
Metadir branch formats use their format string.
1080
class BzrBranchFormat5(BranchFormat):
1081
"""Bzr branch format 5.
1084
- a revision-history file.
1086
- a lock dir guarding the branch itself
1087
- all of this stored in a branch/ subdirectory
1088
- works with shared repositories.
1090
This format is new in bzr 0.8.
1093
def get_format_string(self):
1094
"""See BranchFormat.get_format_string()."""
1095
return "Bazaar-NG branch format 5\n"
1097
def get_format_description(self):
1098
"""See BranchFormat.get_format_description()."""
1099
return "Branch format 5"
1101
def initialize(self, a_bzrdir):
1102
"""Create a branch of this format in a_bzrdir."""
1103
utf8_files = [('revision-history', ''),
1104
('branch-name', ''),
1106
return self._initialize_helper(a_bzrdir, utf8_files)
1109
super(BzrBranchFormat5, self).__init__()
1110
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1112
def open(self, a_bzrdir, _found=False):
1113
"""Return the branch object for a_bzrdir
1115
_found is a private parameter, do not use it. It is used to indicate
1116
if format probing has already be done.
2052
return self.get_format_string()
2054
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2055
"""See BranchFormat.open()."""
2057
format = BranchFormat.find_format(a_bzrdir, name=name)
2058
if format.__class__ != self.__class__:
2059
raise AssertionError("wrong format %r found for %r" %
2061
transport = a_bzrdir.get_branch_transport(None, name=name)
1119
format = BranchFormat.find_format(a_bzrdir)
1120
assert format.__class__ == self.__class__
1122
transport = a_bzrdir.get_branch_transport(None)
2063
1123
control_files = lockable_files.LockableFiles(transport, 'lock',
2064
1124
lockdir.LockDir)
2065
return self._branch_class()(_format=self,
1125
return BzrBranch5(_format=self,
2066
1126
_control_files=control_files,
2068
1127
a_bzrdir=a_bzrdir,
2069
_repository=a_bzrdir.find_repository(),
2070
ignore_fallbacks=ignore_fallbacks)
2071
except errors.NoSuchFile:
2072
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2075
super(BranchFormatMetadir, self).__init__()
2076
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2077
self._matchingbzrdir.set_branch_format(self)
2079
def supports_tags(self):
2083
class BzrBranchFormat5(BranchFormatMetadir):
2084
"""Bzr branch format 5.
2087
- a revision-history file.
2089
- a lock dir guarding the branch itself
2090
- all of this stored in a branch/ subdirectory
2091
- works with shared repositories.
2093
This format is new in bzr 0.8.
2096
def _branch_class(self):
2099
def get_format_string(self):
2100
"""See BranchFormat.get_format_string()."""
2101
return "Bazaar-NG branch format 5\n"
2103
def get_format_description(self):
2104
"""See BranchFormat.get_format_description()."""
2105
return "Branch format 5"
2107
def initialize(self, a_bzrdir, name=None):
2108
"""Create a branch of this format in a_bzrdir."""
2109
utf8_files = [('revision-history', ''),
2110
('branch-name', ''),
2112
return self._initialize_helper(a_bzrdir, utf8_files, name)
2114
def supports_tags(self):
2118
class BzrBranchFormat6(BranchFormatMetadir):
2119
"""Branch format with last-revision and tags.
1128
_repository=a_bzrdir.find_repository())
1130
raise NotBranchError(path=transport.base)
1133
class BzrBranchFormat6(BzrBranchFormat5):
1134
"""Branch format with last-revision
2121
1136
Unlike previous formats, this has no explicit revision history. Instead,
2122
1137
this just stores the last-revision, and the left-hand history leading
2123
1138
up to there is the history.
2125
1140
This format was introduced in bzr 0.15
2126
and became the default in 0.91.
2129
def _branch_class(self):
2132
1143
def get_format_string(self):
2133
1144
"""See BranchFormat.get_format_string()."""
2134
1145
return "Bazaar Branch Format 6 (bzr 0.15)\n"
2137
1148
"""See BranchFormat.get_format_description()."""
2138
1149
return "Branch format 6"
2140
def initialize(self, a_bzrdir, name=None):
2141
"""Create a branch of this format in a_bzrdir."""
2142
utf8_files = [('last-revision', '0 null:\n'),
2143
('branch.conf', ''),
2146
return self._initialize_helper(a_bzrdir, utf8_files, name)
2148
def make_tags(self, branch):
2149
"""See bzrlib.branch.BranchFormat.make_tags()."""
2150
return BasicTags(branch)
2152
def supports_set_append_revisions_only(self):
2156
class BzrBranchFormat8(BranchFormatMetadir):
2157
"""Metadir format supporting storing locations of subtree branches."""
2159
def _branch_class(self):
2162
def get_format_string(self):
2163
"""See BranchFormat.get_format_string()."""
2164
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2166
def get_format_description(self):
2167
"""See BranchFormat.get_format_description()."""
2168
return "Branch format 8"
2170
def initialize(self, a_bzrdir, name=None):
2171
"""Create a branch of this format in a_bzrdir."""
2172
utf8_files = [('last-revision', '0 null:\n'),
2173
('branch.conf', ''),
2177
return self._initialize_helper(a_bzrdir, utf8_files, name)
2180
super(BzrBranchFormat8, self).__init__()
2181
self._matchingbzrdir.repository_format = \
2182
RepositoryFormatKnitPack5RichRoot()
2184
def make_tags(self, branch):
2185
"""See bzrlib.branch.BranchFormat.make_tags()."""
2186
return BasicTags(branch)
2188
def supports_set_append_revisions_only(self):
2191
def supports_stacking(self):
2194
supports_reference_locations = True
2197
class BzrBranchFormat7(BzrBranchFormat8):
2198
"""Branch format with last-revision, tags, and a stacked location pointer.
2200
The stacked location pointer is passed down to the repository and requires
2201
a repository format with supports_external_lookups = True.
2203
This format was introduced in bzr 1.6.
2206
def initialize(self, a_bzrdir, name=None):
2207
"""Create a branch of this format in a_bzrdir."""
2208
utf8_files = [('last-revision', '0 null:\n'),
2209
('branch.conf', ''),
2212
return self._initialize_helper(a_bzrdir, utf8_files, name)
2214
def _branch_class(self):
2217
def get_format_string(self):
2218
"""See BranchFormat.get_format_string()."""
2219
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2221
def get_format_description(self):
2222
"""See BranchFormat.get_format_description()."""
2223
return "Branch format 7"
2225
def supports_set_append_revisions_only(self):
2228
supports_reference_locations = False
1151
def initialize(self, a_bzrdir):
1152
"""Create a branch of this format in a_bzrdir."""
1153
utf8_files = [('last-revision', '0 null:\n'),
1154
('branch-name', ''),
1155
('branch.conf', ''),
1158
return self._initialize_helper(a_bzrdir, utf8_files)
1160
def open(self, a_bzrdir, _found=False):
1161
"""Return the branch object for a_bzrdir
1163
_found is a private parameter, do not use it. It is used to indicate
1164
if format probing has already be done.
1167
format = BranchFormat.find_format(a_bzrdir)
1168
assert format.__class__ == self.__class__
1169
transport = a_bzrdir.get_branch_transport(None)
1170
control_files = lockable_files.LockableFiles(transport, 'lock',
1172
return BzrBranch6(_format=self,
1173
_control_files=control_files,
1175
_repository=a_bzrdir.find_repository())
1177
def supports_tags(self):
2231
1181
class BranchReferenceFormat(BranchFormat):
2246
1196
def get_format_description(self):
2247
1197
"""See BranchFormat.get_format_description()."""
2248
1198
return "Checkout reference format 1"
2250
def get_reference(self, a_bzrdir, name=None):
1200
def get_reference(self, a_bzrdir):
2251
1201
"""See BranchFormat.get_reference()."""
2252
transport = a_bzrdir.get_branch_transport(None, name=name)
2253
return transport.get_bytes('location')
2255
def set_reference(self, a_bzrdir, name, to_branch):
2256
"""See BranchFormat.set_reference()."""
2257
transport = a_bzrdir.get_branch_transport(None, name=name)
2258
location = transport.put_bytes('location', to_branch.base)
2260
def initialize(self, a_bzrdir, name=None, target_branch=None):
1202
transport = a_bzrdir.get_branch_transport(None)
1203
return transport.get('location').read()
1205
def initialize(self, a_bzrdir, target_branch=None):
2261
1206
"""Create a branch of this format in a_bzrdir."""
2262
1207
if target_branch is None:
2263
1208
# this format does not implement branch itself, thus the implicit
2264
1209
# creation contract must see it as uninitializable
2265
1210
raise errors.UninitializableFormat(self)
2266
mutter('creating branch reference in %s', a_bzrdir.user_url)
2267
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1211
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1212
branch_transport = a_bzrdir.get_branch_transport(self)
2268
1213
branch_transport.put_bytes('location',
2269
target_branch.bzrdir.user_url)
1214
target_branch.bzrdir.root_transport.base)
2270
1215
branch_transport.put_bytes('format', self.get_format_string())
2272
a_bzrdir, name, _found=True,
2273
possible_transports=[target_branch.bzrdir.root_transport])
2274
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1216
return self.open(a_bzrdir, _found=True)
2277
1218
def __init__(self):
2278
1219
super(BranchReferenceFormat, self).__init__()
2279
1220
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2280
self._matchingbzrdir.set_branch_format(self)
2282
1222
def _make_reference_clone_function(format, a_branch):
2283
1223
"""Create a clone() routine for a branch dynamically."""
2284
def clone(to_bzrdir, revision_id=None,
2285
repository_policy=None):
1224
def clone(to_bzrdir, revision_id=None):
2286
1225
"""See Branch.clone()."""
2287
return format.initialize(to_bzrdir, target_branch=a_branch)
1226
return format.initialize(to_bzrdir, a_branch)
2288
1227
# cannot obey revision_id limits when cloning a reference ...
2289
1228
# FIXME RBC 20060210 either nuke revision_id for clone, or
2290
1229
# emit some sort of warning/error to the caller ?!
2293
def open(self, a_bzrdir, name=None, _found=False, location=None,
2294
possible_transports=None, ignore_fallbacks=False):
1232
def open(self, a_bzrdir, _found=False, location=None):
2295
1233
"""Return the branch that the branch reference in a_bzrdir points at.
2297
:param a_bzrdir: A BzrDir that contains a branch.
2298
:param name: Name of colocated branch to open, if any
2299
:param _found: a private parameter, do not use it. It is used to
2300
indicate if format probing has already be done.
2301
:param ignore_fallbacks: when set, no fallback branches will be opened
2302
(if there are any). Default is to open fallbacks.
2303
:param location: The location of the referenced branch. If
2304
unspecified, this will be determined from the branch reference in
2306
:param possible_transports: An optional reusable transports list.
1235
_found is a private parameter, do not use it. It is used to indicate
1236
if format probing has already be done.
2309
format = BranchFormat.find_format(a_bzrdir, name=name)
2310
if format.__class__ != self.__class__:
2311
raise AssertionError("wrong format %r found for %r" %
1239
format = BranchFormat.find_format(a_bzrdir)
1240
assert format.__class__ == self.__class__
2313
1241
if location is None:
2314
location = self.get_reference(a_bzrdir, name)
2315
real_bzrdir = bzrdir.BzrDir.open(
2316
location, possible_transports=possible_transports)
2317
result = real_bzrdir.open_branch(name=name,
2318
ignore_fallbacks=ignore_fallbacks)
1242
location = self.get_reference(a_bzrdir)
1243
real_bzrdir = bzrdir.BzrDir.open(location)
1244
result = real_bzrdir.open_branch()
2319
1245
# this changes the behaviour of result.clone to create a new reference
2320
1246
# rather than a copy of the content of the branch.
2321
1247
# I did not use a proxy object because that needs much more extensive
2331
network_format_registry = registry.FormatRegistry()
2332
"""Registry of formats indexed by their network name.
2334
The network name for a branch format is an identifier that can be used when
2335
referring to formats with smart server operations. See
2336
BranchFormat.network_name() for more detail.
2340
1257
# formats which have no format string are not discoverable
2341
1258
# and not independently creatable, so are not registered.
2342
__format5 = BzrBranchFormat5()
2343
__format6 = BzrBranchFormat6()
2344
__format7 = BzrBranchFormat7()
2345
__format8 = BzrBranchFormat8()
2346
BranchFormat.register_format(__format5)
1259
__default_format = BzrBranchFormat5()
1260
BranchFormat.register_format(__default_format)
2347
1261
BranchFormat.register_format(BranchReferenceFormat())
2348
BranchFormat.register_format(__format6)
2349
BranchFormat.register_format(__format7)
2350
BranchFormat.register_format(__format8)
2351
BranchFormat.set_default_format(__format7)
1262
BranchFormat.register_format(BzrBranchFormat6())
1263
BranchFormat.set_default_format(__default_format)
2352
1264
_legacy_formats = [BzrBranchFormat4(),
2354
network_format_registry.register(
2355
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2358
class BranchWriteLockResult(LogicalLockResult):
2359
"""The result of write locking a branch.
2361
:ivar branch_token: The token obtained from the underlying branch lock, or
2363
:ivar unlock: A callable which will unlock the lock.
2366
def __init__(self, unlock, branch_token):
2367
LogicalLockResult.__init__(self, unlock)
2368
self.branch_token = branch_token
2371
return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2375
class BzrBranch(Branch, _RelockDebugMixin):
1267
class BzrBranch(Branch):
2376
1268
"""A branch stored in the actual filesystem.
2378
1270
Note that it's "local" in the context of the filesystem; it doesn't
2379
1271
really matter if it's on an nfs/smb/afs/coda/... share, as long as
2380
1272
it's writable, and can be accessed via the normal filesystem API.
2382
:ivar _transport: Transport for file operations on this branch's
2383
control files, typically pointing to the .bzr/branch directory.
2384
:ivar repository: Repository for this branch.
2385
:ivar base: The url of the base directory for this branch; the one
2386
containing the .bzr directory.
2387
:ivar name: Optional colocated branch name as it exists in the control
2391
1275
def __init__(self, _format=None,
2392
_control_files=None, a_bzrdir=None, name=None,
2393
_repository=None, ignore_fallbacks=False):
1276
_control_files=None, a_bzrdir=None, _repository=None):
2394
1277
"""Create new branch object at a particular location."""
1278
Branch.__init__(self)
2395
1279
if a_bzrdir is None:
2396
1280
raise ValueError('a_bzrdir must be supplied')
2398
1282
self.bzrdir = a_bzrdir
1283
# self._transport used to point to the directory containing the
1284
# control directory, but was not used - now it's just the transport
1285
# for the branch control files. mbp 20070212
2399
1286
self._base = self.bzrdir.transport.clone('..').base
2401
# XXX: We should be able to just do
2402
# self.base = self.bzrdir.root_transport.base
2403
# but this does not quite work yet -- mbp 20080522
2404
1287
self._format = _format
2405
1288
if _control_files is None:
2406
1289
raise ValueError('BzrBranch _control_files is None')
2407
1290
self.control_files = _control_files
2408
1291
self._transport = _control_files._transport
2409
1292
self.repository = _repository
2410
Branch.__init__(self)
2412
1294
def __str__(self):
2413
if self.name is None:
2414
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2416
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
1295
return '%s(%r)' % (self.__class__.__name__, self.base)
2419
1297
__repr__ = __str__
2425
1303
base = property(_get_base, doc="The URL for the root of this branch.")
2427
def _get_config(self):
2428
return TransportConfig(self._transport, 'branch.conf')
1305
def abspath(self, name):
1306
"""See Branch.abspath."""
1307
return self.control_files._transport.abspath(name)
1310
@deprecated_method(zero_sixteen)
1312
def get_root_id(self):
1313
"""See Branch.get_root_id."""
1314
tree = self.repository.revision_tree(self.last_revision())
1315
return tree.inventory.root.file_id
2430
1317
def is_locked(self):
2431
1318
return self.control_files.is_locked()
2433
1320
def lock_write(self, token=None):
2434
"""Lock the branch for write operations.
2436
:param token: A token to permit reacquiring a previously held and
2438
:return: A BranchWriteLockResult.
2440
if not self.is_locked():
2441
self._note_lock('w')
2442
# All-in-one needs to always unlock/lock.
2443
repo_control = getattr(self.repository, 'control_files', None)
2444
if self.control_files == repo_control or not self.is_locked():
2445
self.repository._warn_if_deprecated(self)
2446
self.repository.lock_write()
1321
repo_token = self.repository.lock_write()
2451
return BranchWriteLockResult(self.unlock,
2452
self.control_files.lock_write(token=token))
1323
token = self.control_files.lock_write(token=token)
2455
self.repository.unlock()
1325
self.repository.unlock()
2458
1329
def lock_read(self):
2459
"""Lock the branch for read operations.
2461
:return: A bzrlib.lock.LogicalLockResult.
2463
if not self.is_locked():
2464
self._note_lock('r')
2465
# All-in-one needs to always unlock/lock.
2466
repo_control = getattr(self.repository, 'control_files', None)
2467
if self.control_files == repo_control or not self.is_locked():
2468
self.repository._warn_if_deprecated(self)
2469
self.repository.lock_read()
1330
self.repository.lock_read()
2474
1332
self.control_files.lock_read()
2475
return LogicalLockResult(self.unlock)
2478
self.repository.unlock()
1334
self.repository.unlock()
2481
@only_raises(errors.LockNotHeld, errors.LockBroken)
2482
1337
def unlock(self):
1338
# TODO: test for failed two phase locks. This is known broken.
2484
1340
self.control_files.unlock()
2486
# All-in-one needs to always unlock/lock.
2487
repo_control = getattr(self.repository, 'control_files', None)
2488
if (self.control_files == repo_control or
2489
not self.control_files.is_locked()):
2490
self.repository.unlock()
2491
if not self.control_files.is_locked():
2492
# we just released the lock
2493
self._clear_cached_state()
1342
self.repository.unlock()
1343
if not self.control_files.is_locked():
1344
# we just released the lock
1345
self._clear_cached_state()
2495
1347
def peek_lock_mode(self):
2496
1348
if self.control_files._lock_count == 0:
2506
1358
"""See Branch.print_file."""
2507
1359
return self.repository.print_file(file, revision_id)
1362
def append_revision(self, *revision_ids):
1363
"""See Branch.append_revision."""
1364
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1365
for revision_id in revision_ids:
1366
_mod_revision.check_not_reserved_id(revision_id)
1367
mutter("add {%s} to revision-history" % revision_id)
1368
rev_history = self.revision_history()
1369
rev_history.extend(revision_ids)
1370
self.set_revision_history(rev_history)
2509
1372
def _write_revision_history(self, history):
2510
1373
"""Factored out of set_revision_history.
2512
1375
This performs the actual writing to disk.
2513
1376
It is intended to be called by BzrBranch5.set_revision_history."""
2514
self._transport.put_bytes(
2515
'revision-history', '\n'.join(history),
2516
mode=self.bzrdir._get_file_mode())
1377
self.control_files.put_bytes(
1378
'revision-history', '\n'.join(history))
2518
1380
@needs_write_lock
2519
1381
def set_revision_history(self, rev_history):
2520
1382
"""See Branch.set_revision_history."""
2521
if 'evil' in debug.debug_flags:
2522
mutter_callsite(3, "set_revision_history scales with history.")
2523
check_not_reserved_id = _mod_revision.check_not_reserved_id
2524
for rev_id in rev_history:
2525
check_not_reserved_id(rev_id)
2526
if Branch.hooks['post_change_branch_tip']:
2527
# Don't calculate the last_revision_info() if there are no hooks
2529
old_revno, old_revid = self.last_revision_info()
2530
if len(rev_history) == 0:
2531
revid = _mod_revision.NULL_REVISION
2533
revid = rev_history[-1]
2534
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
1383
rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1384
self._clear_cached_state()
2535
1385
self._write_revision_history(rev_history)
2536
self._clear_cached_state()
2537
1386
self._cache_revision_history(rev_history)
2538
1387
for hook in Branch.hooks['set_rh']:
2539
1388
hook(self, rev_history)
2540
if Branch.hooks['post_change_branch_tip']:
2541
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2543
def _synchronize_history(self, destination, revision_id):
2544
"""Synchronize last revision and revision history between branches.
2546
This version is most efficient when the destination is also a
2547
BzrBranch5, but works for BzrBranch6 as long as the revision
2548
history is the true lefthand parent history, and all of the revisions
2549
are in the destination's repository. If not, set_revision_history
2552
:param destination: The branch to copy the history into
2553
:param revision_id: The revision-id to truncate history at. May
2554
be None to copy complete history.
2556
if not isinstance(destination._format, BzrBranchFormat5):
2557
super(BzrBranch, self)._synchronize_history(
2558
destination, revision_id)
2560
if revision_id == _mod_revision.NULL_REVISION:
2563
new_history = self.revision_history()
2564
if revision_id is not None and new_history != []:
2566
new_history = new_history[:new_history.index(revision_id) + 1]
2568
rev = self.repository.get_revision(revision_id)
2569
new_history = rev.get_history(self.repository)[1:]
2570
destination.set_revision_history(new_history)
2572
1390
@needs_write_lock
2573
1391
def set_last_revision_info(self, revno, revision_id):
2574
"""Set the last revision of this branch.
2576
The caller is responsible for checking that the revno is correct
2577
for this revision id.
2579
It may be possible to set the branch last revision to an id not
2580
present in the repository. However, branches can also be
2581
configured to check constraints on history, in which case this may not
2584
revision_id = _mod_revision.ensure_null(revision_id)
2585
# this old format stores the full history, but this api doesn't
2586
# provide it, so we must generate, and might as well check it's
1392
revision_id = osutils.safe_revision_id(revision_id)
2588
1393
history = self._lefthand_history(revision_id)
2589
if len(history) != revno:
2590
raise AssertionError('%d != %d' % (len(history), revno))
1394
assert len(history) == revno, '%d != %d' % (len(history), revno)
2591
1395
self.set_revision_history(history)
2593
1397
def _gen_revision_history(self):
2594
history = self._transport.get_bytes('revision-history').split('\n')
1398
history = self.control_files.get('revision-history').read().split('\n')
2595
1399
if history[-1:] == ['']:
2596
1400
# There shouldn't be a trailing newline, but just in case.
1404
def _lefthand_history(self, revision_id, last_rev=None,
1406
# stop_revision must be a descendant of last_revision
1407
stop_graph = self.repository.get_revision_graph(revision_id)
1408
if last_rev is not None and last_rev not in stop_graph:
1409
# our previous tip is not merged into stop_revision
1410
raise errors.DivergedBranches(self, other_branch)
1411
# make a new revision history from the graph
1412
current_rev_id = revision_id
1414
while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1415
new_history.append(current_rev_id)
1416
current_rev_id_parents = stop_graph[current_rev_id]
1418
current_rev_id = current_rev_id_parents[0]
1420
current_rev_id = None
1421
new_history.reverse()
2600
1424
@needs_write_lock
2601
1425
def generate_revision_history(self, revision_id, last_rev=None,
2602
1426
other_branch=None):
2608
1432
:param other_branch: The other branch that DivergedBranches should
2609
1433
raise with respect to.
1435
revision_id = osutils.safe_revision_id(revision_id)
2611
1436
self.set_revision_history(self._lefthand_history(revision_id,
2612
1437
last_rev, other_branch))
1440
def update_revisions(self, other, stop_revision=None):
1441
"""See Branch.update_revisions."""
1444
if stop_revision is None:
1445
stop_revision = other.last_revision()
1446
if stop_revision is None:
1447
# if there are no commits, we're done.
1450
stop_revision = osutils.safe_revision_id(stop_revision)
1451
# whats the current last revision, before we fetch [and change it
1453
last_rev = self.last_revision()
1454
# we fetch here regardless of whether we need to so that we pickup
1456
self.fetch(other, stop_revision)
1457
my_ancestry = self.repository.get_ancestry(last_rev,
1459
if stop_revision in my_ancestry:
1460
# last_revision is a descendant of stop_revision
1462
self.generate_revision_history(stop_revision, last_rev=last_rev,
2614
1467
def basis_tree(self):
2615
1468
"""See Branch.basis_tree."""
2616
1469
return self.repository.revision_tree(self.last_revision())
1471
@deprecated_method(zero_eight)
1472
def working_tree(self):
1473
"""Create a Working tree object for this branch."""
1475
from bzrlib.transport.local import LocalTransport
1476
if (self.base.find('://') != -1 or
1477
not isinstance(self._transport, LocalTransport)):
1478
raise NoWorkingTree(self.base)
1479
return self.bzrdir.open_workingtree()
1482
def pull(self, source, overwrite=False, stop_revision=None,
1483
_hook_master=None, run_hooks=True):
1486
:param _hook_master: Private parameter - set the branch to
1487
be supplied as the master to push hooks.
1488
:param run_hooks: Private parameter - if false, this branch
1489
is being called because it's the master of the primary branch,
1490
so it should not run its hooks.
1492
result = PullResult()
1493
result.source_branch = source
1494
result.target_branch = self
1497
result.old_revno, result.old_revid = self.last_revision_info()
1499
self.update_revisions(source, stop_revision)
1500
except DivergedBranches:
1504
if stop_revision is None:
1505
stop_revision = source.last_revision()
1506
self.generate_revision_history(stop_revision)
1507
result.tag_conflicts = source.tags.merge_to(self.tags)
1508
result.new_revno, result.new_revid = self.last_revision_info()
1510
result.master_branch = _hook_master
1511
result.local_branch = self
1513
result.master_branch = self
1514
result.local_branch = None
1516
for hook in Branch.hooks['post_pull']:
2618
1522
def _get_parent_location(self):
2619
1523
_locs = ['parent', 'pull', 'x-pull']
2620
1524
for l in _locs:
2622
return self._transport.get_bytes(l).strip('\n')
2623
except errors.NoSuchFile:
1526
return self.control_files.get(l).read().strip('\n')
1532
def push(self, target, overwrite=False, stop_revision=None,
1533
_override_hook_source_branch=None):
1536
This is the basic concrete implementation of push()
1538
:param _override_hook_source_branch: If specified, run
1539
the hooks passing this Branch as the source, rather than self.
1540
This is for use of RemoteBranch, where push is delegated to the
1541
underlying vfs-based Branch.
1543
# TODO: Public option to disable running hooks - should be trivial but
1547
result = self._push_with_bound_branches(target, overwrite,
1549
_override_hook_source_branch=_override_hook_source_branch)
1554
def _push_with_bound_branches(self, target, overwrite,
1556
_override_hook_source_branch=None):
1557
"""Push from self into target, and into target's master if any.
1559
This is on the base BzrBranch class even though it doesn't support
1560
bound branches because the *target* might be bound.
1563
if _override_hook_source_branch:
1564
result.source_branch = _override_hook_source_branch
1565
for hook in Branch.hooks['post_push']:
1568
bound_location = target.get_bound_location()
1569
if bound_location and target.base != bound_location:
1570
# there is a master branch.
1572
# XXX: Why the second check? Is it even supported for a branch to
1573
# be bound to itself? -- mbp 20070507
1574
master_branch = target.get_master_branch()
1575
master_branch.lock_write()
1577
# push into the master from this branch.
1578
self._basic_push(master_branch, overwrite, stop_revision)
1579
# and push into the target branch from this. Note that we push from
1580
# this branch again, because its considered the highest bandwidth
1582
result = self._basic_push(target, overwrite, stop_revision)
1583
result.master_branch = master_branch
1584
result.local_branch = target
1588
master_branch.unlock()
1591
result = self._basic_push(target, overwrite, stop_revision)
1592
# TODO: Why set master_branch and local_branch if there's no
1593
# binding? Maybe cleaner to just leave them unset? -- mbp
1595
result.master_branch = target
1596
result.local_branch = None
2627
1600
def _basic_push(self, target, overwrite, stop_revision):
2628
1601
"""Basic implementation of push without bound branches or hooks.
2630
Must be called with source read locked and target write locked.
1603
Must be called with self read locked and target write locked.
2632
result = BranchPushResult()
1605
result = PushResult()
2633
1606
result.source_branch = self
2634
1607
result.target_branch = target
2635
1608
result.old_revno, result.old_revid = target.last_revision_info()
2636
self.update_references(target)
2637
if result.old_revid != self.last_revision():
2638
# We assume that during 'push' this repository is closer than
2640
graph = self.repository.get_graph(target.repository)
2641
target.update_revisions(self, stop_revision,
2642
overwrite=overwrite, graph=graph)
2643
if self._push_should_merge_tags():
2644
result.tag_conflicts = self.tags.merge_to(target.tags,
1610
target.update_revisions(self, stop_revision)
1611
except DivergedBranches:
1615
target.set_revision_history(self.revision_history())
1616
result.tag_conflicts = self.tags.merge_to(target.tags)
2646
1617
result.new_revno, result.new_revid = target.last_revision_info()
2649
def get_stacked_on_url(self):
2650
raise errors.UnstackableBranchFormat(self._format, self.user_url)
1620
def get_parent(self):
1621
"""See Branch.get_parent."""
1623
assert self.base[-1] == '/'
1624
parent = self._get_parent_location()
1627
# This is an old-format absolute path to a local branch
1628
# turn it into a url
1629
if parent.startswith('/'):
1630
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1632
return urlutils.join(self.base[:-1], parent)
1633
except errors.InvalidURLJoin, e:
1634
raise errors.InaccessibleParent(parent, self.base)
2652
1636
def set_push_location(self, location):
2653
1637
"""See Branch.set_push_location."""
2655
1639
'push_location', location,
2656
1640
store=_mod_config.STORE_LOCATION_NORECURSE)
1643
def set_parent(self, url):
1644
"""See Branch.set_parent."""
1645
# TODO: Maybe delete old location files?
1646
# URLs should never be unicode, even on the local fs,
1647
# FIXUP this and get_parent in a future branch format bump:
1648
# read and rewrite the file, and have the new format code read
1649
# using .get not .get_utf8. RBC 20060125
1651
if isinstance(url, unicode):
1653
url = url.encode('ascii')
1654
except UnicodeEncodeError:
1655
raise errors.InvalidURL(url,
1656
"Urls must be 7-bit ascii, "
1657
"use bzrlib.urlutils.escape")
1658
url = urlutils.relative_url(self.base, url)
1659
self._set_parent_location(url)
2658
1661
def _set_parent_location(self, url):
2659
1662
if url is None:
2660
self._transport.delete('parent')
1663
self.control_files._transport.delete('parent')
2662
self._transport.put_bytes('parent', url + '\n',
2663
mode=self.bzrdir._get_file_mode())
1665
assert isinstance(url, str)
1666
self.control_files.put_bytes('parent', url + '\n')
1668
@deprecated_function(zero_nine)
1669
def tree_config(self):
1670
"""DEPRECATED; call get_config instead.
1671
TreeConfig has become part of BranchConfig."""
1672
return TreeConfig(self)
2666
1675
class BzrBranch5(BzrBranch):
2667
"""A format 5 branch. This supports new features over plain branches.
1676
"""A format 5 branch. This supports new features over plan branches.
2669
1678
It has support for a master_branch which is the data for bound branches.
1686
super(BzrBranch5, self).__init__(_format=_format,
1687
_control_files=_control_files,
1689
_repository=_repository)
1692
def pull(self, source, overwrite=False, stop_revision=None,
1694
"""Pull from source into self, updating my master if any.
1696
:param run_hooks: Private parameter - if false, this branch
1697
is being called because it's the master of the primary branch,
1698
so it should not run its hooks.
1700
bound_location = self.get_bound_location()
1701
master_branch = None
1702
if bound_location and source.base != bound_location:
1703
# not pulling from master, so we need to update master.
1704
master_branch = self.get_master_branch()
1705
master_branch.lock_write()
1708
# pull from source into master.
1709
master_branch.pull(source, overwrite, stop_revision,
1711
return super(BzrBranch5, self).pull(source, overwrite,
1712
stop_revision, _hook_master=master_branch,
1713
run_hooks=run_hooks)
1716
master_branch.unlock()
2672
1718
def get_bound_location(self):
2674
return self._transport.get_bytes('bound')[:-1]
1720
return self.control_files.get_utf8('bound').read()[:-1]
2675
1721
except errors.NoSuchFile:
2678
1724
@needs_read_lock
2679
def get_master_branch(self, possible_transports=None):
1725
def get_master_branch(self):
2680
1726
"""Return the branch we are bound to.
2682
1728
:return: Either a Branch, or None
2684
1730
This could memoise the branch, but if thats done
2744
1804
return self.set_bound_location(None)
2746
1806
@needs_write_lock
2747
def update(self, possible_transports=None):
2748
"""Synchronise this branch with the master branch if any.
1808
"""Synchronise this branch with the master branch if any.
2750
1810
:return: None or the last_revision that was pivoted out during the
2753
master = self.get_master_branch(possible_transports)
1813
master = self.get_master_branch()
2754
1814
if master is not None:
2755
old_tip = _mod_revision.ensure_null(self.last_revision())
1815
old_tip = self.last_revision()
2756
1816
self.pull(master, overwrite=True)
2757
if self.repository.get_graph().is_ancestor(old_tip,
2758
_mod_revision.ensure_null(self.last_revision())):
1817
if old_tip in self.repository.get_ancestry(self.last_revision(),
2764
class BzrBranch8(BzrBranch5):
2765
"""A branch that stores tree-reference locations."""
2767
def _open_hook(self):
2768
if self._ignore_fallbacks:
1824
class BzrBranchExperimental(BzrBranch5):
1825
"""Bzr experimental branch format
1828
- a revision-history file.
1830
- a lock dir guarding the branch itself
1831
- all of this stored in a branch/ subdirectory
1832
- works with shared repositories.
1833
- a tag dictionary in the branch
1835
This format is new in bzr 0.15, but shouldn't be used for real data,
1838
This class acts as it's own BranchFormat.
1841
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1844
def get_format_string(cls):
1845
"""See BranchFormat.get_format_string()."""
1846
return "Bazaar-NG branch format experimental\n"
1849
def get_format_description(cls):
1850
"""See BranchFormat.get_format_description()."""
1851
return "Experimental branch format"
1854
def get_reference(cls, a_bzrdir):
1855
"""Get the target reference of the branch in a_bzrdir.
1857
format probing must have been completed before calling
1858
this method - it is assumed that the format of the branch
1859
in a_bzrdir is correct.
1861
:param a_bzrdir: The bzrdir to get the branch data from.
1862
:return: None if the branch is not a reference branch.
1867
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1869
branch_transport = a_bzrdir.get_branch_transport(cls)
1870
control_files = lockable_files.LockableFiles(branch_transport,
1871
lock_filename, lock_class)
1872
control_files.create_lock()
1873
control_files.lock_write()
2771
url = self.get_stacked_on_url()
2772
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2773
errors.UnstackableBranchFormat):
2776
for hook in Branch.hooks['transform_fallback_location']:
2777
url = hook(self, url)
2779
hook_name = Branch.hooks.get_hook_name(hook)
2780
raise AssertionError(
2781
"'transform_fallback_location' hook %s returned "
2782
"None, not a URL." % hook_name)
2783
self._activate_fallback_location(url)
2785
def __init__(self, *args, **kwargs):
2786
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2787
super(BzrBranch8, self).__init__(*args, **kwargs)
2788
self._last_revision_info_cache = None
2789
self._reference_info = None
2791
def _clear_cached_state(self):
2792
super(BzrBranch8, self)._clear_cached_state()
2793
self._last_revision_info_cache = None
2794
self._reference_info = None
2796
def _last_revision_info(self):
2797
revision_string = self._transport.get_bytes('last-revision')
1875
for filename, content in utf8_files:
1876
control_files.put_utf8(filename, content)
1878
control_files.unlock()
1881
def initialize(cls, a_bzrdir):
1882
"""Create a branch of this format in a_bzrdir."""
1883
utf8_files = [('format', cls.get_format_string()),
1884
('revision-history', ''),
1885
('branch-name', ''),
1888
cls._initialize_control_files(a_bzrdir, utf8_files,
1889
'lock', lockdir.LockDir)
1890
return cls.open(a_bzrdir, _found=True)
1893
def open(cls, a_bzrdir, _found=False):
1894
"""Return the branch object for a_bzrdir
1896
_found is a private parameter, do not use it. It is used to indicate
1897
if format probing has already be done.
1900
format = BranchFormat.find_format(a_bzrdir)
1901
assert format.__class__ == cls
1902
transport = a_bzrdir.get_branch_transport(None)
1903
control_files = lockable_files.LockableFiles(transport, 'lock',
1905
return cls(_format=cls,
1906
_control_files=control_files,
1908
_repository=a_bzrdir.find_repository())
1911
def is_supported(cls):
1914
def _make_tags(self):
1915
return BasicTags(self)
1918
def supports_tags(cls):
1922
BranchFormat.register_format(BzrBranchExperimental)
1925
class BzrBranch6(BzrBranch5):
1928
def last_revision_info(self):
1929
revision_string = self.control_files.get('last-revision').read()
2798
1930
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2799
1931
revision_id = cache_utf8.get_cached_utf8(revision_id)
2800
1932
revno = int(revno)
2801
1933
return revno, revision_id
1935
def last_revision(self):
1936
"""Return last revision id, or None"""
1937
revision_id = self.last_revision_info()[1]
1938
if revision_id == _mod_revision.NULL_REVISION:
2803
1942
def _write_last_revision_info(self, revno, revision_id):
2804
1943
"""Simply write out the revision id, with no checks.
2809
1948
Intended to be called by set_last_revision_info and
2810
1949
_write_revision_history.
2812
revision_id = _mod_revision.ensure_null(revision_id)
1951
if revision_id is None:
1952
revision_id = 'null:'
2813
1953
out_string = '%d %s\n' % (revno, revision_id)
2814
self._transport.put_bytes('last-revision', out_string,
2815
mode=self.bzrdir._get_file_mode())
1954
self.control_files.put_bytes('last-revision', out_string)
2817
1956
@needs_write_lock
2818
1957
def set_last_revision_info(self, revno, revision_id):
2819
revision_id = _mod_revision.ensure_null(revision_id)
2820
old_revno, old_revid = self.last_revision_info()
1958
revision_id = osutils.safe_revision_id(revision_id)
2821
1959
if self._get_append_revisions_only():
2822
1960
self._check_history_violation(revision_id)
2823
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2824
1961
self._write_last_revision_info(revno, revision_id)
2825
1962
self._clear_cached_state()
2826
self._last_revision_info_cache = revno, revision_id
2827
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2829
def _synchronize_history(self, destination, revision_id):
2830
"""Synchronize last revision and revision history between branches.
2832
:see: Branch._synchronize_history
2834
# XXX: The base Branch has a fast implementation of this method based
2835
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2836
# that uses set_revision_history. This class inherits from BzrBranch5,
2837
# but wants the fast implementation, so it calls
2838
# Branch._synchronize_history directly.
2839
Branch._synchronize_history(self, destination, revision_id)
2841
1964
def _check_history_violation(self, revision_id):
2842
last_revision = _mod_revision.ensure_null(self.last_revision())
2843
if _mod_revision.is_null(last_revision):
1965
last_revision = self.last_revision()
1966
if last_revision is None:
2845
1968
if last_revision not in self._lefthand_history(revision_id):
2846
raise errors.AppendRevisionsOnlyViolation(self.user_url)
1969
raise errors.AppendRevisionsOnlyViolation(self.base)
2848
1971
def _gen_revision_history(self):
2849
1972
"""Generate the revision history from last revision
2851
last_revno, last_revision = self.last_revision_info()
2852
self._extend_partial_history(stop_index=last_revno-1)
2853
return list(reversed(self._partial_revision_history_cache))
1974
history = list(self.repository.iter_reverse_revision_history(
1975
self.last_revision()))
2855
1979
def _write_revision_history(self, history):
2856
1980
"""Factored out of set_revision_history.
2878
2021
"""Set the parent branch"""
2879
2022
return self._get_config_location('parent_location')
2882
def _set_all_reference_info(self, info_dict):
2883
"""Replace all reference info stored in a branch.
2885
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2888
writer = rio.RioWriter(s)
2889
for key, (tree_path, branch_location) in info_dict.iteritems():
2890
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2891
branch_location=branch_location)
2892
writer.write_stanza(stanza)
2893
self._transport.put_bytes('references', s.getvalue())
2894
self._reference_info = info_dict
2897
def _get_all_reference_info(self):
2898
"""Return all the reference info stored in a branch.
2900
:return: A dict of {file_id: (tree_path, branch_location)}
2902
if self._reference_info is not None:
2903
return self._reference_info
2904
rio_file = self._transport.get('references')
2906
stanzas = rio.read_stanzas(rio_file)
2907
info_dict = dict((s['file_id'], (s['tree_path'],
2908
s['branch_location'])) for s in stanzas)
2911
self._reference_info = info_dict
2914
def set_reference_info(self, file_id, tree_path, branch_location):
2915
"""Set the branch location to use for a tree reference.
2917
:param file_id: The file-id of the tree reference.
2918
:param tree_path: The path of the tree reference in the tree.
2919
:param branch_location: The location of the branch to retrieve tree
2922
info_dict = self._get_all_reference_info()
2923
info_dict[file_id] = (tree_path, branch_location)
2924
if None in (tree_path, branch_location):
2925
if tree_path is not None:
2926
raise ValueError('tree_path must be None when branch_location'
2928
if branch_location is not None:
2929
raise ValueError('branch_location must be None when tree_path'
2931
del info_dict[file_id]
2932
self._set_all_reference_info(info_dict)
2934
def get_reference_info(self, file_id):
2935
"""Get the tree_path and branch_location for a tree reference.
2937
:return: a tuple of (tree_path, branch_location)
2939
return self._get_all_reference_info().get(file_id, (None, None))
2941
def reference_parent(self, file_id, path, possible_transports=None):
2942
"""Return the parent branch for a tree-reference file_id.
2944
:param file_id: The file_id of the tree reference
2945
:param path: The path of the file_id in the tree
2946
:return: A branch associated with the file_id
2948
branch_location = self.get_reference_info(file_id)[1]
2949
if branch_location is None:
2950
return Branch.reference_parent(self, file_id, path,
2951
possible_transports)
2952
branch_location = urlutils.join(self.user_url, branch_location)
2953
return Branch.open(branch_location,
2954
possible_transports=possible_transports)
2956
2024
def set_push_location(self, location):
2957
2025
"""See Branch.set_push_location."""
2958
2026
self._set_config_location('push_location', location)
2991
2059
"""See Branch.get_old_bound_location"""
2992
2060
return self._get_bound_location(False)
2994
def get_stacked_on_url(self):
2995
# you can always ask for the URL; but you might not be able to use it
2996
# if the repo can't support stacking.
2997
## self._check_stackable_repo()
2998
stacked_url = self._get_config_location('stacked_on_location')
2999
if stacked_url is None:
3000
raise errors.NotStacked(self)
2062
def set_append_revisions_only(self, enabled):
2067
self.get_config().set_user_option('append_revisions_only', value)
3003
2069
def _get_append_revisions_only(self):
3004
return self.get_config(
3005
).get_user_option_as_bool('append_revisions_only')
3008
def generate_revision_history(self, revision_id, last_rev=None,
3010
"""See BzrBranch5.generate_revision_history"""
3011
history = self._lefthand_history(revision_id, last_rev, other_branch)
3012
revno = len(history)
3013
self.set_last_revision_info(revno, revision_id)
3016
def get_rev_id(self, revno, history=None):
3017
"""Find the revision id of the specified revno."""
3019
return _mod_revision.NULL_REVISION
3021
last_revno, last_revision_id = self.last_revision_info()
3022
if revno <= 0 or revno > last_revno:
3023
raise errors.NoSuchRevision(self, revno)
3025
if history is not None:
3026
return history[revno - 1]
3028
index = last_revno - revno
3029
if len(self._partial_revision_history_cache) <= index:
3030
self._extend_partial_history(stop_index=index)
3031
if len(self._partial_revision_history_cache) > index:
3032
return self._partial_revision_history_cache[index]
2070
value = self.get_config().get_user_option('append_revisions_only')
2071
return value == 'True'
2073
def _synchronize_history(self, destination, revision_id):
2074
"""Synchronize last revision and revision history between branches.
2076
This version is most efficient when the destination is also a
2077
BzrBranch6, but works for BzrBranch5, as long as the destination's
2078
repository contains all the lefthand ancestors of the intended
2079
last_revision. If not, set_last_revision_info will fail.
2081
:param destination: The branch to copy the history into
2082
:param revision_id: The revision-id to truncate history at. May
2083
be None to copy complete history.
2085
if revision_id is None:
2086
revno, revision_id = self.last_revision_info()
3034
raise errors.NoSuchRevision(self, revno)
3037
def revision_id_to_revno(self, revision_id):
3038
"""Given a revision id, return its revno"""
3039
if _mod_revision.is_null(revision_id):
3042
index = self._partial_revision_history_cache.index(revision_id)
3044
self._extend_partial_history(stop_revision=revision_id)
3045
index = len(self._partial_revision_history_cache) - 1
3046
if self._partial_revision_history_cache[index] != revision_id:
3047
raise errors.NoSuchRevision(self, revision_id)
3048
return self.revno() - index
3051
class BzrBranch7(BzrBranch8):
3052
"""A branch with support for a fallback repository."""
3054
def set_reference_info(self, file_id, tree_path, branch_location):
3055
Branch.set_reference_info(self, file_id, tree_path, branch_location)
3057
def get_reference_info(self, file_id):
3058
Branch.get_reference_info(self, file_id)
3060
def reference_parent(self, file_id, path, possible_transports=None):
3061
return Branch.reference_parent(self, file_id, path,
3062
possible_transports)
3065
class BzrBranch6(BzrBranch7):
3066
"""See BzrBranchFormat6 for the capabilities of this branch.
3068
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2088
# To figure out the revno for a random revision, we need to build
2089
# the revision history, and count its length.
2090
# We don't care about the order, just how long it is.
2091
# Alternatively, we could start at the current location, and count
2092
# backwards. But there is no guarantee that we will find it since
2093
# it may be a merged revision.
2094
revno = len(list(self.repository.iter_reverse_revision_history(
2096
destination.set_last_revision_info(revno, revision_id)
2098
def _make_tags(self):
2099
return BasicTags(self)
2102
class BranchTestProviderAdapter(object):
2103
"""A tool to generate a suite testing multiple branch formats at once.
2105
This is done by copying the test once for each transport and injecting
2106
the transport_server, transport_readonly_server, and branch_format
2107
classes into each copy. Each copy is also given a new id() to make it
3072
def get_stacked_on_url(self):
3073
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2111
def __init__(self, transport_server, transport_readonly_server, formats,
2112
vfs_transport_factory=None):
2113
self._transport_server = transport_server
2114
self._transport_readonly_server = transport_readonly_server
2115
self._formats = formats
2117
def adapt(self, test):
2118
result = TestSuite()
2119
for branch_format, bzrdir_format in self._formats:
2120
new_test = deepcopy(test)
2121
new_test.transport_server = self._transport_server
2122
new_test.transport_readonly_server = self._transport_readonly_server
2123
new_test.bzrdir_format = bzrdir_format
2124
new_test.branch_format = branch_format
2125
def make_new_test_id():
2126
# the format can be either a class or an instance
2127
name = getattr(branch_format, '__name__',
2128
branch_format.__class__.__name__)
2129
new_id = "%s(%s)" % (new_test.id(), name)
2130
return lambda: new_id
2131
new_test.id = make_new_test_id()
2132
result.addTest(new_test)
3076
2136
######################################################################
3187
2234
new_branch.tags._set_tag_dict({})
3189
2236
# Copying done; now update target format
3190
new_branch._transport.put_bytes('format',
3191
format.get_format_string(),
3192
mode=new_branch.bzrdir._get_file_mode())
2237
new_branch.control_files.put_utf8('format',
2238
format.get_format_string())
3194
2240
# Clean up old files
3195
new_branch._transport.delete('revision-history')
2241
new_branch.control_files._transport.delete('revision-history')
3197
2243
branch.set_parent(None)
3198
except errors.NoSuchFile:
3200
2246
branch.set_bound_location(None)
3203
class Converter6to7(object):
3204
"""Perform an in-place upgrade of format 6 to format 7"""
3206
def convert(self, branch):
3207
format = BzrBranchFormat7()
3208
branch._set_config_location('stacked_on_location', '')
3209
# update target format
3210
branch._transport.put_bytes('format', format.get_format_string())
3213
class Converter7to8(object):
3214
"""Perform an in-place upgrade of format 6 to format 7"""
3216
def convert(self, branch):
3217
format = BzrBranchFormat8()
3218
branch._transport.put_bytes('references', '')
3219
# update target format
3220
branch._transport.put_bytes('format', format.get_format_string())
3223
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3224
"""Run ``callable(*args, **kwargs)``, write-locking target for the
3227
_run_with_write_locked_target will attempt to release the lock it acquires.
3229
If an exception is raised by callable, then that exception *will* be
3230
propagated, even if the unlock attempt raises its own error. Thus
3231
_run_with_write_locked_target should be preferred to simply doing::
3235
return callable(*args, **kwargs)
3240
# This is very similar to bzrlib.decorators.needs_write_lock. Perhaps they
3241
# should share code?
3244
result = callable(*args, **kwargs)
3246
exc_info = sys.exc_info()
3250
raise exc_info[0], exc_info[1], exc_info[2]
3256
class InterBranch(InterObject):
3257
"""This class represents operations taking place between two branches.
3259
Its instances have methods like pull() and push() and contain
3260
references to the source and target repositories these operations
3261
can be carried out on.
3265
"""The available optimised InterBranch types."""
3268
def _get_branch_formats_to_test(klass):
3269
"""Return an iterable of format tuples for testing.
3271
:return: An iterable of (from_format, to_format) to use when testing
3272
this InterBranch class. Each InterBranch class should define this
3275
raise NotImplementedError(klass._get_branch_formats_to_test)
3278
def pull(self, overwrite=False, stop_revision=None,
3279
possible_transports=None, local=False):
3280
"""Mirror source into target branch.
3282
The target branch is considered to be 'local', having low latency.
3284
:returns: PullResult instance
3286
raise NotImplementedError(self.pull)
3289
def update_revisions(self, stop_revision=None, overwrite=False,
3291
"""Pull in new perfect-fit revisions.
3293
:param stop_revision: Updated until the given revision
3294
:param overwrite: Always set the branch pointer, rather than checking
3295
to see if it is a proper descendant.
3296
:param graph: A Graph object that can be used to query history
3297
information. This can be None.
3300
raise NotImplementedError(self.update_revisions)
3303
def push(self, overwrite=False, stop_revision=None,
3304
_override_hook_source_branch=None):
3305
"""Mirror the source branch into the target branch.
3307
The source branch is considered to be 'local', having low latency.
3309
raise NotImplementedError(self.push)
3312
def copy_content_into(self, revision_id=None):
3313
"""Copy the content of source into target
3315
revision_id: if not None, the revision history in the new branch will
3316
be truncated to end with revision_id.
3318
raise NotImplementedError(self.copy_content_into)
3321
class GenericInterBranch(InterBranch):
3322
"""InterBranch implementation that uses public Branch functions."""
3325
def is_compatible(klass, source, target):
3326
# GenericBranch uses the public API, so always compatible
3330
def _get_branch_formats_to_test(klass):
3331
return [(BranchFormat._default_format, BranchFormat._default_format)]
3334
def unwrap_format(klass, format):
3335
if isinstance(format, remote.RemoteBranchFormat):
3336
format._ensure_real()
3337
return format._custom_format
3341
def copy_content_into(self, revision_id=None):
3342
"""Copy the content of source into target
3344
revision_id: if not None, the revision history in the new branch will
3345
be truncated to end with revision_id.
3347
self.source.update_references(self.target)
3348
self.source._synchronize_history(self.target, revision_id)
3350
parent = self.source.get_parent()
3351
except errors.InaccessibleParent, e:
3352
mutter('parent was not accessible to copy: %s', e)
3355
self.target.set_parent(parent)
3356
if self.source._push_should_merge_tags():
3357
self.source.tags.merge_to(self.target.tags)
3360
def update_revisions(self, stop_revision=None, overwrite=False,
3362
"""See InterBranch.update_revisions()."""
3363
other_revno, other_last_revision = self.source.last_revision_info()
3364
stop_revno = None # unknown
3365
if stop_revision is None:
3366
stop_revision = other_last_revision
3367
if _mod_revision.is_null(stop_revision):
3368
# if there are no commits, we're done.
3370
stop_revno = other_revno
3372
# what's the current last revision, before we fetch [and change it
3374
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3375
# we fetch here so that we don't process data twice in the common
3376
# case of having something to pull, and so that the check for
3377
# already merged can operate on the just fetched graph, which will
3378
# be cached in memory.
3379
self.target.fetch(self.source, stop_revision)
3380
# Check to see if one is an ancestor of the other
3383
graph = self.target.repository.get_graph()
3384
if self.target._check_if_descendant_or_diverged(
3385
stop_revision, last_rev, graph, self.source):
3386
# stop_revision is a descendant of last_rev, but we aren't
3387
# overwriting, so we're done.
3389
if stop_revno is None:
3391
graph = self.target.repository.get_graph()
3392
this_revno, this_last_revision = \
3393
self.target.last_revision_info()
3394
stop_revno = graph.find_distance_to_null(stop_revision,
3395
[(other_last_revision, other_revno),
3396
(this_last_revision, this_revno)])
3397
self.target.set_last_revision_info(stop_revno, stop_revision)
3400
def pull(self, overwrite=False, stop_revision=None,
3401
possible_transports=None, run_hooks=True,
3402
_override_hook_target=None, local=False):
3403
"""Pull from source into self, updating my master if any.
3405
:param run_hooks: Private parameter - if false, this branch
3406
is being called because it's the master of the primary branch,
3407
so it should not run its hooks.
3409
bound_location = self.target.get_bound_location()
3410
if local and not bound_location:
3411
raise errors.LocalRequiresBoundBranch()
3412
master_branch = None
3413
if not local and bound_location and self.source.user_url != bound_location:
3414
# not pulling from master, so we need to update master.
3415
master_branch = self.target.get_master_branch(possible_transports)
3416
master_branch.lock_write()
3419
# pull from source into master.
3420
master_branch.pull(self.source, overwrite, stop_revision,
3422
return self._pull(overwrite,
3423
stop_revision, _hook_master=master_branch,
3424
run_hooks=run_hooks,
3425
_override_hook_target=_override_hook_target)
3428
master_branch.unlock()
3430
def push(self, overwrite=False, stop_revision=None,
3431
_override_hook_source_branch=None):
3432
"""See InterBranch.push.
3434
This is the basic concrete implementation of push()
3436
:param _override_hook_source_branch: If specified, run
3437
the hooks passing this Branch as the source, rather than self.
3438
This is for use of RemoteBranch, where push is delegated to the
3439
underlying vfs-based Branch.
3441
# TODO: Public option to disable running hooks - should be trivial but
3443
self.source.lock_read()
3445
return _run_with_write_locked_target(
3446
self.target, self._push_with_bound_branches, overwrite,
3448
_override_hook_source_branch=_override_hook_source_branch)
3450
self.source.unlock()
3452
def _push_with_bound_branches(self, overwrite, stop_revision,
3453
_override_hook_source_branch=None):
3454
"""Push from source into target, and into target's master if any.
3457
if _override_hook_source_branch:
3458
result.source_branch = _override_hook_source_branch
3459
for hook in Branch.hooks['post_push']:
3462
bound_location = self.target.get_bound_location()
3463
if bound_location and self.target.base != bound_location:
3464
# there is a master branch.
3466
# XXX: Why the second check? Is it even supported for a branch to
3467
# be bound to itself? -- mbp 20070507
3468
master_branch = self.target.get_master_branch()
3469
master_branch.lock_write()
3471
# push into the master from the source branch.
3472
self.source._basic_push(master_branch, overwrite, stop_revision)
3473
# and push into the target branch from the source. Note that we
3474
# push from the source branch again, because its considered the
3475
# highest bandwidth repository.
3476
result = self.source._basic_push(self.target, overwrite,
3478
result.master_branch = master_branch
3479
result.local_branch = self.target
3483
master_branch.unlock()
3486
result = self.source._basic_push(self.target, overwrite,
3488
# TODO: Why set master_branch and local_branch if there's no
3489
# binding? Maybe cleaner to just leave them unset? -- mbp
3491
result.master_branch = self.target
3492
result.local_branch = None
3496
def _pull(self, overwrite=False, stop_revision=None,
3497
possible_transports=None, _hook_master=None, run_hooks=True,
3498
_override_hook_target=None, local=False):
3501
This function is the core worker, used by GenericInterBranch.pull to
3502
avoid duplication when pulling source->master and source->local.
3504
:param _hook_master: Private parameter - set the branch to
3505
be supplied as the master to pull hooks.
3506
:param run_hooks: Private parameter - if false, this branch
3507
is being called because it's the master of the primary branch,
3508
so it should not run its hooks.
3509
:param _override_hook_target: Private parameter - set the branch to be
3510
supplied as the target_branch to pull hooks.
3511
:param local: Only update the local branch, and not the bound branch.
3513
# This type of branch can't be bound.
3515
raise errors.LocalRequiresBoundBranch()
3516
result = PullResult()
3517
result.source_branch = self.source
3518
if _override_hook_target is None:
3519
result.target_branch = self.target
3521
result.target_branch = _override_hook_target
3522
self.source.lock_read()
3524
# We assume that during 'pull' the target repository is closer than
3526
self.source.update_references(self.target)
3527
graph = self.target.repository.get_graph(self.source.repository)
3528
# TODO: Branch formats should have a flag that indicates
3529
# that revno's are expensive, and pull() should honor that flag.
3531
result.old_revno, result.old_revid = \
3532
self.target.last_revision_info()
3533
self.target.update_revisions(self.source, stop_revision,
3534
overwrite=overwrite, graph=graph)
3535
# TODO: The old revid should be specified when merging tags,
3536
# so a tags implementation that versions tags can only
3537
# pull in the most recent changes. -- JRV20090506
3538
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3540
result.new_revno, result.new_revid = self.target.last_revision_info()
3542
result.master_branch = _hook_master
3543
result.local_branch = result.target_branch
3545
result.master_branch = result.target_branch
3546
result.local_branch = None
3548
for hook in Branch.hooks['post_pull']:
3551
self.source.unlock()
3555
InterBranch.register_optimiser(GenericInterBranch)