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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
from __future__ import absolute_import
21
from cStringIO import StringIO
18
23
from bzrlib.lazy_import import lazy_import
19
24
lazy_import(globals(), """
20
26
from bzrlib import (
23
config as _mod_config,
29
revision as _mod_revision,
35
from bzrlib.config import BranchConfig
36
from bzrlib.tag import (
31
config as _mod_config,
40
revision as _mod_revision,
49
from bzrlib.i18n import gettext, ngettext
42
from bzrlib.decorators import needs_read_lock, needs_write_lock
52
# Explicitly import bzrlib.bzrdir so that the BzrProber
53
# is guaranteed to be registered.
60
from bzrlib.decorators import (
43
65
from bzrlib.hooks import Hooks
66
from bzrlib.inter import InterObject
67
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
68
from bzrlib import registry
69
from bzrlib.symbol_versioning import (
44
73
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
47
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
48
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
49
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
52
# TODO: Maybe include checks for common corruption of newlines, etc?
54
# TODO: Some operations like log might retrieve the same revisions
55
# repeatedly to calculate deltas. We could perhaps have a weakref
56
# cache in memory to make this faster. In general anything can be
57
# cached in memory between lock and unlock operations. .. nb thats
58
# what the transaction identity map provides
61
######################################################################
76
class Branch(controldir.ControlComponent):
65
77
"""Branch holding a history of revisions.
68
Base directory/url of the branch.
70
hooks: An instance of BranchHooks.
80
Base directory/url of the branch; using control_url and
81
control_transport is more standardized.
82
:ivar hooks: An instance of BranchHooks.
83
:ivar _master_branch_cache: cached result of get_master_branch, see
72
86
# this is really an instance variable - FIXME move it there
76
# override this to set the strategy for storing tags
78
return DisabledTags(self)
80
def __init__(self, *ignored, **ignored_too):
81
self.tags = self._make_tags()
91
def control_transport(self):
92
return self._transport
95
def user_transport(self):
96
return self.bzrdir.user_transport
98
def __init__(self, possible_transports=None):
99
self.tags = self._format.make_tags(self)
82
100
self._revision_history_cache = None
83
101
self._revision_id_to_revno_cache = None
102
self._partial_revision_id_to_revno_cache = {}
103
self._partial_revision_history_cache = []
104
self._tags_bytes = None
105
self._last_revision_info_cache = None
106
self._master_branch_cache = None
107
self._merge_sorted_revisions_cache = None
108
self._open_hook(possible_transports)
109
hooks = Branch.hooks['open']
113
def _open_hook(self, possible_transports):
114
"""Called by init to allow simpler extension of the base class."""
116
def _activate_fallback_location(self, url, possible_transports):
117
"""Activate the branch/repository from url as a fallback repository."""
118
for existing_fallback_repo in self.repository._fallback_repositories:
119
if existing_fallback_repo.user_url == url:
120
# This fallback is already configured. This probably only
121
# happens because ControlDir.sprout is a horrible mess. To avoid
122
# confusing _unstack we don't add this a second time.
123
mutter('duplicate activation of fallback %r on %r', url, self)
125
repo = self._get_fallback_repository(url, possible_transports)
126
if repo.has_same_location(self.repository):
127
raise errors.UnstackableLocationError(self.user_url, url)
128
self.repository.add_fallback_repository(repo)
85
130
def break_lock(self):
86
131
"""Break a lock if one is present from another instance.
103
182
For instance, if the branch is at URL/.bzr/branch,
104
183
Branch.open(URL) -> a Branch instance.
106
control = bzrdir.BzrDir.open(base, _unsupported,
107
possible_transports=possible_transports)
108
return control.open_branch(_unsupported)
185
control = controldir.ControlDir.open(base,
186
possible_transports=possible_transports, _unsupported=_unsupported)
187
return control.open_branch(unsupported=_unsupported,
188
possible_transports=possible_transports)
111
def open_from_transport(transport, _unsupported=False):
191
def open_from_transport(transport, name=None, _unsupported=False,
192
possible_transports=None):
112
193
"""Open the branch rooted at transport"""
113
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
114
return control.open_branch(_unsupported)
194
control = controldir.ControlDir.open_from_transport(transport, _unsupported)
195
return control.open_branch(name=name, unsupported=_unsupported,
196
possible_transports=possible_transports)
117
199
def open_containing(url, possible_transports=None):
118
200
"""Open an existing branch which contains url.
120
202
This probes for a branch at url, and searches upwards from there.
122
204
Basically we keep looking up until we find the control directory or
123
205
run into the root. If there isn't one, raises NotBranchError.
124
If there is one and it is either an unrecognised format or an unsupported
206
If there is one and it is either an unrecognised format or an unsupported
125
207
format, UnknownFormatError or UnsupportedFormatError are raised.
126
208
If there is one, it is returned, along with the unused portion of url.
128
control, relpath = bzrdir.BzrDir.open_containing(url,
210
control, relpath = controldir.ControlDir.open_containing(url,
129
211
possible_transports)
130
return control.open_branch(), relpath
212
branch = control.open_branch(possible_transports=possible_transports)
213
return (branch, relpath)
215
def _push_should_merge_tags(self):
216
"""Should _basic_push merge this branch's tags into the target?
218
The default implementation returns False if this branch has no tags,
219
and True the rest of the time. Subclasses may override this.
221
return self.supports_tags() and self.tags.get_tag_dict()
132
223
def get_config(self):
133
return BranchConfig(self)
136
return self.get_config().get_nickname()
224
"""Get a bzrlib.config.BranchConfig for this Branch.
226
This can then be used to get and set configuration options for the
229
:return: A bzrlib.config.BranchConfig.
231
return _mod_config.BranchConfig(self)
233
def get_config_stack(self):
234
"""Get a bzrlib.config.BranchStack for this Branch.
236
This can then be used to get and set configuration options for the
239
:return: A bzrlib.config.BranchStack.
241
return _mod_config.BranchStack(self)
243
def _get_config(self):
244
"""Get the concrete config for just the config in this branch.
246
This is not intended for client use; see Branch.get_config for the
251
:return: An object supporting get_option and set_option.
253
raise NotImplementedError(self._get_config)
255
def store_uncommitted(self, creator, message=None):
256
"""Store uncommitted changes from a ShelfCreator.
258
:param creator: The ShelfCreator containing uncommitted changes, or
259
None to delete any stored changes.
260
:param message: The message to associate with the changes.
261
:raises: ChangesAlreadyStored if the branch already has changes.
263
raise NotImplementedError(self.store_uncommitted)
265
def get_unshelver(self, tree):
266
"""Return a shelf.Unshelver for this branch and tree.
268
:param tree: The tree to use to construct the Unshelver.
269
:return: an Unshelver or None if no changes are stored.
271
raise NotImplementedError(self.get_unshelver)
273
def _get_fallback_repository(self, url, possible_transports):
274
"""Get the repository we fallback to at url."""
275
url = urlutils.join(self.base, url)
276
a_branch = Branch.open(url, possible_transports=possible_transports)
277
return a_branch.repository
280
def _get_tags_bytes(self):
281
"""Get the bytes of a serialised tags dict.
283
Note that not all branches support tags, nor do all use the same tags
284
logic: this method is specific to BasicTags. Other tag implementations
285
may use the same method name and behave differently, safely, because
286
of the double-dispatch via
287
format.make_tags->tags_instance->get_tags_dict.
289
:return: The bytes of the tags file.
290
:seealso: Branch._set_tags_bytes.
292
if self._tags_bytes is None:
293
self._tags_bytes = self._transport.get_bytes('tags')
294
return self._tags_bytes
296
def _get_nick(self, local=False, possible_transports=None):
297
config = self.get_config()
298
# explicit overrides master, but don't look for master if local is True
299
if not local and not config.has_explicit_nickname():
301
master = self.get_master_branch(possible_transports)
302
if master and self.user_url == master.user_url:
303
raise errors.RecursiveBind(self.user_url)
304
if master is not None:
305
# return the master branch value
307
except errors.RecursiveBind, e:
309
except errors.BzrError, e:
310
# Silently fall back to local implicit nick if the master is
312
mutter("Could not connect to bound branch, "
313
"falling back to local nick.\n " + str(e))
314
return config.get_nickname()
138
316
def _set_nick(self, nick):
139
317
self.get_config().set_user_option('nickname', nick, warn_masked=True)
190
469
:return: A dictionary mapping revision_id => dotted revno.
192
last_revision = self.last_revision()
193
revision_graph = repository._old_get_graph(self.repository,
195
merge_sorted_revisions = tsort.merge_sort(
200
471
revision_id_to_revno = dict((rev_id, revno)
201
for seq_num, rev_id, depth, revno, end_of_merge
202
in merge_sorted_revisions)
472
for rev_id, depth, revno, end_of_merge
473
in self.iter_merge_sorted_revisions())
203
474
return revision_id_to_revno
477
def iter_merge_sorted_revisions(self, start_revision_id=None,
478
stop_revision_id=None, stop_rule='exclude', direction='reverse'):
479
"""Walk the revisions for a branch in merge sorted order.
481
Merge sorted order is the output from a merge-aware,
482
topological sort, i.e. all parents come before their
483
children going forward; the opposite for reverse.
485
:param start_revision_id: the revision_id to begin walking from.
486
If None, the branch tip is used.
487
:param stop_revision_id: the revision_id to terminate the walk
488
after. If None, the rest of history is included.
489
:param stop_rule: if stop_revision_id is not None, the precise rule
490
to use for termination:
492
* 'exclude' - leave the stop revision out of the result (default)
493
* 'include' - the stop revision is the last item in the result
494
* 'with-merges' - include the stop revision and all of its
495
merged revisions in the result
496
* 'with-merges-without-common-ancestry' - filter out revisions
497
that are in both ancestries
498
:param direction: either 'reverse' or 'forward':
500
* reverse means return the start_revision_id first, i.e.
501
start at the most recent revision and go backwards in history
502
* forward returns tuples in the opposite order to reverse.
503
Note in particular that forward does *not* do any intelligent
504
ordering w.r.t. depth as some clients of this API may like.
505
(If required, that ought to be done at higher layers.)
507
:return: an iterator over (revision_id, depth, revno, end_of_merge)
510
* revision_id: the unique id of the revision
511
* depth: How many levels of merging deep this node has been
513
* revno_sequence: This field provides a sequence of
514
revision numbers for all revisions. The format is:
515
(REVNO, BRANCHNUM, BRANCHREVNO). BRANCHNUM is the number of the
516
branch that the revno is on. From left to right the REVNO numbers
517
are the sequence numbers within that branch of the revision.
518
* end_of_merge: When True the next node (earlier in history) is
519
part of a different merge.
521
# Note: depth and revno values are in the context of the branch so
522
# we need the full graph to get stable numbers, regardless of the
524
if self._merge_sorted_revisions_cache is None:
525
last_revision = self.last_revision()
526
known_graph = self.repository.get_known_graph_ancestry(
528
self._merge_sorted_revisions_cache = known_graph.merge_sort(
530
filtered = self._filter_merge_sorted_revisions(
531
self._merge_sorted_revisions_cache, start_revision_id,
532
stop_revision_id, stop_rule)
533
# Make sure we don't return revisions that are not part of the
534
# start_revision_id ancestry.
535
filtered = self._filter_start_non_ancestors(filtered)
536
if direction == 'reverse':
538
if direction == 'forward':
539
return reversed(list(filtered))
541
raise ValueError('invalid direction %r' % direction)
543
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
544
start_revision_id, stop_revision_id, stop_rule):
545
"""Iterate over an inclusive range of sorted revisions."""
546
rev_iter = iter(merge_sorted_revisions)
547
if start_revision_id is not None:
548
for node in rev_iter:
550
if rev_id != start_revision_id:
553
# The decision to include the start or not
554
# depends on the stop_rule if a stop is provided
555
# so pop this node back into the iterator
556
rev_iter = itertools.chain(iter([node]), rev_iter)
558
if stop_revision_id is None:
560
for node in rev_iter:
562
yield (rev_id, node.merge_depth, node.revno,
564
elif stop_rule == 'exclude':
565
for node in rev_iter:
567
if rev_id == stop_revision_id:
569
yield (rev_id, node.merge_depth, node.revno,
571
elif stop_rule == 'include':
572
for node in rev_iter:
574
yield (rev_id, node.merge_depth, node.revno,
576
if rev_id == stop_revision_id:
578
elif stop_rule == 'with-merges-without-common-ancestry':
579
# We want to exclude all revisions that are already part of the
580
# stop_revision_id ancestry.
581
graph = self.repository.get_graph()
582
ancestors = graph.find_unique_ancestors(start_revision_id,
584
for node in rev_iter:
586
if rev_id not in ancestors:
588
yield (rev_id, node.merge_depth, node.revno,
590
elif stop_rule == 'with-merges':
591
stop_rev = self.repository.get_revision(stop_revision_id)
592
if stop_rev.parent_ids:
593
left_parent = stop_rev.parent_ids[0]
595
left_parent = _mod_revision.NULL_REVISION
596
# left_parent is the actual revision we want to stop logging at,
597
# since we want to show the merged revisions after the stop_rev too
598
reached_stop_revision_id = False
599
revision_id_whitelist = []
600
for node in rev_iter:
602
if rev_id == left_parent:
603
# reached the left parent after the stop_revision
605
if (not reached_stop_revision_id or
606
rev_id in revision_id_whitelist):
607
yield (rev_id, node.merge_depth, node.revno,
609
if reached_stop_revision_id or rev_id == stop_revision_id:
610
# only do the merged revs of rev_id from now on
611
rev = self.repository.get_revision(rev_id)
613
reached_stop_revision_id = True
614
revision_id_whitelist.extend(rev.parent_ids)
616
raise ValueError('invalid stop_rule %r' % stop_rule)
618
def _filter_start_non_ancestors(self, rev_iter):
619
# If we started from a dotted revno, we want to consider it as a tip
620
# and don't want to yield revisions that are not part of its
621
# ancestry. Given the order guaranteed by the merge sort, we will see
622
# uninteresting descendants of the first parent of our tip before the
624
first = rev_iter.next()
625
(rev_id, merge_depth, revno, end_of_merge) = first
628
# We start at a mainline revision so by definition, all others
629
# revisions in rev_iter are ancestors
630
for node in rev_iter:
635
pmap = self.repository.get_parent_map([rev_id])
636
parents = pmap.get(rev_id, [])
638
whitelist.update(parents)
640
# If there is no parents, there is nothing of interest left
642
# FIXME: It's hard to test this scenario here as this code is never
643
# called in that case. -- vila 20100322
646
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
648
if rev_id in whitelist:
649
pmap = self.repository.get_parent_map([rev_id])
650
parents = pmap.get(rev_id, [])
651
whitelist.remove(rev_id)
652
whitelist.update(parents)
654
# We've reached the mainline, there is nothing left to
658
# A revision that is not part of the ancestry of our
661
yield (rev_id, merge_depth, revno, end_of_merge)
205
663
def leave_lock_in_place(self):
206
664
"""Tell this branch object not to release the physical lock when this
207
665
object is unlocked.
209
667
If lock_write doesn't return a token, then this method is not supported.
211
669
self.control_files.leave_in_place()
293
741
:param committer: Optional committer to set for commit.
294
742
:param revprops: Optional dictionary of revision properties.
295
743
:param revision_id: Optional revision id.
744
:param lossy: Whether to discard data that can not be natively
745
represented, when pushing to a foreign VCS
299
config = self.get_config()
301
return self.repository.get_commit_builder(self, parents, config,
302
timestamp, timezone, committer, revprops, revision_id)
748
if config_stack is None:
749
config_stack = self.get_config_stack()
751
return self.repository.get_commit_builder(self, parents, config_stack,
752
timestamp, timezone, committer, revprops, revision_id,
304
755
def get_master_branch(self, possible_transports=None):
305
756
"""Return the branch we are bound to.
307
758
:return: Either a Branch, or None
762
@deprecated_method(deprecated_in((2, 5, 0)))
311
763
def get_revision_delta(self, revno):
312
764
"""Return the delta for one revision.
314
766
The delta is relative to its mainline predecessor, or the
315
767
empty tree for revision 1.
317
rh = self.revision_history()
318
if not (1 <= revno <= len(rh)):
770
revid = self.get_rev_id(revno)
771
except errors.NoSuchRevision:
319
772
raise errors.InvalidRevisionNumber(revno)
320
return self.repository.get_revision_delta(rh[revno-1])
773
return self.repository.get_revision_delta(revid)
775
def get_stacked_on_url(self):
776
"""Get the URL this branch is stacked against.
778
:raises NotStacked: If the branch is not stacked.
779
:raises UnstackableBranchFormat: If the branch does not support
782
raise NotImplementedError(self.get_stacked_on_url)
322
784
def print_file(self, file, revision_id):
323
785
"""Print `file` to stdout."""
324
786
raise NotImplementedError(self.print_file)
326
def set_revision_history(self, rev_history):
327
raise NotImplementedError(self.set_revision_history)
789
def set_last_revision_info(self, revno, revision_id):
790
"""Set the last revision of this branch.
792
The caller is responsible for checking that the revno is correct
793
for this revision id.
795
It may be possible to set the branch last revision to an id not
796
present in the repository. However, branches can also be
797
configured to check constraints on history, in which case this may not
800
raise NotImplementedError(self.set_last_revision_info)
803
def generate_revision_history(self, revision_id, last_rev=None,
805
"""See Branch.generate_revision_history"""
806
graph = self.repository.get_graph()
807
(last_revno, last_revid) = self.last_revision_info()
808
known_revision_ids = [
809
(last_revid, last_revno),
810
(_mod_revision.NULL_REVISION, 0),
812
if last_rev is not None:
813
if not graph.is_ancestor(last_rev, revision_id):
814
# our previous tip is not merged into stop_revision
815
raise errors.DivergedBranches(self, other_branch)
816
revno = graph.find_distance_to_null(revision_id, known_revision_ids)
817
self.set_last_revision_info(revno, revision_id)
820
def set_parent(self, url):
821
"""See Branch.set_parent."""
822
# TODO: Maybe delete old location files?
823
# URLs should never be unicode, even on the local fs,
824
# FIXUP this and get_parent in a future branch format bump:
825
# read and rewrite the file. RBC 20060125
827
if isinstance(url, unicode):
829
url = url.encode('ascii')
830
except UnicodeEncodeError:
831
raise errors.InvalidURL(url,
832
"Urls must be 7-bit ascii, "
833
"use bzrlib.urlutils.escape")
834
url = urlutils.relative_url(self.base, url)
835
self._set_parent_location(url)
838
def set_stacked_on_url(self, url):
839
"""Set the URL this branch is stacked against.
841
:raises UnstackableBranchFormat: If the branch does not support
843
:raises UnstackableRepositoryFormat: If the repository does not support
846
if not self._format.supports_stacking():
847
raise errors.UnstackableBranchFormat(self._format, self.user_url)
848
# XXX: Changing from one fallback repository to another does not check
849
# that all the data you need is present in the new fallback.
850
# Possibly it should.
851
self._check_stackable_repo()
854
old_url = self.get_stacked_on_url()
855
except (errors.NotStacked, errors.UnstackableBranchFormat,
856
errors.UnstackableRepositoryFormat):
860
self._activate_fallback_location(url,
861
possible_transports=[self.bzrdir.root_transport])
862
# write this out after the repository is stacked to avoid setting a
863
# stacked config that doesn't work.
864
self._set_config_location('stacked_on_location', url)
867
"""Change a branch to be unstacked, copying data as needed.
869
Don't call this directly, use set_stacked_on_url(None).
871
pb = ui.ui_factory.nested_progress_bar()
873
pb.update(gettext("Unstacking"))
874
# The basic approach here is to fetch the tip of the branch,
875
# including all available ghosts, from the existing stacked
876
# repository into a new repository object without the fallbacks.
878
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
879
# correct for CHKMap repostiories
880
old_repository = self.repository
881
if len(old_repository._fallback_repositories) != 1:
882
raise AssertionError("can't cope with fallback repositories "
883
"of %r (fallbacks: %r)" % (old_repository,
884
old_repository._fallback_repositories))
885
# Open the new repository object.
886
# Repositories don't offer an interface to remove fallback
887
# repositories today; take the conceptually simpler option and just
888
# reopen it. We reopen it starting from the URL so that we
889
# get a separate connection for RemoteRepositories and can
890
# stream from one of them to the other. This does mean doing
891
# separate SSH connection setup, but unstacking is not a
892
# common operation so it's tolerable.
893
new_bzrdir = controldir.ControlDir.open(
894
self.bzrdir.root_transport.base)
895
new_repository = new_bzrdir.find_repository()
896
if new_repository._fallback_repositories:
897
raise AssertionError("didn't expect %r to have "
898
"fallback_repositories"
899
% (self.repository,))
900
# Replace self.repository with the new repository.
901
# Do our best to transfer the lock state (i.e. lock-tokens and
902
# lock count) of self.repository to the new repository.
903
lock_token = old_repository.lock_write().repository_token
904
self.repository = new_repository
905
if isinstance(self, remote.RemoteBranch):
906
# Remote branches can have a second reference to the old
907
# repository that need to be replaced.
908
if self._real_branch is not None:
909
self._real_branch.repository = new_repository
910
self.repository.lock_write(token=lock_token)
911
if lock_token is not None:
912
old_repository.leave_lock_in_place()
913
old_repository.unlock()
914
if lock_token is not None:
915
# XXX: self.repository.leave_lock_in_place() before this
916
# function will not be preserved. Fortunately that doesn't
917
# affect the current default format (2a), and would be a
918
# corner-case anyway.
919
# - Andrew Bennetts, 2010/06/30
920
self.repository.dont_leave_lock_in_place()
924
old_repository.unlock()
925
except errors.LockNotHeld:
928
if old_lock_count == 0:
929
raise AssertionError(
930
'old_repository should have been locked at least once.')
931
for i in range(old_lock_count-1):
932
self.repository.lock_write()
933
# Fetch from the old repository into the new.
934
old_repository.lock_read()
936
# XXX: If you unstack a branch while it has a working tree
937
# with a pending merge, the pending-merged revisions will no
938
# longer be present. You can (probably) revert and remerge.
940
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
941
except errors.TagsNotSupported:
942
tags_to_fetch = set()
943
fetch_spec = vf_search.NotInOtherForRevs(self.repository,
944
old_repository, required_ids=[self.last_revision()],
945
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
946
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
948
old_repository.unlock()
952
def _set_tags_bytes(self, bytes):
953
"""Mirror method for _get_tags_bytes.
955
:seealso: Branch._get_tags_bytes.
957
op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
958
op.add_cleanup(self.lock_write().unlock)
959
return op.run_simple(bytes)
961
def _set_tags_bytes_locked(self, bytes):
962
self._tags_bytes = bytes
963
return self._transport.put_bytes('tags', bytes)
329
965
def _cache_revision_history(self, rev_history):
330
966
"""Set the cached revision history to rev_history.
399
1035
def unbind(self):
400
1036
"""Older format branches cannot bind or unbind."""
401
raise errors.UpgradeRequired(self.base)
403
def set_append_revisions_only(self, enabled):
404
"""Older format branches are never restricted to append-only"""
405
raise errors.UpgradeRequired(self.base)
1037
raise errors.UpgradeRequired(self.user_url)
407
1039
def last_revision(self):
408
1040
"""Return last revision id, or NULL_REVISION."""
409
1041
return self.last_revision_info()[1]
411
1044
def last_revision_info(self):
412
1045
"""Return information about the last revision.
414
:return: A tuple (revno, last_revision_id).
416
rh = self.revision_history()
419
return (revno, rh[-1])
421
return (0, _mod_revision.NULL_REVISION)
423
def missing_revisions(self, other, stop_revision=None):
424
"""Return a list of new revisions that would perfectly fit.
426
If self and other have not diverged, return a list of the revisions
427
present in other, but missing from self.
429
self_history = self.revision_history()
430
self_len = len(self_history)
431
other_history = other.revision_history()
432
other_len = len(other_history)
433
common_index = min(self_len, other_len) -1
434
if common_index >= 0 and \
435
self_history[common_index] != other_history[common_index]:
436
raise errors.DivergedBranches(self, other)
438
if stop_revision is None:
439
stop_revision = other_len
441
if stop_revision > other_len:
442
raise errors.NoSuchRevision(self, stop_revision)
443
return other_history[self_len:stop_revision]
445
def update_revisions(self, other, stop_revision=None):
446
"""Pull in new perfect-fit revisions.
448
:param other: Another Branch to pull from
449
:param stop_revision: Updated until the given revision
452
raise NotImplementedError(self.update_revisions)
1047
:return: A tuple (revno, revision_id).
1049
if self._last_revision_info_cache is None:
1050
self._last_revision_info_cache = self._read_last_revision_info()
1051
return self._last_revision_info_cache
1053
def _read_last_revision_info(self):
1054
raise NotImplementedError(self._read_last_revision_info)
1056
def import_last_revision_info_and_tags(self, source, revno, revid,
1058
"""Set the last revision info, importing from another repo if necessary.
1060
This is used by the bound branch code to upload a revision to
1061
the master branch first before updating the tip of the local branch.
1062
Revisions referenced by source's tags are also transferred.
1064
:param source: Source branch to optionally fetch from
1065
:param revno: Revision number of the new tip
1066
:param revid: Revision id of the new tip
1067
:param lossy: Whether to discard metadata that can not be
1068
natively represented
1069
:return: Tuple with the new revision number and revision id
1070
(should only be different from the arguments when lossy=True)
1072
if not self.repository.has_same_location(source.repository):
1073
self.fetch(source, revid)
1074
self.set_last_revision_info(revno, revid)
1075
return (revno, revid)
454
1077
def revision_id_to_revno(self, revision_id):
455
1078
"""Given a revision id, return its revno"""
456
1079
if _mod_revision.is_null(revision_id):
458
history = self.revision_history()
1081
history = self._revision_history()
460
1083
return history.index(revision_id) + 1
461
1084
except ValueError:
462
1085
raise errors.NoSuchRevision(self, revision_id)
464
1088
def get_rev_id(self, revno, history=None):
465
1089
"""Find the revision id of the specified revno."""
467
1091
return _mod_revision.NULL_REVISION
469
history = self.revision_history()
470
if revno <= 0 or revno > len(history):
1092
last_revno, last_revid = self.last_revision_info()
1093
if revno == last_revno:
1095
if revno <= 0 or revno > last_revno:
471
1096
raise errors.NoSuchRevision(self, revno)
472
return history[revno - 1]
1097
distance_from_last = last_revno - revno
1098
if len(self._partial_revision_history_cache) <= distance_from_last:
1099
self._extend_partial_history(distance_from_last)
1100
return self._partial_revision_history_cache[distance_from_last]
474
1102
def pull(self, source, overwrite=False, stop_revision=None,
475
possible_transports=None):
1103
possible_transports=None, *args, **kwargs):
476
1104
"""Mirror source into this branch.
478
1106
This branch is considered to be 'local', having low latency.
480
1108
:returns: PullResult instance
482
raise NotImplementedError(self.pull)
1110
return InterBranch.get(source, self).pull(overwrite=overwrite,
1111
stop_revision=stop_revision,
1112
possible_transports=possible_transports, *args, **kwargs)
484
def push(self, target, overwrite=False, stop_revision=None):
1114
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
485
1116
"""Mirror this branch into target.
487
1118
This branch is considered to be 'local', having low latency.
489
raise NotImplementedError(self.push)
1120
return InterBranch.get(self, target).push(overwrite, stop_revision,
1121
lossy, *args, **kwargs)
491
1123
def basis_tree(self):
492
1124
"""Return `Tree` object for last revision."""
493
1125
return self.repository.revision_tree(self.last_revision())
495
def rename_one(self, from_rel, to_rel):
498
This can change the directory or the filename or both.
500
raise NotImplementedError(self.rename_one)
502
def move(self, from_paths, to_name):
505
to_name must exist as a versioned directory.
507
If to_name exists and is a directory, the files are moved into
508
it, keeping their old names. If it is a directory,
510
Note that to_name is only the last component of the new name;
511
this doesn't change the directory.
513
This returns a list of (from_path, to_path) pairs for each
516
raise NotImplementedError(self.move)
518
1127
def get_parent(self):
519
1128
"""Return the parent location of the branch.
521
This is the default location for push/pull/missing. The usual
1130
This is the default location for pull/missing. The usual
522
1131
pattern is that the user can override it by specifying a
525
raise NotImplementedError(self.get_parent)
1134
parent = self._get_parent_location()
1137
# This is an old-format absolute path to a local branch
1138
# turn it into a url
1139
if parent.startswith('/'):
1140
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1142
return urlutils.join(self.base[:-1], parent)
1143
except errors.InvalidURLJoin, e:
1144
raise errors.InaccessibleParent(parent, self.user_url)
1146
def _get_parent_location(self):
1147
raise NotImplementedError(self._get_parent_location)
527
1149
def _set_config_location(self, name, url, config=None,
528
1150
make_relative=False):
529
1151
if config is None:
530
config = self.get_config()
1152
config = self.get_config_stack()
533
1155
elif make_relative:
534
1156
url = urlutils.relative_url(self.base, url)
535
config.set_user_option(name, url, warn_masked=True)
1157
config.set(name, url)
537
1159
def _get_config_location(self, name, config=None):
538
1160
if config is None:
539
config = self.get_config()
540
location = config.get_user_option(name)
1161
config = self.get_config_stack()
1162
location = config.get(name)
541
1163
if location == '':
1167
def get_child_submit_format(self):
1168
"""Return the preferred format of submissions to this branch."""
1169
return self.get_config_stack().get('child_submit_format')
545
1171
def get_submit_branch(self):
546
1172
"""Return the submit location of the branch.
614
1257
raise errors.InvalidRevisionNumber(revno)
616
1259
@needs_read_lock
617
def clone(self, to_bzrdir, revision_id=None):
1260
def clone(self, to_bzrdir, revision_id=None, repository_policy=None):
618
1261
"""Clone this branch into to_bzrdir preserving all semantic values.
1263
Most API users will want 'create_clone_on_transport', which creates a
1264
new bzrdir and branch on the fly.
620
1266
revision_id: if not None, the revision history in the new branch will
621
1267
be truncated to end with revision_id.
623
result = self._format.initialize(to_bzrdir)
624
self.copy_content_into(result, revision_id=revision_id)
1269
result = to_bzrdir.create_branch()
1272
if repository_policy is not None:
1273
repository_policy.configure_branch(result)
1274
self.copy_content_into(result, revision_id=revision_id)
627
1279
@needs_read_lock
628
def sprout(self, to_bzrdir, revision_id=None):
1280
def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
629
1282
"""Create a new line of development from the branch, into to_bzrdir.
1284
to_bzrdir controls the branch format.
631
1286
revision_id: if not None, the revision history in the new branch will
632
1287
be truncated to end with revision_id.
634
result = self._format.initialize(to_bzrdir)
635
self.copy_content_into(result, revision_id=revision_id)
636
result.set_parent(self.bzrdir.root_transport.base)
1289
if (repository_policy is not None and
1290
repository_policy.requires_stacking()):
1291
to_bzrdir._format.require_stacking(_skip_repo=True)
1292
result = to_bzrdir.create_branch(repository=repository)
1295
if repository_policy is not None:
1296
repository_policy.configure_branch(result)
1297
self.copy_content_into(result, revision_id=revision_id)
1298
master_url = self.get_bound_location()
1299
if master_url is None:
1300
result.set_parent(self.bzrdir.root_transport.base)
1302
result.set_parent(master_url)
639
1307
def _synchronize_history(self, destination, revision_id):
640
1308
"""Synchronize last revision and revision history between branches.
642
1310
This version is most efficient when the destination is also a
643
BzrBranch5, but works for BzrBranch6 as long as the revision
644
history is the true lefthand parent history, and all of the revisions
645
are in the destination's repository. If not, set_revision_history
1311
BzrBranch6, but works for BzrBranch5, as long as the destination's
1312
repository contains all the lefthand ancestors of the intended
1313
last_revision. If not, set_last_revision_info will fail.
648
1315
:param destination: The branch to copy the history into
649
1316
:param revision_id: The revision-id to truncate history at. May
650
1317
be None to copy complete history.
652
if revision_id == _mod_revision.NULL_REVISION:
654
new_history = self.revision_history()
655
if revision_id is not None and new_history != []:
1319
source_revno, source_revision_id = self.last_revision_info()
1320
if revision_id is None:
1321
revno, revision_id = source_revno, source_revision_id
1323
graph = self.repository.get_graph()
657
new_history = new_history[:new_history.index(revision_id) + 1]
659
rev = self.repository.get_revision(revision_id)
660
new_history = rev.get_history(self.repository)[1:]
661
destination.set_revision_history(new_history)
1325
revno = graph.find_distance_to_null(revision_id,
1326
[(source_revision_id, source_revno)])
1327
except errors.GhostRevisionsHaveNoRevno:
1328
# Default to 1, if we can't find anything else
1330
destination.set_last_revision_info(revno, revision_id)
664
1332
def copy_content_into(self, destination, revision_id=None):
665
1333
"""Copy the content of self into destination.
667
1335
revision_id: if not None, the revision history in the new branch will
668
1336
be truncated to end with revision_id.
670
self._synchronize_history(destination, revision_id)
672
parent = self.get_parent()
673
except errors.InaccessibleParent, e:
674
mutter('parent was not accessible to copy: %s', e)
677
destination.set_parent(parent)
678
self.tags.merge_to(destination.tags)
1338
return InterBranch.get(self, destination).copy_content_into(
1339
revision_id=revision_id)
1341
def update_references(self, target):
1342
if not getattr(self._format, 'supports_reference_locations', False):
1344
reference_dict = self._get_all_reference_info()
1345
if len(reference_dict) == 0:
1347
old_base = self.base
1348
new_base = target.base
1349
target_reference_dict = target._get_all_reference_info()
1350
for file_id, (tree_path, branch_location) in (
1351
reference_dict.items()):
1352
branch_location = urlutils.rebase_url(branch_location,
1354
target_reference_dict.setdefault(
1355
file_id, (tree_path, branch_location))
1356
target._set_all_reference_info(target_reference_dict)
680
1358
@needs_read_lock
1359
def check(self, refs):
682
1360
"""Check consistency of the branch.
684
1362
In particular this checks that revisions given in the revision-history
685
do actually match up in the revision graph, and that they're all
1363
do actually match up in the revision graph, and that they're all
686
1364
present in the repository.
688
1366
Callers will typically also want to check the repository.
1368
:param refs: Calculated refs for this branch as specified by
1369
branch._get_check_refs()
690
1370
:return: A BranchCheckResult.
692
mainline_parent_id = None
1372
result = BranchCheckResult(self)
693
1373
last_revno, last_revision_id = self.last_revision_info()
694
real_rev_history = list(self.repository.iter_reverse_revision_history(
696
real_rev_history.reverse()
697
if len(real_rev_history) != last_revno:
698
raise errors.BzrCheckError('revno does not match len(mainline)'
699
' %s != %s' % (last_revno, len(real_rev_history)))
700
# TODO: We should probably also check that real_rev_history actually
701
# matches self.revision_history()
702
for revision_id in real_rev_history:
704
revision = self.repository.get_revision(revision_id)
705
except errors.NoSuchRevision, e:
706
raise errors.BzrCheckError("mainline revision {%s} not in repository"
708
# In general the first entry on the revision history has no parents.
709
# But it's not illegal for it to have parents listed; this can happen
710
# in imports from Arch when the parents weren't reachable.
711
if mainline_parent_id is not None:
712
if mainline_parent_id not in revision.parent_ids:
713
raise errors.BzrCheckError("previous revision {%s} not listed among "
715
% (mainline_parent_id, revision_id))
716
mainline_parent_id = revision_id
717
return BranchCheckResult(self)
1374
actual_revno = refs[('lefthand-distance', last_revision_id)]
1375
if actual_revno != last_revno:
1376
result.errors.append(errors.BzrCheckError(
1377
'revno does not match len(mainline) %s != %s' % (
1378
last_revno, actual_revno)))
1379
# TODO: We should probably also check that self.revision_history
1380
# matches the repository for older branch formats.
1381
# If looking for the code that cross-checks repository parents against
1382
# the Graph.iter_lefthand_ancestry output, that is now a repository
719
def _get_checkout_format(self):
1386
def _get_checkout_format(self, lightweight=False):
720
1387
"""Return the most suitable metadir for a checkout of this branch.
721
1388
Weaves are used if this branch's repository uses weaves.
723
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
724
from bzrlib.repofmt import weaverepo
725
format = bzrdir.BzrDirMetaFormat1()
726
format.repository_format = weaverepo.RepositoryFormat7()
728
format = self.repository.bzrdir.checkout_metadir()
729
format.set_branch_format(self._format)
1390
format = self.repository.bzrdir.checkout_metadir()
1391
format.set_branch_format(self._format)
1394
def create_clone_on_transport(self, to_transport, revision_id=None,
1395
stacked_on=None, create_prefix=False, use_existing_dir=False,
1397
"""Create a clone of this branch and its bzrdir.
1399
:param to_transport: The transport to clone onto.
1400
:param revision_id: The revision id to use as tip in the new branch.
1401
If None the tip is obtained from this branch.
1402
:param stacked_on: An optional URL to stack the clone on.
1403
:param create_prefix: Create any missing directories leading up to
1405
:param use_existing_dir: Use an existing directory if one exists.
1407
# XXX: Fix the bzrdir API to allow getting the branch back from the
1408
# clone call. Or something. 20090224 RBC/spiv.
1409
# XXX: Should this perhaps clone colocated branches as well,
1410
# rather than just the default branch? 20100319 JRV
1411
if revision_id is None:
1412
revision_id = self.last_revision()
1413
dir_to = self.bzrdir.clone_on_transport(to_transport,
1414
revision_id=revision_id, stacked_on=stacked_on,
1415
create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1417
return dir_to.open_branch()
732
1419
def create_checkout(self, to_location, revision_id=None,
733
1420
lightweight=False, accelerator_tree=None,
734
1421
hardlink=False):
735
1422
"""Create a checkout of a branch.
737
1424
:param to_location: The url to produce the checkout at
738
1425
:param revision_id: The revision to check out
739
1426
:param lightweight: If True, produce a lightweight checkout, otherwise,
740
produce a bound branch (heavyweight checkout)
1427
produce a bound branch (heavyweight checkout)
741
1428
:param accelerator_tree: A tree which can be used for retrieving file
742
1429
contents more quickly than the revision tree, i.e. a workingtree.
743
1430
The revision tree will be used for cases where accelerator_tree's
786
1488
reconciler.reconcile()
787
1489
return reconciler
789
def reference_parent(self, file_id, path):
1491
def reference_parent(self, file_id, path, possible_transports=None):
790
1492
"""Return the parent branch for a tree-reference file_id
791
1494
:param file_id: The file_id of the tree reference
792
1495
:param path: The path of the file_id in the tree
793
1496
:return: A branch associated with the file_id
795
1498
# FIXME should provide multiple branches, based on config
796
return Branch.open(self.bzrdir.root_transport.clone(path).base)
1499
return Branch.open(self.bzrdir.root_transport.clone(path).base,
1500
possible_transports=possible_transports)
798
1502
def supports_tags(self):
799
1503
return self._format.supports_tags()
802
class BranchFormat(object):
1505
def automatic_tag_name(self, revision_id):
1506
"""Try to automatically find the tag name for a revision.
1508
:param revision_id: Revision id of the revision.
1509
:return: A tag name or None if no tag name could be determined.
1511
for hook in Branch.hooks['automatic_tag_name']:
1512
ret = hook(self, revision_id)
1517
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1519
"""Ensure that revision_b is a descendant of revision_a.
1521
This is a helper function for update_revisions.
1523
:raises: DivergedBranches if revision_b has diverged from revision_a.
1524
:returns: True if revision_b is a descendant of revision_a.
1526
relation = self._revision_relations(revision_a, revision_b, graph)
1527
if relation == 'b_descends_from_a':
1529
elif relation == 'diverged':
1530
raise errors.DivergedBranches(self, other_branch)
1531
elif relation == 'a_descends_from_b':
1534
raise AssertionError("invalid relation: %r" % (relation,))
1536
def _revision_relations(self, revision_a, revision_b, graph):
1537
"""Determine the relationship between two revisions.
1539
:returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
1541
heads = graph.heads([revision_a, revision_b])
1542
if heads == set([revision_b]):
1543
return 'b_descends_from_a'
1544
elif heads == set([revision_a, revision_b]):
1545
# These branches have diverged
1547
elif heads == set([revision_a]):
1548
return 'a_descends_from_b'
1550
raise AssertionError("invalid heads: %r" % (heads,))
1552
def heads_to_fetch(self):
1553
"""Return the heads that must and that should be fetched to copy this
1554
branch into another repo.
1556
:returns: a 2-tuple of (must_fetch, if_present_fetch). must_fetch is a
1557
set of heads that must be fetched. if_present_fetch is a set of
1558
heads that must be fetched if present, but no error is necessary if
1559
they are not present.
1561
# For bzr native formats must_fetch is just the tip, and
1562
# if_present_fetch are the tags.
1563
must_fetch = set([self.last_revision()])
1564
if_present_fetch = set()
1565
if self.get_config_stack().get('branch.fetch_tags'):
1567
if_present_fetch = set(self.tags.get_reverse_tag_dict())
1568
except errors.TagsNotSupported:
1570
must_fetch.discard(_mod_revision.NULL_REVISION)
1571
if_present_fetch.discard(_mod_revision.NULL_REVISION)
1572
return must_fetch, if_present_fetch
1575
class BranchFormat(controldir.ControlComponentFormat):
803
1576
"""An encapsulation of the initialization and open routines for a format.
805
1578
Formats provide three things:
806
1579
* An initialization routine,
1580
* a format description
808
1581
* an open routine.
810
Formats are placed in an dict by their format string for reference
811
during branch opening. Its not required that these be instances, they
812
can be classes themselves with class methods - it simply depends on
1583
Formats are placed in an dict by their format string for reference
1584
during branch opening. It's not required that these be instances, they
1585
can be classes themselves with class methods - it simply depends on
813
1586
whether state is needed for a given format or not.
815
1588
Once a format is deprecated, just deprecate the initialize and open
816
methods on the format class. Do not deprecate the object, as the
1589
methods on the format class. Do not deprecate the object, as the
817
1590
object will be created every time regardless.
820
_default_format = None
821
"""The default format used for new branches."""
824
"""The known formats."""
826
1593
def __eq__(self, other):
827
1594
return self.__class__ is other.__class__
829
1596
def __ne__(self, other):
830
1597
return not (self == other)
833
def find_format(klass, a_bzrdir):
834
"""Return the format for the branch object in a_bzrdir."""
836
transport = a_bzrdir.get_branch_transport(None)
837
format_string = transport.get("format").read()
838
return klass._formats[format_string]
839
except errors.NoSuchFile:
840
raise errors.NotBranchError(path=transport.base)
842
raise errors.UnknownFormatError(format=format_string, kind='branch')
845
def get_default_format(klass):
846
"""Return the current default format."""
847
return klass._default_format
849
def get_reference(self, a_bzrdir):
850
"""Get the target reference of the branch in a_bzrdir.
1599
def get_reference(self, controldir, name=None):
1600
"""Get the target reference of the branch in controldir.
852
1602
format probing must have been completed before calling
853
1603
this method - it is assumed that the format of the branch
854
in a_bzrdir is correct.
1604
in controldir is correct.
856
:param a_bzrdir: The bzrdir to get the branch data from.
1606
:param controldir: The controldir to get the branch data from.
1607
:param name: Name of the colocated branch to fetch
857
1608
:return: None if the branch is not a reference branch.
862
def set_reference(self, a_bzrdir, to_branch):
863
"""Set the target reference of the branch in a_bzrdir.
1613
def set_reference(self, controldir, name, to_branch):
1614
"""Set the target reference of the branch in controldir.
865
1616
format probing must have been completed before calling
866
1617
this method - it is assumed that the format of the branch
867
in a_bzrdir is correct.
1618
in controldir is correct.
869
:param a_bzrdir: The bzrdir to set the branch reference for.
1620
:param controldir: The controldir to set the branch reference for.
1621
:param name: Name of colocated branch to set, None for default
870
1622
:param to_branch: branch that the checkout is to reference
872
1624
raise NotImplementedError(self.set_reference)
874
def get_format_string(self):
875
"""Return the ASCII format string that identifies this format."""
876
raise NotImplementedError(self.get_format_string)
878
1626
def get_format_description(self):
879
1627
"""Return the short format description for this format."""
880
1628
raise NotImplementedError(self.get_format_description)
882
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
884
"""Initialize a branch in a bzrdir, with specified files
886
:param a_bzrdir: The bzrdir to initialize the branch in
887
:param utf8_files: The files to create as a list of
888
(filename, content) tuples
889
:param set_format: If True, set the format with
890
self.get_format_string. (BzrBranch4 has its format set
892
:return: a branch in this format
1630
def _run_post_branch_init_hooks(self, controldir, name, branch):
1631
hooks = Branch.hooks['post_branch_init']
1634
params = BranchInitHookParams(self, controldir, name, branch)
1638
def initialize(self, controldir, name=None, repository=None,
1639
append_revisions_only=None):
1640
"""Create a branch of this format in controldir.
1642
:param name: Name of the colocated branch to create.
894
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
895
branch_transport = a_bzrdir.get_branch_transport(self)
897
'metadir': ('lock', lockdir.LockDir),
898
'branch4': ('branch-lock', lockable_files.TransportLock),
900
lock_name, lock_class = lock_map[lock_type]
901
control_files = lockable_files.LockableFiles(branch_transport,
902
lock_name, lock_class)
903
control_files.create_lock()
904
control_files.lock_write()
906
control_files.put_utf8('format', self.get_format_string())
908
for file, content in utf8_files:
909
control_files.put_utf8(file, content)
911
control_files.unlock()
912
return self.open(a_bzrdir, _found=True)
914
def initialize(self, a_bzrdir):
915
"""Create a branch of this format in a_bzrdir."""
916
1644
raise NotImplementedError(self.initialize)
918
1646
def is_supported(self):
919
1647
"""Is this format supported?
921
1649
Supported formats can be initialized and opened.
922
Unsupported formats may not support initialization or committing or
1650
Unsupported formats may not support initialization or committing or
923
1651
some other features depending on the reason for not being supported.
927
def open(self, a_bzrdir, _found=False):
928
"""Return the branch object for a_bzrdir
930
_found is a private parameter, do not use it. It is used to indicate
931
if format probing has already be done.
1655
def make_tags(self, branch):
1656
"""Create a tags object for branch.
1658
This method is on BranchFormat, because BranchFormats are reflected
1659
over the wire via network_name(), whereas full Branch instances require
1660
multiple VFS method calls to operate at all.
1662
The default implementation returns a disabled-tags instance.
1664
Note that it is normal for branch to be a RemoteBranch when using tags
1667
return _mod_tag.DisabledTags(branch)
1669
def network_name(self):
1670
"""A simple byte string uniquely identifying this format for RPC calls.
1672
MetaDir branch formats use their disk format string to identify the
1673
repository over the wire. All in one formats such as bzr < 0.8, and
1674
foreign formats like svn/git and hg should use some marker which is
1675
unique and immutable.
1677
raise NotImplementedError(self.network_name)
1679
def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1680
found_repository=None, possible_transports=None):
1681
"""Return the branch object for controldir.
1683
:param controldir: A ControlDir that contains a branch.
1684
:param name: Name of colocated branch to open
1685
:param _found: a private parameter, do not use it. It is used to
1686
indicate if format probing has already be done.
1687
:param ignore_fallbacks: when set, no fallback branches will be opened
1688
(if there are any). Default is to open fallbacks.
933
1690
raise NotImplementedError(self.open)
936
def register_format(klass, format):
937
klass._formats[format.get_format_string()] = format
940
def set_default_format(klass, format):
941
klass._default_format = format
944
def unregister_format(klass, format):
945
del klass._formats[format.get_format_string()]
1692
def supports_set_append_revisions_only(self):
1693
"""True if this format supports set_append_revisions_only."""
1696
def supports_stacking(self):
1697
"""True if this format records a stacked-on branch."""
1700
def supports_leaving_lock(self):
1701
"""True if this format supports leaving locks in place."""
1702
return False # by default
947
1704
def __str__(self):
948
return self.get_format_string().rstrip()
1705
return self.get_format_description().rstrip()
950
1707
def supports_tags(self):
951
1708
"""True if this format supports tags stored in the branch"""
952
1709
return False # by default
954
# XXX: Probably doesn't really belong here -- mbp 20070212
955
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
957
branch_transport = a_bzrdir.get_branch_transport(self)
958
control_files = lockable_files.LockableFiles(branch_transport,
959
lock_filename, lock_class)
960
control_files.create_lock()
961
control_files.lock_write()
963
for filename, content in utf8_files:
964
control_files.put_utf8(filename, content)
966
control_files.unlock()
1711
def tags_are_versioned(self):
1712
"""Whether the tag container for this branch versions tags."""
1715
def supports_tags_referencing_ghosts(self):
1716
"""True if tags can reference ghost revisions."""
1720
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1721
"""A factory for a BranchFormat object, permitting simple lazy registration.
1723
While none of the built in BranchFormats are lazy registered yet,
1724
bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
1725
use it, and the bzr-loom plugin uses it as well (see
1726
bzrlib.plugins.loom.formats).
1729
def __init__(self, format_string, module_name, member_name):
1730
"""Create a MetaDirBranchFormatFactory.
1732
:param format_string: The format string the format has.
1733
:param module_name: Module to load the format class from.
1734
:param member_name: Attribute name within the module for the format class.
1736
registry._LazyObjectGetter.__init__(self, module_name, member_name)
1737
self._format_string = format_string
1739
def get_format_string(self):
1740
"""See BranchFormat.get_format_string."""
1741
return self._format_string
1744
"""Used for network_format_registry support."""
1745
return self.get_obj()()
969
1748
class BranchHooks(Hooks):
970
1749
"""A dictionary mapping hook name to a list of callables for branch hooks.
972
e.g. ['set_rh'] Is the list of items to be called when the
973
set_revision_history function is invoked.
1751
e.g. ['post_push'] Is the list of items to be called when the
1752
push function is invoked.
976
1755
def __init__(self):
979
1758
These are all empty initially, because by default nothing should get
983
# Introduced in 0.15:
984
# invoked whenever the revision history has been set
985
# with set_revision_history. The api signature is
986
# (branch, revision_history), and the branch will
989
# invoked after a push operation completes.
990
# the api signature is
992
# containing the members
993
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
994
# where local is the local target branch or None, master is the target
995
# master branch, and the rest should be self explanatory. The source
996
# is read locked and the target branches write locked. Source will
997
# be the local low-latency branch.
998
self['post_push'] = []
999
# invoked after a pull operation completes.
1000
# the api signature is
1002
# containing the members
1003
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
1004
# where local is the local branch or None, master is the target
1005
# master branch, and the rest should be self explanatory. The source
1006
# is read locked and the target branches write locked. The local
1007
# branch is the low-latency branch.
1008
self['post_pull'] = []
1009
# invoked before a commit operation takes place.
1010
# the api signature is
1011
# (local, master, old_revno, old_revid, future_revno, future_revid,
1012
# tree_delta, future_tree).
1013
# old_revid is NULL_REVISION for the first commit to a branch
1014
# tree_delta is a TreeDelta object describing changes from the basis
1015
# revision, hooks MUST NOT modify this delta
1016
# future_tree is an in-memory tree obtained from
1017
# CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
1018
self['pre_commit'] = []
1019
# invoked after a commit operation completes.
1020
# the api signature is
1021
# (local, master, old_revno, old_revid, new_revno, new_revid)
1022
# old_revid is NULL_REVISION for the first commit to a branch.
1023
self['post_commit'] = []
1024
# invoked after a uncommit operation completes.
1025
# the api signature is
1026
# (local, master, old_revno, old_revid, new_revno, new_revid) where
1027
# local is the local branch or None, master is the target branch,
1028
# and an empty branch recieves new_revno of 0, new_revid of None.
1029
self['post_uncommit'] = []
1031
# Invoked after the tip of a branch changes.
1032
# the api signature is
1033
# (params) where params is a ChangeBranchTipParams with the members
1034
# (branch, old_revno, new_revno, old_revid, new_revid)
1035
self['post_change_branch_tip'] = []
1761
Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1762
self.add_hook('open',
1763
"Called with the Branch object that has been opened after a "
1764
"branch is opened.", (1, 8))
1765
self.add_hook('post_push',
1766
"Called after a push operation completes. post_push is called "
1767
"with a bzrlib.branch.BranchPushResult object and only runs in the "
1768
"bzr client.", (0, 15))
1769
self.add_hook('post_pull',
1770
"Called after a pull operation completes. post_pull is called "
1771
"with a bzrlib.branch.PullResult object and only runs in the "
1772
"bzr client.", (0, 15))
1773
self.add_hook('pre_commit',
1774
"Called after a commit is calculated but before it is "
1775
"completed. pre_commit is called with (local, master, old_revno, "
1776
"old_revid, future_revno, future_revid, tree_delta, future_tree"
1777
"). old_revid is NULL_REVISION for the first commit to a branch, "
1778
"tree_delta is a TreeDelta object describing changes from the "
1779
"basis revision. hooks MUST NOT modify this delta. "
1780
" future_tree is an in-memory tree obtained from "
1781
"CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1783
self.add_hook('post_commit',
1784
"Called in the bzr client after a commit has completed. "
1785
"post_commit is called with (local, master, old_revno, old_revid, "
1786
"new_revno, new_revid). old_revid is NULL_REVISION for the first "
1787
"commit to a branch.", (0, 15))
1788
self.add_hook('post_uncommit',
1789
"Called in the bzr client after an uncommit completes. "
1790
"post_uncommit is called with (local, master, old_revno, "
1791
"old_revid, new_revno, new_revid) where local is the local branch "
1792
"or None, master is the target branch, and an empty branch "
1793
"receives new_revno of 0, new_revid of None.", (0, 15))
1794
self.add_hook('pre_change_branch_tip',
1795
"Called in bzr client and server before a change to the tip of a "
1796
"branch is made. pre_change_branch_tip is called with a "
1797
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1798
"commit, uncommit will all trigger this hook.", (1, 6))
1799
self.add_hook('post_change_branch_tip',
1800
"Called in bzr client and server after a change to the tip of a "
1801
"branch is made. post_change_branch_tip is called with a "
1802
"bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1803
"commit, uncommit will all trigger this hook.", (1, 4))
1804
self.add_hook('transform_fallback_location',
1805
"Called when a stacked branch is activating its fallback "
1806
"locations. transform_fallback_location is called with (branch, "
1807
"url), and should return a new url. Returning the same url "
1808
"allows it to be used as-is, returning a different one can be "
1809
"used to cause the branch to stack on a closer copy of that "
1810
"fallback_location. Note that the branch cannot have history "
1811
"accessing methods called on it during this hook because the "
1812
"fallback locations have not been activated. When there are "
1813
"multiple hooks installed for transform_fallback_location, "
1814
"all are called with the url returned from the previous hook."
1815
"The order is however undefined.", (1, 9))
1816
self.add_hook('automatic_tag_name',
1817
"Called to determine an automatic tag name for a revision. "
1818
"automatic_tag_name is called with (branch, revision_id) and "
1819
"should return a tag name or None if no tag name could be "
1820
"determined. The first non-None tag name returned will be used.",
1822
self.add_hook('post_branch_init',
1823
"Called after new branch initialization completes. "
1824
"post_branch_init is called with a "
1825
"bzrlib.branch.BranchInitHookParams. "
1826
"Note that init, branch and checkout (both heavyweight and "
1827
"lightweight) will all trigger this hook.", (2, 2))
1828
self.add_hook('post_switch',
1829
"Called after a checkout switches branch. "
1830
"post_switch is called with a "
1831
"bzrlib.branch.SwitchHookParams.", (2, 2))
1038
1835
# install the default hooks into the Branch class.
1068
1865
self.old_revid = old_revid
1069
1866
self.new_revid = new_revid
1072
class BzrBranchFormat4(BranchFormat):
1073
"""Bzr branch format 4.
1076
- a revision-history file.
1077
- a branch-lock lock file [ to be shared with the bzrdir ]
1080
def get_format_description(self):
1081
"""See BranchFormat.get_format_description()."""
1082
return "Branch format 4"
1084
def initialize(self, a_bzrdir):
1085
"""Create a branch of this format in a_bzrdir."""
1086
utf8_files = [('revision-history', ''),
1087
('branch-name', ''),
1089
return self._initialize_helper(a_bzrdir, utf8_files,
1090
lock_type='branch4', set_format=False)
1093
super(BzrBranchFormat4, self).__init__()
1094
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1096
def open(self, a_bzrdir, _found=False):
1097
"""Return the branch object for a_bzrdir
1099
_found is a private parameter, do not use it. It is used to indicate
1100
if format probing has already be done.
1103
# we are being called directly and must probe.
1104
raise NotImplementedError
1105
return BzrBranch(_format=self,
1106
_control_files=a_bzrdir._control_files,
1108
_repository=a_bzrdir.open_repository())
1111
return "Bazaar-NG branch format 4"
1114
class BzrBranchFormat5(BranchFormat):
1115
"""Bzr branch format 5.
1118
- a revision-history file.
1120
- a lock dir guarding the branch itself
1121
- all of this stored in a branch/ subdirectory
1122
- works with shared repositories.
1124
This format is new in bzr 0.8.
1127
def get_format_string(self):
1128
"""See BranchFormat.get_format_string()."""
1129
return "Bazaar-NG branch format 5\n"
1131
def get_format_description(self):
1132
"""See BranchFormat.get_format_description()."""
1133
return "Branch format 5"
1135
def initialize(self, a_bzrdir):
1136
"""Create a branch of this format in a_bzrdir."""
1137
utf8_files = [('revision-history', ''),
1138
('branch-name', ''),
1140
return self._initialize_helper(a_bzrdir, utf8_files)
1143
super(BzrBranchFormat5, self).__init__()
1144
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1146
def open(self, a_bzrdir, _found=False):
1147
"""Return the branch object for a_bzrdir
1149
_found is a private parameter, do not use it. It is used to indicate
1150
if format probing has already be done.
1153
format = BranchFormat.find_format(a_bzrdir)
1868
def __eq__(self, other):
1869
return self.__dict__ == other.__dict__
1872
return "<%s of %s from (%s, %s) to (%s, %s)>" % (
1873
self.__class__.__name__, self.branch,
1874
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1877
class BranchInitHookParams(object):
1878
"""Object holding parameters passed to `*_branch_init` hooks.
1880
There are 4 fields that hooks may wish to access:
1882
:ivar format: the branch format
1883
:ivar bzrdir: the ControlDir where the branch will be/has been initialized
1884
:ivar name: name of colocated branch, if any (or None)
1885
:ivar branch: the branch created
1887
Note that for lightweight checkouts, the bzrdir and format fields refer to
1888
the checkout, hence they are different from the corresponding fields in
1889
branch, which refer to the original branch.
1892
def __init__(self, format, controldir, name, branch):
1893
"""Create a group of BranchInitHook parameters.
1895
:param format: the branch format
1896
:param controldir: the ControlDir where the branch will be/has been
1898
:param name: name of colocated branch, if any (or None)
1899
:param branch: the branch created
1901
Note that for lightweight checkouts, the bzrdir and format fields refer
1902
to the checkout, hence they are different from the corresponding fields
1903
in branch, which refer to the original branch.
1905
self.format = format
1906
self.bzrdir = controldir
1908
self.branch = branch
1910
def __eq__(self, other):
1911
return self.__dict__ == other.__dict__
1914
return "<%s of %s>" % (self.__class__.__name__, self.branch)
1917
class SwitchHookParams(object):
1918
"""Object holding parameters passed to `*_switch` hooks.
1920
There are 4 fields that hooks may wish to access:
1922
:ivar control_dir: ControlDir of the checkout to change
1923
:ivar to_branch: branch that the checkout is to reference
1924
:ivar force: skip the check for local commits in a heavy checkout
1925
:ivar revision_id: revision ID to switch to (or None)
1928
def __init__(self, control_dir, to_branch, force, revision_id):
1929
"""Create a group of SwitchHook parameters.
1931
:param control_dir: ControlDir of the checkout to change
1932
:param to_branch: branch that the checkout is to reference
1933
:param force: skip the check for local commits in a heavy checkout
1934
:param revision_id: revision ID to switch to (or None)
1936
self.control_dir = control_dir
1937
self.to_branch = to_branch
1939
self.revision_id = revision_id
1941
def __eq__(self, other):
1942
return self.__dict__ == other.__dict__
1945
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1946
self.control_dir, self.to_branch,
1950
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1951
"""Base class for branch formats that live in meta directories.
1955
BranchFormat.__init__(self)
1956
bzrdir.BzrFormat.__init__(self)
1959
def find_format(klass, controldir, name=None):
1960
"""Return the format for the branch object in controldir."""
1962
transport = controldir.get_branch_transport(None, name=name)
1963
except errors.NoSuchFile:
1964
raise errors.NotBranchError(path=name, bzrdir=controldir)
1966
format_string = transport.get_bytes("format")
1967
except errors.NoSuchFile:
1968
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1969
return klass._find_format(format_registry, 'branch', format_string)
1971
def _branch_class(self):
1972
"""What class to instantiate on open calls."""
1973
raise NotImplementedError(self._branch_class)
1975
def _get_initial_config(self, append_revisions_only=None):
1976
if append_revisions_only:
1977
return "append_revisions_only = True\n"
1979
# Avoid writing anything if append_revisions_only is disabled,
1980
# as that is the default.
1983
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1985
"""Initialize a branch in a control dir, with specified files
1987
:param a_bzrdir: The bzrdir to initialize the branch in
1988
:param utf8_files: The files to create as a list of
1989
(filename, content) tuples
1990
:param name: Name of colocated branch to create, if any
1991
:return: a branch in this format
1994
name = a_bzrdir._get_selected_branch()
1995
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1996
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1997
control_files = lockable_files.LockableFiles(branch_transport,
1998
'lock', lockdir.LockDir)
1999
control_files.create_lock()
2000
control_files.lock_write()
2002
utf8_files += [('format', self.as_string())]
2003
for (filename, content) in utf8_files:
2004
branch_transport.put_bytes(
2006
mode=a_bzrdir._get_file_mode())
2008
control_files.unlock()
2009
branch = self.open(a_bzrdir, name, _found=True,
2010
found_repository=repository)
2011
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2014
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2015
found_repository=None, possible_transports=None):
2016
"""See BranchFormat.open()."""
2018
name = a_bzrdir._get_selected_branch()
2020
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
1154
2021
if format.__class__ != self.__class__:
1155
2022
raise AssertionError("wrong format %r found for %r" %
1156
2023
(format, self))
2024
transport = a_bzrdir.get_branch_transport(None, name=name)
1158
transport = a_bzrdir.get_branch_transport(None)
1159
2026
control_files = lockable_files.LockableFiles(transport, 'lock',
1160
2027
lockdir.LockDir)
1161
return BzrBranch5(_format=self,
2028
if found_repository is None:
2029
found_repository = a_bzrdir.find_repository()
2030
return self._branch_class()(_format=self,
1162
2031
_control_files=control_files,
1163
2033
a_bzrdir=a_bzrdir,
1164
_repository=a_bzrdir.find_repository())
2034
_repository=found_repository,
2035
ignore_fallbacks=ignore_fallbacks,
2036
possible_transports=possible_transports)
1165
2037
except errors.NoSuchFile:
1166
raise errors.NotBranchError(path=transport.base)
1169
class BzrBranchFormat6(BzrBranchFormat5):
2038
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2041
def _matchingbzrdir(self):
2042
ret = bzrdir.BzrDirMetaFormat1()
2043
ret.set_branch_format(self)
2046
def supports_tags(self):
2049
def supports_leaving_lock(self):
2052
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2054
BranchFormat.check_support_status(self,
2055
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2057
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2058
recommend_upgrade=recommend_upgrade, basedir=basedir)
2061
class BzrBranchFormat6(BranchFormatMetadir):
1170
2062
"""Branch format with last-revision and tags.
1172
2064
Unlike previous formats, this has no explicit revision history. Instead,
1185
2081
"""See BranchFormat.get_format_description()."""
1186
2082
return "Branch format 6"
1188
def initialize(self, a_bzrdir):
1189
"""Create a branch of this format in a_bzrdir."""
1190
utf8_files = [('last-revision', '0 null:\n'),
1191
('branch.conf', ''),
1194
return self._initialize_helper(a_bzrdir, utf8_files)
1196
def open(self, a_bzrdir, _found=False):
1197
"""Return the branch object for a_bzrdir
1199
_found is a private parameter, do not use it. It is used to indicate
1200
if format probing has already be done.
1203
format = BranchFormat.find_format(a_bzrdir)
1204
if format.__class__ != self.__class__:
1205
raise AssertionError("wrong format %r found for %r" %
1207
transport = a_bzrdir.get_branch_transport(None)
1208
control_files = lockable_files.LockableFiles(transport, 'lock',
1210
return BzrBranch6(_format=self,
1211
_control_files=control_files,
1213
_repository=a_bzrdir.find_repository())
1215
def supports_tags(self):
1219
class BranchReferenceFormat(BranchFormat):
2084
def initialize(self, a_bzrdir, name=None, repository=None,
2085
append_revisions_only=None):
2086
"""Create a branch of this format in a_bzrdir."""
2087
utf8_files = [('last-revision', '0 null:\n'),
2089
self._get_initial_config(append_revisions_only)),
2092
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2094
def make_tags(self, branch):
2095
"""See bzrlib.branch.BranchFormat.make_tags()."""
2096
return _mod_tag.BasicTags(branch)
2098
def supports_set_append_revisions_only(self):
2102
class BzrBranchFormat8(BranchFormatMetadir):
2103
"""Metadir format supporting storing locations of subtree branches."""
2105
def _branch_class(self):
2109
def get_format_string(cls):
2110
"""See BranchFormat.get_format_string()."""
2111
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2113
def get_format_description(self):
2114
"""See BranchFormat.get_format_description()."""
2115
return "Branch format 8"
2117
def initialize(self, a_bzrdir, name=None, repository=None,
2118
append_revisions_only=None):
2119
"""Create a branch of this format in a_bzrdir."""
2120
utf8_files = [('last-revision', '0 null:\n'),
2122
self._get_initial_config(append_revisions_only)),
2126
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2128
def make_tags(self, branch):
2129
"""See bzrlib.branch.BranchFormat.make_tags()."""
2130
return _mod_tag.BasicTags(branch)
2132
def supports_set_append_revisions_only(self):
2135
def supports_stacking(self):
2138
supports_reference_locations = True
2141
class BzrBranchFormat7(BranchFormatMetadir):
2142
"""Branch format with last-revision, tags, and a stacked location pointer.
2144
The stacked location pointer is passed down to the repository and requires
2145
a repository format with supports_external_lookups = True.
2147
This format was introduced in bzr 1.6.
2150
def initialize(self, a_bzrdir, name=None, repository=None,
2151
append_revisions_only=None):
2152
"""Create a branch of this format in a_bzrdir."""
2153
utf8_files = [('last-revision', '0 null:\n'),
2155
self._get_initial_config(append_revisions_only)),
2158
return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2160
def _branch_class(self):
2164
def get_format_string(cls):
2165
"""See BranchFormat.get_format_string()."""
2166
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2168
def get_format_description(self):
2169
"""See BranchFormat.get_format_description()."""
2170
return "Branch format 7"
2172
def supports_set_append_revisions_only(self):
2175
def supports_stacking(self):
2178
def make_tags(self, branch):
2179
"""See bzrlib.branch.BranchFormat.make_tags()."""
2180
return _mod_tag.BasicTags(branch)
2182
supports_reference_locations = False
2185
class BranchReferenceFormat(BranchFormatMetadir):
1220
2186
"""Bzr branch reference format.
1222
2188
Branch references are used in implementing checkouts, they
1235
2202
"""See BranchFormat.get_format_description()."""
1236
2203
return "Checkout reference format 1"
1238
def get_reference(self, a_bzrdir):
2205
def get_reference(self, a_bzrdir, name=None):
1239
2206
"""See BranchFormat.get_reference()."""
1240
transport = a_bzrdir.get_branch_transport(None)
1241
return transport.get('location').read()
2207
transport = a_bzrdir.get_branch_transport(None, name=name)
2208
return transport.get_bytes('location')
1243
def set_reference(self, a_bzrdir, to_branch):
2210
def set_reference(self, a_bzrdir, name, to_branch):
1244
2211
"""See BranchFormat.set_reference()."""
1245
transport = a_bzrdir.get_branch_transport(None)
2212
transport = a_bzrdir.get_branch_transport(None, name=name)
1246
2213
location = transport.put_bytes('location', to_branch.base)
1248
def initialize(self, a_bzrdir, target_branch=None):
2215
def initialize(self, a_bzrdir, name=None, target_branch=None,
2216
repository=None, append_revisions_only=None):
1249
2217
"""Create a branch of this format in a_bzrdir."""
1250
2218
if target_branch is None:
1251
2219
# this format does not implement branch itself, thus the implicit
1252
2220
# creation contract must see it as uninitializable
1253
2221
raise errors.UninitializableFormat(self)
1254
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1255
branch_transport = a_bzrdir.get_branch_transport(self)
2222
mutter('creating branch reference in %s', a_bzrdir.user_url)
2223
if a_bzrdir._format.fixed_components:
2224
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2226
name = a_bzrdir._get_selected_branch()
2227
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1256
2228
branch_transport.put_bytes('location',
1257
target_branch.bzrdir.root_transport.base)
1258
branch_transport.put_bytes('format', self.get_format_string())
1260
a_bzrdir, _found=True,
2229
target_branch.user_url)
2230
branch_transport.put_bytes('format', self.as_string())
2231
branch = self.open(a_bzrdir, name, _found=True,
1261
2232
possible_transports=[target_branch.bzrdir.root_transport])
1264
super(BranchReferenceFormat, self).__init__()
1265
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2233
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1267
2236
def _make_reference_clone_function(format, a_branch):
1268
2237
"""Create a clone() routine for a branch dynamically."""
1269
def clone(to_bzrdir, revision_id=None):
2238
def clone(to_bzrdir, revision_id=None,
2239
repository_policy=None):
1270
2240
"""See Branch.clone()."""
1271
return format.initialize(to_bzrdir, a_branch)
2241
return format.initialize(to_bzrdir, target_branch=a_branch)
1272
2242
# cannot obey revision_id limits when cloning a reference ...
1273
2243
# FIXME RBC 20060210 either nuke revision_id for clone, or
1274
2244
# emit some sort of warning/error to the caller ?!
1277
def open(self, a_bzrdir, _found=False, location=None,
1278
possible_transports=None):
2247
def open(self, a_bzrdir, name=None, _found=False, location=None,
2248
possible_transports=None, ignore_fallbacks=False,
2249
found_repository=None):
1279
2250
"""Return the branch that the branch reference in a_bzrdir points at.
1281
_found is a private parameter, do not use it. It is used to indicate
1282
if format probing has already be done.
2252
:param a_bzrdir: A BzrDir that contains a branch.
2253
:param name: Name of colocated branch to open, if any
2254
:param _found: a private parameter, do not use it. It is used to
2255
indicate if format probing has already be done.
2256
:param ignore_fallbacks: when set, no fallback branches will be opened
2257
(if there are any). Default is to open fallbacks.
2258
:param location: The location of the referenced branch. If
2259
unspecified, this will be determined from the branch reference in
2261
:param possible_transports: An optional reusable transports list.
2264
name = a_bzrdir._get_selected_branch()
1285
format = BranchFormat.find_format(a_bzrdir)
2266
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
1286
2267
if format.__class__ != self.__class__:
1287
2268
raise AssertionError("wrong format %r found for %r" %
1288
2269
(format, self))
1289
2270
if location is None:
1290
location = self.get_reference(a_bzrdir)
1291
real_bzrdir = bzrdir.BzrDir.open(
2271
location = self.get_reference(a_bzrdir, name)
2272
real_bzrdir = controldir.ControlDir.open(
1292
2273
location, possible_transports=possible_transports)
1293
result = real_bzrdir.open_branch()
2274
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2275
possible_transports=possible_transports)
1294
2276
# this changes the behaviour of result.clone to create a new reference
1295
2277
# rather than a copy of the content of the branch.
1296
2278
# I did not use a proxy object because that needs much more extensive
2288
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
2289
"""Branch format registry."""
2291
def __init__(self, other_registry=None):
2292
super(BranchFormatRegistry, self).__init__(other_registry)
2293
self._default_format = None
2295
def set_default(self, format):
2296
self._default_format = format
2298
def get_default(self):
2299
return self._default_format
2302
network_format_registry = registry.FormatRegistry()
2303
"""Registry of formats indexed by their network name.
2305
The network name for a branch format is an identifier that can be used when
2306
referring to formats with smart server operations. See
2307
BranchFormat.network_name() for more detail.
2310
format_registry = BranchFormatRegistry(network_format_registry)
1306
2313
# formats which have no format string are not discoverable
1307
2314
# and not independently creatable, so are not registered.
1308
__format5 = BzrBranchFormat5()
1309
2315
__format6 = BzrBranchFormat6()
1310
BranchFormat.register_format(__format5)
1311
BranchFormat.register_format(BranchReferenceFormat())
1312
BranchFormat.register_format(__format6)
1313
BranchFormat.set_default_format(__format6)
1314
_legacy_formats = [BzrBranchFormat4(),
1317
class BzrBranch(Branch):
2316
__format7 = BzrBranchFormat7()
2317
__format8 = BzrBranchFormat8()
2318
format_registry.register_lazy(
2319
"Bazaar-NG branch format 5\n", "bzrlib.branchfmt.fullhistory", "BzrBranchFormat5")
2320
format_registry.register(BranchReferenceFormat())
2321
format_registry.register(__format6)
2322
format_registry.register(__format7)
2323
format_registry.register(__format8)
2324
format_registry.set_default(__format7)
2327
class BranchWriteLockResult(LogicalLockResult):
2328
"""The result of write locking a branch.
2330
:ivar branch_token: The token obtained from the underlying branch lock, or
2332
:ivar unlock: A callable which will unlock the lock.
2335
def __init__(self, unlock, branch_token):
2336
LogicalLockResult.__init__(self, unlock)
2337
self.branch_token = branch_token
2340
return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2344
class BzrBranch(Branch, _RelockDebugMixin):
1318
2345
"""A branch stored in the actual filesystem.
1320
2347
Note that it's "local" in the context of the filesystem; it doesn't
1321
2348
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1322
2349
it's writable, and can be accessed via the normal filesystem API.
2351
:ivar _transport: Transport for file operations on this branch's
2352
control files, typically pointing to the .bzr/branch directory.
2353
:ivar repository: Repository for this branch.
2354
:ivar base: The url of the base directory for this branch; the one
2355
containing the .bzr directory.
2356
:ivar name: Optional colocated branch name as it exists in the control
1325
2360
def __init__(self, _format=None,
1326
_control_files=None, a_bzrdir=None, _repository=None):
2361
_control_files=None, a_bzrdir=None, name=None,
2362
_repository=None, ignore_fallbacks=False,
2363
possible_transports=None):
1327
2364
"""Create new branch object at a particular location."""
1328
Branch.__init__(self)
1329
2365
if a_bzrdir is None:
1330
2366
raise ValueError('a_bzrdir must be supplied')
1332
self.bzrdir = a_bzrdir
1333
# self._transport used to point to the directory containing the
1334
# control directory, but was not used - now it's just the transport
1335
# for the branch control files. mbp 20070212
1336
self._base = self.bzrdir.transport.clone('..').base
2368
raise ValueError('name must be supplied')
2369
self.bzrdir = a_bzrdir
2370
self._user_transport = self.bzrdir.transport.clone('..')
2372
self._user_transport.set_segment_parameter(
2373
"branch", urlutils.escape(name))
2374
self._base = self._user_transport.base
1337
2376
self._format = _format
1338
2377
if _control_files is None:
1339
2378
raise ValueError('BzrBranch _control_files is None')
1340
2379
self.control_files = _control_files
1341
2380
self._transport = _control_files._transport
1342
2381
self.repository = _repository
2382
self.conf_store = None
2383
Branch.__init__(self, possible_transports)
1344
2385
def __str__(self):
1345
return '%s(%r)' % (self.__class__.__name__, self.base)
2386
return '%s(%s)' % (self.__class__.__name__, self.user_url)
1347
2388
__repr__ = __str__
1353
2394
base = property(_get_base, doc="The URL for the root of this branch.")
1355
def abspath(self, name):
1356
"""See Branch.abspath."""
1357
return self.control_files._transport.abspath(name)
2397
def user_transport(self):
2398
return self._user_transport
2400
def _get_config(self):
2401
return _mod_config.TransportConfig(self._transport, 'branch.conf')
2403
def _get_config_store(self):
2404
if self.conf_store is None:
2405
self.conf_store = _mod_config.BranchStore(self)
2406
return self.conf_store
2408
def _uncommitted_branch(self):
2409
"""Return the branch that may contain uncommitted changes."""
2410
master = self.get_master_branch()
2411
if master is not None:
2416
def store_uncommitted(self, creator, message=None):
2417
"""Store uncommitted changes from a ShelfCreator.
2419
:param creator: The ShelfCreator containing uncommitted changes, or
2420
None to delete any stored changes.
2421
:param message: The message to associate with the changes.
2422
:raises: ChangesAlreadyStored if the branch already has changes.
2424
branch = self._uncommitted_branch()
2426
branch._transport.delete('stored-transform')
2428
if branch._transport.has('stored-transform'):
2429
raise errors.ChangesAlreadyStored
2430
transform = StringIO()
2431
creator.write_shelf(transform, message)
2433
branch._transport.put_file('stored-transform', transform)
2435
def get_unshelver(self, tree):
2436
"""Return a shelf.Unshelver for this branch and tree.
2438
:param tree: The tree to use to construct the Unshelver.
2439
:return: an Unshelver or None if no changes are stored.
2441
branch = self._uncommitted_branch()
2443
transform = branch._transport.get('stored-transform')
2444
except errors.NoSuchFile:
2446
return shelf.Unshelver.from_tree_and_shelf(tree, transform)
1359
2448
def is_locked(self):
1360
2449
return self.control_files.is_locked()
1362
2451
def lock_write(self, token=None):
1363
repo_token = self.repository.lock_write()
2452
"""Lock the branch for write operations.
2454
:param token: A token to permit reacquiring a previously held and
2456
:return: A BranchWriteLockResult.
2458
if not self.is_locked():
2459
self._note_lock('w')
2460
self.repository._warn_if_deprecated(self)
2461
self.repository.lock_write()
1365
token = self.control_files.lock_write(token=token)
2466
return BranchWriteLockResult(self.unlock,
2467
self.control_files.lock_write(token=token))
1367
self.repository.unlock()
2470
self.repository.unlock()
1371
2473
def lock_read(self):
1372
self.repository.lock_read()
2474
"""Lock the branch for read operations.
2476
:return: A bzrlib.lock.LogicalLockResult.
2478
if not self.is_locked():
2479
self._note_lock('r')
2480
self.repository._warn_if_deprecated(self)
2481
self.repository.lock_read()
1374
2486
self.control_files.lock_read()
2487
return LogicalLockResult(self.unlock)
1376
self.repository.unlock()
2490
self.repository.unlock()
2493
@only_raises(errors.LockNotHeld, errors.LockBroken)
1379
2494
def unlock(self):
1380
# TODO: test for failed two phase locks. This is known broken.
2495
if self.control_files._lock_count == 1 and self.conf_store is not None:
2496
self.conf_store.save_changes()
1382
2498
self.control_files.unlock()
1384
self.repository.unlock()
1385
if not self.control_files.is_locked():
1386
# we just released the lock
1387
self._clear_cached_state()
2500
if not self.control_files.is_locked():
2501
self.repository.unlock()
2502
# we just released the lock
2503
self._clear_cached_state()
1389
2505
def peek_lock_mode(self):
1390
2506
if self.control_files._lock_count == 0:
1400
2516
"""See Branch.print_file."""
1401
2517
return self.repository.print_file(file, revision_id)
1403
def _write_revision_history(self, history):
1404
"""Factored out of set_revision_history.
1406
This performs the actual writing to disk.
1407
It is intended to be called by BzrBranch5.set_revision_history."""
1408
self.control_files.put_bytes(
1409
'revision-history', '\n'.join(history))
1412
def set_revision_history(self, rev_history):
1413
"""See Branch.set_revision_history."""
1414
if 'evil' in debug.debug_flags:
1415
mutter_callsite(3, "set_revision_history scales with history.")
1416
self._write_revision_history(rev_history)
1417
self._clear_cached_state()
1418
self._cache_revision_history(rev_history)
1419
for hook in Branch.hooks['set_rh']:
1420
hook(self, rev_history)
1422
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
1423
"""Run the post_change_branch_tip hooks."""
1424
hooks = Branch.hooks['post_change_branch_tip']
1427
new_revno, new_revid = self.last_revision_info()
1428
params = ChangeBranchTipParams(
1429
self, old_revno, new_revno, old_revid, new_revid)
1433
2519
@needs_write_lock
1434
2520
def set_last_revision_info(self, revno, revision_id):
1435
"""Set the last revision of this branch.
1437
The caller is responsible for checking that the revno is correct
1438
for this revision id.
1440
It may be possible to set the branch last revision to an id not
1441
present in the repository. However, branches can also be
1442
configured to check constraints on history, in which case this may not
2521
if not revision_id or not isinstance(revision_id, basestring):
2522
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
1445
2523
revision_id = _mod_revision.ensure_null(revision_id)
1446
2524
old_revno, old_revid = self.last_revision_info()
1447
# this old format stores the full history, but this api doesn't
1448
# provide it, so we must generate, and might as well check it's
1450
history = self._lefthand_history(revision_id)
1451
if len(history) != revno:
1452
raise AssertionError('%d != %d' % (len(history), revno))
1453
self.set_revision_history(history)
2525
if self.get_append_revisions_only():
2526
self._check_history_violation(revision_id)
2527
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2528
self._write_last_revision_info(revno, revision_id)
2529
self._clear_cached_state()
2530
self._last_revision_info_cache = revno, revision_id
1454
2531
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1456
def _gen_revision_history(self):
1457
history = self.control_files.get('revision-history').read().split('\n')
1458
if history[-1:] == ['']:
1459
# There shouldn't be a trailing newline, but just in case.
1463
def _lefthand_history(self, revision_id, last_rev=None,
1465
if 'evil' in debug.debug_flags:
1466
mutter_callsite(4, "_lefthand_history scales with history.")
1467
# stop_revision must be a descendant of last_revision
1468
graph = self.repository.get_graph()
1469
if last_rev is not None:
1470
if not graph.is_ancestor(last_rev, revision_id):
1471
# our previous tip is not merged into stop_revision
1472
raise errors.DivergedBranches(self, other_branch)
1473
# make a new revision history from the graph
1474
parents_map = graph.get_parent_map([revision_id])
1475
if revision_id not in parents_map:
1476
raise errors.NoSuchRevision(self, revision_id)
1477
current_rev_id = revision_id
1479
# Do not include ghosts or graph origin in revision_history
1480
while (current_rev_id in parents_map and
1481
len(parents_map[current_rev_id]) > 0):
1482
new_history.append(current_rev_id)
1483
current_rev_id = parents_map[current_rev_id][0]
1484
parents_map = graph.get_parent_map([current_rev_id])
1485
new_history.reverse()
1489
def generate_revision_history(self, revision_id, last_rev=None,
1491
"""Create a new revision history that will finish with revision_id.
1493
:param revision_id: the new tip to use.
1494
:param last_rev: The previous last_revision. If not None, then this
1495
must be a ancestory of revision_id, or DivergedBranches is raised.
1496
:param other_branch: The other branch that DivergedBranches should
1497
raise with respect to.
1499
self.set_revision_history(self._lefthand_history(revision_id,
1500
last_rev, other_branch))
1503
def update_revisions(self, other, stop_revision=None, overwrite=False):
1504
"""See Branch.update_revisions."""
1507
other_last_revno, other_last_revision = other.last_revision_info()
1508
if stop_revision is None:
1509
stop_revision = other_last_revision
1510
if _mod_revision.is_null(stop_revision):
1511
# if there are no commits, we're done.
1513
# whats the current last revision, before we fetch [and change it
1515
last_rev = _mod_revision.ensure_null(self.last_revision())
1516
# we fetch here so that we don't process data twice in the common
1517
# case of having something to pull, and so that the check for
1518
# already merged can operate on the just fetched graph, which will
1519
# be cached in memory.
1520
self.fetch(other, stop_revision)
1521
# Check to see if one is an ancestor of the other
1523
heads = self.repository.get_graph().heads([stop_revision,
1525
if heads == set([last_rev]):
1526
# The current revision is a decendent of the target,
1529
elif heads == set([stop_revision, last_rev]):
1530
# These branches have diverged
1531
raise errors.DivergedBranches(self, other)
1532
elif heads != set([stop_revision]):
1533
raise AssertionError("invalid heads: %r" % heads)
1534
if other_last_revision == stop_revision:
1535
self.set_last_revision_info(other_last_revno,
1536
other_last_revision)
1538
# TODO: jam 2007-11-29 Is there a way to determine the
1539
# revno without searching all of history??
1541
self.generate_revision_history(stop_revision)
1543
self.generate_revision_history(stop_revision,
1544
last_rev=last_rev, other_branch=other)
1548
2533
def basis_tree(self):
1549
2534
"""See Branch.basis_tree."""
1550
2535
return self.repository.revision_tree(self.last_revision())
1553
def pull(self, source, overwrite=False, stop_revision=None,
1554
_hook_master=None, run_hooks=True, possible_transports=None):
1557
:param _hook_master: Private parameter - set the branch to
1558
be supplied as the master to push hooks.
1559
:param run_hooks: Private parameter - if false, this branch
1560
is being called because it's the master of the primary branch,
1561
so it should not run its hooks.
1563
result = PullResult()
1564
result.source_branch = source
1565
result.target_branch = self
1568
result.old_revno, result.old_revid = self.last_revision_info()
1569
self.update_revisions(source, stop_revision, overwrite=overwrite)
1570
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1571
result.new_revno, result.new_revid = self.last_revision_info()
1573
result.master_branch = _hook_master
1574
result.local_branch = self
1576
result.master_branch = self
1577
result.local_branch = None
1579
for hook in Branch.hooks['post_pull']:
1585
2537
def _get_parent_location(self):
1586
2538
_locs = ['parent', 'pull', 'x-pull']
1587
2539
for l in _locs:
1589
return self.control_files.get(l).read().strip('\n')
2541
return self._transport.get_bytes(l).strip('\n')
1590
2542
except errors.NoSuchFile:
1595
def push(self, target, overwrite=False, stop_revision=None,
1596
_override_hook_source_branch=None):
1599
This is the basic concrete implementation of push()
1601
:param _override_hook_source_branch: If specified, run
1602
the hooks passing this Branch as the source, rather than self.
1603
This is for use of RemoteBranch, where push is delegated to the
1604
underlying vfs-based Branch.
1606
# TODO: Public option to disable running hooks - should be trivial but
1610
result = self._push_with_bound_branches(target, overwrite,
1612
_override_hook_source_branch=_override_hook_source_branch)
1617
def _push_with_bound_branches(self, target, overwrite,
1619
_override_hook_source_branch=None):
1620
"""Push from self into target, and into target's master if any.
1622
This is on the base BzrBranch class even though it doesn't support
1623
bound branches because the *target* might be bound.
1626
if _override_hook_source_branch:
1627
result.source_branch = _override_hook_source_branch
1628
for hook in Branch.hooks['post_push']:
1631
bound_location = target.get_bound_location()
1632
if bound_location and target.base != bound_location:
1633
# there is a master branch.
1635
# XXX: Why the second check? Is it even supported for a branch to
1636
# be bound to itself? -- mbp 20070507
1637
master_branch = target.get_master_branch()
1638
master_branch.lock_write()
1640
# push into the master from this branch.
1641
self._basic_push(master_branch, overwrite, stop_revision)
1642
# and push into the target branch from this. Note that we push from
1643
# this branch again, because its considered the highest bandwidth
1645
result = self._basic_push(target, overwrite, stop_revision)
1646
result.master_branch = master_branch
1647
result.local_branch = target
1651
master_branch.unlock()
1654
result = self._basic_push(target, overwrite, stop_revision)
1655
# TODO: Why set master_branch and local_branch if there's no
1656
# binding? Maybe cleaner to just leave them unset? -- mbp
1658
result.master_branch = target
1659
result.local_branch = None
1663
def _basic_push(self, target, overwrite, stop_revision):
1664
"""Basic implementation of push without bound branches or hooks.
1666
Must be called with self read locked and target write locked.
1668
result = PushResult()
1669
result.source_branch = self
1670
result.target_branch = target
1671
#import pdb; pdb.set_trace()
1674
result.old_revno, result.old_revid = target.last_revision_info()
1676
target.update_revisions(self, stop_revision)
1677
except errors.DivergedBranches:
1681
target.set_revision_history(self.revision_history())
1682
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1683
result.new_revno, result.new_revid = target.last_revision_info()
1688
def get_parent(self):
1689
"""See Branch.get_parent."""
1690
parent = self._get_parent_location()
1693
# This is an old-format absolute path to a local branch
1694
# turn it into a url
1695
if parent.startswith('/'):
1696
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1698
return urlutils.join(self.base[:-1], parent)
1699
except errors.InvalidURLJoin, e:
1700
raise errors.InaccessibleParent(parent, self.base)
2546
def get_stacked_on_url(self):
2547
raise errors.UnstackableBranchFormat(self._format, self.user_url)
1702
2549
def set_push_location(self, location):
1703
2550
"""See Branch.set_push_location."""
1705
2552
'push_location', location,
1706
2553
store=_mod_config.STORE_LOCATION_NORECURSE)
1709
def set_parent(self, url):
1710
"""See Branch.set_parent."""
1711
# TODO: Maybe delete old location files?
1712
# URLs should never be unicode, even on the local fs,
1713
# FIXUP this and get_parent in a future branch format bump:
1714
# read and rewrite the file. RBC 20060125
1716
if isinstance(url, unicode):
1718
url = url.encode('ascii')
1719
except UnicodeEncodeError:
1720
raise errors.InvalidURL(url,
1721
"Urls must be 7-bit ascii, "
1722
"use bzrlib.urlutils.escape")
1723
url = urlutils.relative_url(self.base, url)
1724
self._set_parent_location(url)
1726
2555
def _set_parent_location(self, url):
1727
2556
if url is None:
1728
self.control_files._transport.delete('parent')
1730
self.control_files.put_bytes('parent', url + '\n')
1733
class BzrBranch5(BzrBranch):
1734
"""A format 5 branch. This supports new features over plain branches.
1736
It has support for a master_branch which is the data for bound branches.
1744
super(BzrBranch5, self).__init__(_format=_format,
1745
_control_files=_control_files,
1747
_repository=_repository)
1750
def pull(self, source, overwrite=False, stop_revision=None,
1751
run_hooks=True, possible_transports=None):
1752
"""Pull from source into self, updating my master if any.
1754
:param run_hooks: Private parameter - if false, this branch
1755
is being called because it's the master of the primary branch,
1756
so it should not run its hooks.
1758
bound_location = self.get_bound_location()
1759
master_branch = None
1760
if bound_location and source.base != bound_location:
1761
# not pulling from master, so we need to update master.
1762
master_branch = self.get_master_branch(possible_transports)
1763
master_branch.lock_write()
1766
# pull from source into master.
1767
master_branch.pull(source, overwrite, stop_revision,
1769
return super(BzrBranch5, self).pull(source, overwrite,
1770
stop_revision, _hook_master=master_branch,
1771
run_hooks=run_hooks)
1774
master_branch.unlock()
1776
def get_bound_location(self):
1778
return self._transport.get_bytes('bound')[:-1]
1779
except errors.NoSuchFile:
1783
def get_master_branch(self, possible_transports=None):
1784
"""Return the branch we are bound to.
1786
:return: Either a Branch, or None
1788
This could memoise the branch, but if thats done
1789
it must be revalidated on each new lock.
1790
So for now we just don't memoise it.
1791
# RBC 20060304 review this decision.
1793
bound_loc = self.get_bound_location()
1797
return Branch.open(bound_loc,
1798
possible_transports=possible_transports)
1799
except (errors.NotBranchError, errors.ConnectionError), e:
1800
raise errors.BoundBranchConnectionFailure(
1804
def set_bound_location(self, location):
1805
"""Set the target where this branch is bound to.
1807
:param location: URL to the target branch
1810
self.control_files.put_utf8('bound', location+'\n')
1813
self.control_files._transport.delete('bound')
1814
except errors.NoSuchFile:
2557
self._transport.delete('parent')
2559
self._transport.put_bytes('parent', url + '\n',
2560
mode=self.bzrdir._get_file_mode())
2564
"""If bound, unbind"""
2565
return self.set_bound_location(None)
1818
2567
@needs_write_lock
1819
2568
def bind(self, other):
1899
2665
Use set_last_revision_info to perform this safely.
1901
2667
Does not update the revision_history cache.
1902
Intended to be called by set_last_revision_info and
1903
_write_revision_history.
1905
2669
revision_id = _mod_revision.ensure_null(revision_id)
1906
2670
out_string = '%d %s\n' % (revno, revision_id)
1907
self.control_files.put_bytes('last-revision', out_string)
2671
self._transport.put_bytes('last-revision', out_string,
2672
mode=self.bzrdir._get_file_mode())
1909
2674
@needs_write_lock
1910
def set_last_revision_info(self, revno, revision_id):
1911
revision_id = _mod_revision.ensure_null(revision_id)
1912
old_revno, old_revid = self.last_revision_info()
1913
if self._get_append_revisions_only():
1914
self._check_history_violation(revision_id)
1915
self._write_last_revision_info(revno, revision_id)
1916
self._clear_cached_state()
1917
self._last_revision_info_cache = revno, revision_id
1918
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2675
def update_feature_flags(self, updated_flags):
2676
"""Update the feature flags for this branch.
2678
:param updated_flags: Dictionary mapping feature names to necessities
2679
A necessity can be None to indicate the feature should be removed
2681
self._format._update_feature_flags(updated_flags)
2682
self.control_transport.put_bytes('format', self._format.as_string())
2685
class BzrBranch8(BzrBranch):
2686
"""A branch that stores tree-reference locations."""
2688
def _open_hook(self, possible_transports=None):
2689
if self._ignore_fallbacks:
2691
if possible_transports is None:
2692
possible_transports = [self.bzrdir.root_transport]
2694
url = self.get_stacked_on_url()
2695
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2696
errors.UnstackableBranchFormat):
2699
for hook in Branch.hooks['transform_fallback_location']:
2700
url = hook(self, url)
2702
hook_name = Branch.hooks.get_hook_name(hook)
2703
raise AssertionError(
2704
"'transform_fallback_location' hook %s returned "
2705
"None, not a URL." % hook_name)
2706
self._activate_fallback_location(url,
2707
possible_transports=possible_transports)
2709
def __init__(self, *args, **kwargs):
2710
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2711
super(BzrBranch8, self).__init__(*args, **kwargs)
2712
self._last_revision_info_cache = None
2713
self._reference_info = None
2715
def _clear_cached_state(self):
2716
super(BzrBranch8, self)._clear_cached_state()
2717
self._last_revision_info_cache = None
2718
self._reference_info = None
1920
2720
def _check_history_violation(self, revision_id):
1921
last_revision = _mod_revision.ensure_null(self.last_revision())
2721
current_revid = self.last_revision()
2722
last_revision = _mod_revision.ensure_null(current_revid)
1922
2723
if _mod_revision.is_null(last_revision):
1924
if last_revision not in self._lefthand_history(revision_id):
1925
raise errors.AppendRevisionsOnlyViolation(self.base)
2725
graph = self.repository.get_graph()
2726
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2727
if lh_ancestor == current_revid:
2729
raise errors.AppendRevisionsOnlyViolation(self.user_url)
1927
2731
def _gen_revision_history(self):
1928
2732
"""Generate the revision history from last revision
1930
self._extend_partial_history()
2734
last_revno, last_revision = self.last_revision_info()
2735
self._extend_partial_history(stop_index=last_revno-1)
1931
2736
return list(reversed(self._partial_revision_history_cache))
1933
def _extend_partial_history(self, stop_index=None, stop_revision=None):
1934
"""Extend the partial history to include a given index
1936
If a stop_index is supplied, stop when that index has been reached.
1937
If a stop_revision is supplied, stop when that revision is
1938
encountered. Otherwise, stop when the beginning of history is
1941
:param stop_index: The index which should be present. When it is
1942
present, history extension will stop.
1943
:param revision_id: The revision id which should be present. When
1944
it is encountered, history extension will stop.
1946
repo = self.repository
1947
if len(self._partial_revision_history_cache) == 0:
1948
iterator = repo.iter_reverse_revision_history(self.last_revision())
1950
start_revision = self._partial_revision_history_cache[-1]
1951
iterator = repo.iter_reverse_revision_history(start_revision)
1952
#skip the last revision in the list
1953
next_revision = iterator.next()
1954
for revision_id in iterator:
1955
self._partial_revision_history_cache.append(revision_id)
1956
if (stop_index is not None and
1957
len(self._partial_revision_history_cache) > stop_index):
1959
if revision_id == stop_revision:
1962
def _write_revision_history(self, history):
1963
"""Factored out of set_revision_history.
1965
This performs the actual writing to disk, with format-specific checks.
1966
It is intended to be called by BzrBranch5.set_revision_history.
1968
if len(history) == 0:
1969
last_revision = 'null:'
1971
if history != self._lefthand_history(history[-1]):
1972
raise errors.NotLefthandHistory(history)
1973
last_revision = history[-1]
1974
if self._get_append_revisions_only():
1975
self._check_history_violation(last_revision)
1976
self._write_last_revision_info(len(history), last_revision)
1978
2738
@needs_write_lock
1979
2739
def _set_parent_location(self, url):
1980
2740
"""Set the parent branch"""
1985
2745
"""Set the parent branch"""
1986
2746
return self._get_config_location('parent_location')
2749
def _set_all_reference_info(self, info_dict):
2750
"""Replace all reference info stored in a branch.
2752
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2755
writer = rio.RioWriter(s)
2756
for key, (tree_path, branch_location) in info_dict.iteritems():
2757
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2758
branch_location=branch_location)
2759
writer.write_stanza(stanza)
2760
self._transport.put_bytes('references', s.getvalue())
2761
self._reference_info = info_dict
2764
def _get_all_reference_info(self):
2765
"""Return all the reference info stored in a branch.
2767
:return: A dict of {file_id: (tree_path, branch_location)}
2769
if self._reference_info is not None:
2770
return self._reference_info
2771
rio_file = self._transport.get('references')
2773
stanzas = rio.read_stanzas(rio_file)
2774
info_dict = dict((s['file_id'], (s['tree_path'],
2775
s['branch_location'])) for s in stanzas)
2778
self._reference_info = info_dict
2781
def set_reference_info(self, file_id, tree_path, branch_location):
2782
"""Set the branch location to use for a tree reference.
2784
:param file_id: The file-id of the tree reference.
2785
:param tree_path: The path of the tree reference in the tree.
2786
:param branch_location: The location of the branch to retrieve tree
2789
info_dict = self._get_all_reference_info()
2790
info_dict[file_id] = (tree_path, branch_location)
2791
if None in (tree_path, branch_location):
2792
if tree_path is not None:
2793
raise ValueError('tree_path must be None when branch_location'
2795
if branch_location is not None:
2796
raise ValueError('branch_location must be None when tree_path'
2798
del info_dict[file_id]
2799
self._set_all_reference_info(info_dict)
2801
def get_reference_info(self, file_id):
2802
"""Get the tree_path and branch_location for a tree reference.
2804
:return: a tuple of (tree_path, branch_location)
2806
return self._get_all_reference_info().get(file_id, (None, None))
2808
def reference_parent(self, file_id, path, possible_transports=None):
2809
"""Return the parent branch for a tree-reference file_id.
2811
:param file_id: The file_id of the tree reference
2812
:param path: The path of the file_id in the tree
2813
:return: A branch associated with the file_id
2815
branch_location = self.get_reference_info(file_id)[1]
2816
if branch_location is None:
2817
return Branch.reference_parent(self, file_id, path,
2818
possible_transports)
2819
branch_location = urlutils.join(self.user_url, branch_location)
2820
return Branch.open(branch_location,
2821
possible_transports=possible_transports)
1988
2823
def set_push_location(self, location):
1989
2824
"""See Branch.set_push_location."""
1990
2825
self._set_config_location('push_location', location)
1992
2827
def set_bound_location(self, location):
1993
2828
"""See Branch.set_push_location."""
2829
self._master_branch_cache = None
1995
config = self.get_config()
2831
conf = self.get_config_stack()
1996
2832
if location is None:
1997
if config.get_user_option('bound') != 'True':
2833
if not conf.get('bound'):
2000
config.set_user_option('bound', 'False', warn_masked=True)
2836
conf.set('bound', 'False')
2003
2839
self._set_config_location('bound_location', location,
2005
config.set_user_option('bound', 'True', warn_masked=True)
2841
conf.set('bound', 'True')
2008
2844
def _get_bound_location(self, bound):
2009
2845
"""Return the bound location in the config file.
2011
2847
Return None if the bound parameter does not match"""
2012
config = self.get_config()
2013
config_bound = (config.get_user_option('bound') == 'True')
2014
if config_bound != bound:
2848
conf = self.get_config_stack()
2849
if conf.get('bound') != bound:
2016
return self._get_config_location('bound_location', config=config)
2851
return self._get_config_location('bound_location', config=conf)
2018
2853
def get_bound_location(self):
2019
"""See Branch.set_push_location."""
2854
"""See Branch.get_bound_location."""
2020
2855
return self._get_bound_location(True)
2022
2857
def get_old_bound_location(self):
2023
2858
"""See Branch.get_old_bound_location"""
2024
2859
return self._get_bound_location(False)
2026
def set_append_revisions_only(self, enabled):
2031
self.get_config().set_user_option('append_revisions_only', value,
2034
def _get_append_revisions_only(self):
2035
value = self.get_config().get_user_option('append_revisions_only')
2036
return value == 'True'
2038
def _synchronize_history(self, destination, revision_id):
2039
"""Synchronize last revision and revision history between branches.
2041
This version is most efficient when the destination is also a
2042
BzrBranch6, but works for BzrBranch5, as long as the destination's
2043
repository contains all the lefthand ancestors of the intended
2044
last_revision. If not, set_last_revision_info will fail.
2046
:param destination: The branch to copy the history into
2047
:param revision_id: The revision-id to truncate history at. May
2048
be None to copy complete history.
2050
source_revno, source_revision_id = self.last_revision_info()
2051
if revision_id is None:
2052
revno, revision_id = source_revno, source_revision_id
2053
elif source_revision_id == revision_id:
2054
# we know the revno without needing to walk all of history
2055
revno = source_revno
2057
# To figure out the revno for a random revision, we need to build
2058
# the revision history, and count its length.
2059
# We don't care about the order, just how long it is.
2060
# Alternatively, we could start at the current location, and count
2061
# backwards. But there is no guarantee that we will find it since
2062
# it may be a merged revision.
2063
revno = len(list(self.repository.iter_reverse_revision_history(
2065
destination.set_last_revision_info(revno, revision_id)
2067
def _make_tags(self):
2068
return BasicTags(self)
2071
def generate_revision_history(self, revision_id, last_rev=None,
2073
"""See BzrBranch5.generate_revision_history"""
2074
history = self._lefthand_history(revision_id, last_rev, other_branch)
2075
revno = len(history)
2076
self.set_last_revision_info(revno, revision_id)
2861
def get_stacked_on_url(self):
2862
# you can always ask for the URL; but you might not be able to use it
2863
# if the repo can't support stacking.
2864
## self._check_stackable_repo()
2865
# stacked_on_location is only ever defined in branch.conf, so don't
2866
# waste effort reading the whole stack of config files.
2867
conf = _mod_config.BranchOnlyStack(self)
2868
stacked_url = self._get_config_location('stacked_on_location',
2870
if stacked_url is None:
2871
raise errors.NotStacked(self)
2872
return stacked_url.encode('utf-8')
2078
2874
@needs_read_lock
2079
2875
def get_rev_id(self, revno, history=None):
2132
2958
:ivar new_revno: Revision number after pull.
2133
2959
:ivar old_revid: Tip revision id before pull.
2134
2960
:ivar new_revid: Tip revision id after pull.
2135
:ivar source_branch: Source (local) branch object.
2136
:ivar master_branch: Master branch of the target, or None.
2137
:ivar target_branch: Target/destination branch object.
2961
:ivar source_branch: Source (local) branch object. (read locked)
2962
:ivar master_branch: Master branch of the target, or the target if no
2964
:ivar local_branch: target branch if there is a Master, else None
2965
:ivar target_branch: Target/destination branch object. (write locked)
2966
:ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2967
:ivar tag_updates: A dict with new tags, see BasicTags.merge_to
2141
# DEPRECATED: pull used to return the change in revno
2142
return self.new_revno - self.old_revno
2144
2970
def report(self, to_file):
2971
tag_conflicts = getattr(self, "tag_conflicts", None)
2972
tag_updates = getattr(self, "tag_updates", None)
2145
2973
if not is_quiet():
2146
if self.old_revid == self.new_revid:
2147
to_file.write('No revisions to pull.\n')
2974
if self.old_revid != self.new_revid:
2149
2975
to_file.write('Now on revision %d.\n' % self.new_revno)
2977
to_file.write('%d tag(s) updated.\n' % len(tag_updates))
2978
if self.old_revid == self.new_revid and not tag_updates:
2979
if not tag_conflicts:
2980
to_file.write('No revisions or tags to pull.\n')
2982
to_file.write('No revisions to pull.\n')
2150
2983
self._show_tag_conficts(to_file)
2153
class PushResult(_Result):
2986
class BranchPushResult(_Result):
2154
2987
"""Result of a Branch.push operation.
2156
:ivar old_revno: Revision number before push.
2157
:ivar new_revno: Revision number after push.
2158
:ivar old_revid: Tip revision id before push.
2159
:ivar new_revid: Tip revision id after push.
2160
:ivar source_branch: Source branch object.
2161
:ivar master_branch: Master branch of the target, or None.
2162
:ivar target_branch: Target/destination branch object.
2989
:ivar old_revno: Revision number (eg 10) of the target before push.
2990
:ivar new_revno: Revision number (eg 12) of the target after push.
2991
:ivar old_revid: Tip revision id (eg joe@foo.com-1234234-aoeua34) of target
2993
:ivar new_revid: Tip revision id (eg joe@foo.com-5676566-boa234a) of target
2995
:ivar source_branch: Source branch object that the push was from. This is
2996
read locked, and generally is a local (and thus low latency) branch.
2997
:ivar master_branch: If target is a bound branch, the master branch of
2998
target, or target itself. Always write locked.
2999
:ivar target_branch: The direct Branch where data is being sent (write
3001
:ivar local_branch: If the target is a bound branch this will be the
3002
target, otherwise it will be None.
2166
# DEPRECATED: push used to return the change in revno
2167
return self.new_revno - self.old_revno
2169
3005
def report(self, to_file):
2170
"""Write a human-readable description of the result."""
2171
if self.old_revid == self.new_revid:
2172
to_file.write('No new revisions to push.\n')
2174
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
3006
# TODO: This function gets passed a to_file, but then
3007
# ignores it and calls note() instead. This is also
3008
# inconsistent with PullResult(), which writes to stdout.
3009
# -- JRV20110901, bug #838853
3010
tag_conflicts = getattr(self, "tag_conflicts", None)
3011
tag_updates = getattr(self, "tag_updates", None)
3013
if self.old_revid != self.new_revid:
3014
note(gettext('Pushed up to revision %d.') % self.new_revno)
3016
note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3017
if self.old_revid == self.new_revid and not tag_updates:
3018
if not tag_conflicts:
3019
note(gettext('No new revisions or tags to push.'))
3021
note(gettext('No new revisions to push.'))
2175
3022
self._show_tag_conficts(to_file)
2206
3055
# Copy source data into target
2207
3056
new_branch._write_last_revision_info(*branch.last_revision_info())
2208
new_branch.set_parent(branch.get_parent())
2209
new_branch.set_bound_location(branch.get_bound_location())
2210
new_branch.set_push_location(branch.get_push_location())
3057
new_branch.lock_write()
3059
new_branch.set_parent(branch.get_parent())
3060
new_branch.set_bound_location(branch.get_bound_location())
3061
new_branch.set_push_location(branch.get_push_location())
2212
3065
# New branch has no tags by default
2213
3066
new_branch.tags._set_tag_dict({})
2215
3068
# Copying done; now update target format
2216
new_branch.control_files.put_utf8('format',
2217
format.get_format_string())
3069
new_branch._transport.put_bytes('format',
3071
mode=new_branch.bzrdir._get_file_mode())
2219
3073
# Clean up old files
2220
new_branch.control_files._transport.delete('revision-history')
2222
branch.set_parent(None)
2223
except errors.NoSuchFile:
2225
branch.set_bound_location(None)
3074
new_branch._transport.delete('revision-history')
3078
branch.set_parent(None)
3079
except errors.NoSuchFile:
3081
branch.set_bound_location(None)
3086
class Converter6to7(object):
3087
"""Perform an in-place upgrade of format 6 to format 7"""
3089
def convert(self, branch):
3090
format = BzrBranchFormat7()
3091
branch._set_config_location('stacked_on_location', '')
3092
# update target format
3093
branch._transport.put_bytes('format', format.as_string())
3096
class Converter7to8(object):
3097
"""Perform an in-place upgrade of format 7 to format 8"""
3099
def convert(self, branch):
3100
format = BzrBranchFormat8()
3101
branch._transport.put_bytes('references', '')
3102
# update target format
3103
branch._transport.put_bytes('format', format.as_string())
3106
class InterBranch(InterObject):
3107
"""This class represents operations taking place between two branches.
3109
Its instances have methods like pull() and push() and contain
3110
references to the source and target repositories these operations
3111
can be carried out on.
3115
"""The available optimised InterBranch types."""
3118
def _get_branch_formats_to_test(klass):
3119
"""Return an iterable of format tuples for testing.
3121
:return: An iterable of (from_format, to_format) to use when testing
3122
this InterBranch class. Each InterBranch class should define this
3125
raise NotImplementedError(klass._get_branch_formats_to_test)
3128
def pull(self, overwrite=False, stop_revision=None,
3129
possible_transports=None, local=False):
3130
"""Mirror source into target branch.
3132
The target branch is considered to be 'local', having low latency.
3134
:returns: PullResult instance
3136
raise NotImplementedError(self.pull)
3139
def push(self, overwrite=False, stop_revision=None, lossy=False,
3140
_override_hook_source_branch=None):
3141
"""Mirror the source branch into the target branch.
3143
The source branch is considered to be 'local', having low latency.
3145
raise NotImplementedError(self.push)
3148
def copy_content_into(self, revision_id=None):
3149
"""Copy the content of source into target
3151
revision_id: if not None, the revision history in the new branch will
3152
be truncated to end with revision_id.
3154
raise NotImplementedError(self.copy_content_into)
3157
def fetch(self, stop_revision=None, limit=None):
3160
:param stop_revision: Last revision to fetch
3161
:param limit: Optional rough limit of revisions to fetch
3163
raise NotImplementedError(self.fetch)
3166
def _fix_overwrite_type(overwrite):
3167
if isinstance(overwrite, bool):
3169
return ["history", "tags"]
3175
class GenericInterBranch(InterBranch):
3176
"""InterBranch implementation that uses public Branch functions."""
3179
def is_compatible(klass, source, target):
3180
# GenericBranch uses the public API, so always compatible
3184
def _get_branch_formats_to_test(klass):
3185
return [(format_registry.get_default(), format_registry.get_default())]
3188
def unwrap_format(klass, format):
3189
if isinstance(format, remote.RemoteBranchFormat):
3190
format._ensure_real()
3191
return format._custom_format
3195
def copy_content_into(self, revision_id=None):
3196
"""Copy the content of source into target
3198
revision_id: if not None, the revision history in the new branch will
3199
be truncated to end with revision_id.
3201
self.source.update_references(self.target)
3202
self.source._synchronize_history(self.target, revision_id)
3204
parent = self.source.get_parent()
3205
except errors.InaccessibleParent, e:
3206
mutter('parent was not accessible to copy: %s', e)
3209
self.target.set_parent(parent)
3210
if self.source._push_should_merge_tags():
3211
self.source.tags.merge_to(self.target.tags)
3214
def fetch(self, stop_revision=None, limit=None):
3215
if self.target.base == self.source.base:
3217
self.source.lock_read()
3219
fetch_spec_factory = fetch.FetchSpecFactory()
3220
fetch_spec_factory.source_branch = self.source
3221
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3222
fetch_spec_factory.source_repo = self.source.repository
3223
fetch_spec_factory.target_repo = self.target.repository
3224
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3225
fetch_spec_factory.limit = limit
3226
fetch_spec = fetch_spec_factory.make_fetch_spec()
3227
return self.target.repository.fetch(self.source.repository,
3228
fetch_spec=fetch_spec)
3230
self.source.unlock()
3233
def _update_revisions(self, stop_revision=None, overwrite=False,
3235
other_revno, other_last_revision = self.source.last_revision_info()
3236
stop_revno = None # unknown
3237
if stop_revision is None:
3238
stop_revision = other_last_revision
3239
if _mod_revision.is_null(stop_revision):
3240
# if there are no commits, we're done.
3242
stop_revno = other_revno
3244
# what's the current last revision, before we fetch [and change it
3246
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3247
# we fetch here so that we don't process data twice in the common
3248
# case of having something to pull, and so that the check for
3249
# already merged can operate on the just fetched graph, which will
3250
# be cached in memory.
3251
self.fetch(stop_revision=stop_revision)
3252
# Check to see if one is an ancestor of the other
3255
graph = self.target.repository.get_graph()
3256
if self.target._check_if_descendant_or_diverged(
3257
stop_revision, last_rev, graph, self.source):
3258
# stop_revision is a descendant of last_rev, but we aren't
3259
# overwriting, so we're done.
3261
if stop_revno is None:
3263
graph = self.target.repository.get_graph()
3264
this_revno, this_last_revision = \
3265
self.target.last_revision_info()
3266
stop_revno = graph.find_distance_to_null(stop_revision,
3267
[(other_last_revision, other_revno),
3268
(this_last_revision, this_revno)])
3269
self.target.set_last_revision_info(stop_revno, stop_revision)
3272
def pull(self, overwrite=False, stop_revision=None,
3273
possible_transports=None, run_hooks=True,
3274
_override_hook_target=None, local=False):
3275
"""Pull from source into self, updating my master if any.
3277
:param run_hooks: Private parameter - if false, this branch
3278
is being called because it's the master of the primary branch,
3279
so it should not run its hooks.
3281
bound_location = self.target.get_bound_location()
3282
if local and not bound_location:
3283
raise errors.LocalRequiresBoundBranch()
3284
master_branch = None
3285
source_is_master = False
3287
# bound_location comes from a config file, some care has to be
3288
# taken to relate it to source.user_url
3289
normalized = urlutils.normalize_url(bound_location)
3291
relpath = self.source.user_transport.relpath(normalized)
3292
source_is_master = (relpath == '')
3293
except (errors.PathNotChild, errors.InvalidURL):
3294
source_is_master = False
3295
if not local and bound_location and not source_is_master:
3296
# not pulling from master, so we need to update master.
3297
master_branch = self.target.get_master_branch(possible_transports)
3298
master_branch.lock_write()
3301
# pull from source into master.
3302
master_branch.pull(self.source, overwrite, stop_revision,
3304
return self._pull(overwrite,
3305
stop_revision, _hook_master=master_branch,
3306
run_hooks=run_hooks,
3307
_override_hook_target=_override_hook_target,
3308
merge_tags_to_master=not source_is_master)
3311
master_branch.unlock()
3313
def push(self, overwrite=False, stop_revision=None, lossy=False,
3314
_override_hook_source_branch=None):
3315
"""See InterBranch.push.
3317
This is the basic concrete implementation of push()
3319
:param _override_hook_source_branch: If specified, run the hooks
3320
passing this Branch as the source, rather than self. This is for
3321
use of RemoteBranch, where push is delegated to the underlying
3325
raise errors.LossyPushToSameVCS(self.source, self.target)
3326
# TODO: Public option to disable running hooks - should be trivial but
3329
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3330
op.add_cleanup(self.source.lock_read().unlock)
3331
op.add_cleanup(self.target.lock_write().unlock)
3332
return op.run(overwrite, stop_revision,
3333
_override_hook_source_branch=_override_hook_source_branch)
3335
def _basic_push(self, overwrite, stop_revision):
3336
"""Basic implementation of push without bound branches or hooks.
3338
Must be called with source read locked and target write locked.
3340
result = BranchPushResult()
3341
result.source_branch = self.source
3342
result.target_branch = self.target
3343
result.old_revno, result.old_revid = self.target.last_revision_info()
3344
self.source.update_references(self.target)
3345
overwrite = _fix_overwrite_type(overwrite)
3346
if result.old_revid != stop_revision:
3347
# We assume that during 'push' this repository is closer than
3349
graph = self.source.repository.get_graph(self.target.repository)
3350
self._update_revisions(stop_revision,
3351
overwrite=("history" in overwrite),
3353
if self.source._push_should_merge_tags():
3354
result.tag_updates, result.tag_conflicts = (
3355
self.source.tags.merge_to(
3356
self.target.tags, "tags" in overwrite))
3357
result.new_revno, result.new_revid = self.target.last_revision_info()
3360
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3361
_override_hook_source_branch=None):
3362
"""Push from source into target, and into target's master if any.
3365
if _override_hook_source_branch:
3366
result.source_branch = _override_hook_source_branch
3367
for hook in Branch.hooks['post_push']:
3370
bound_location = self.target.get_bound_location()
3371
if bound_location and self.target.base != bound_location:
3372
# there is a master branch.
3374
# XXX: Why the second check? Is it even supported for a branch to
3375
# be bound to itself? -- mbp 20070507
3376
master_branch = self.target.get_master_branch()
3377
master_branch.lock_write()
3378
operation.add_cleanup(master_branch.unlock)
3379
# push into the master from the source branch.
3380
master_inter = InterBranch.get(self.source, master_branch)
3381
master_inter._basic_push(overwrite, stop_revision)
3382
# and push into the target branch from the source. Note that
3383
# we push from the source branch again, because it's considered
3384
# the highest bandwidth repository.
3385
result = self._basic_push(overwrite, stop_revision)
3386
result.master_branch = master_branch
3387
result.local_branch = self.target
3389
master_branch = None
3391
result = self._basic_push(overwrite, stop_revision)
3392
# TODO: Why set master_branch and local_branch if there's no
3393
# binding? Maybe cleaner to just leave them unset? -- mbp
3395
result.master_branch = self.target
3396
result.local_branch = None
3400
def _pull(self, overwrite=False, stop_revision=None,
3401
possible_transports=None, _hook_master=None, run_hooks=True,
3402
_override_hook_target=None, local=False,
3403
merge_tags_to_master=True):
3406
This function is the core worker, used by GenericInterBranch.pull to
3407
avoid duplication when pulling source->master and source->local.
3409
:param _hook_master: Private parameter - set the branch to
3410
be supplied as the master to pull hooks.
3411
:param run_hooks: Private parameter - if false, this branch
3412
is being called because it's the master of the primary branch,
3413
so it should not run its hooks.
3414
is being called because it's the master of the primary branch,
3415
so it should not run its hooks.
3416
:param _override_hook_target: Private parameter - set the branch to be
3417
supplied as the target_branch to pull hooks.
3418
:param local: Only update the local branch, and not the bound branch.
3420
# This type of branch can't be bound.
3422
raise errors.LocalRequiresBoundBranch()
3423
result = PullResult()
3424
result.source_branch = self.source
3425
if _override_hook_target is None:
3426
result.target_branch = self.target
3428
result.target_branch = _override_hook_target
3429
self.source.lock_read()
3431
# We assume that during 'pull' the target repository is closer than
3433
self.source.update_references(self.target)
3434
graph = self.target.repository.get_graph(self.source.repository)
3435
# TODO: Branch formats should have a flag that indicates
3436
# that revno's are expensive, and pull() should honor that flag.
3438
result.old_revno, result.old_revid = \
3439
self.target.last_revision_info()
3440
overwrite = _fix_overwrite_type(overwrite)
3441
self._update_revisions(stop_revision,
3442
overwrite=("history" in overwrite),
3444
# TODO: The old revid should be specified when merging tags,
3445
# so a tags implementation that versions tags can only
3446
# pull in the most recent changes. -- JRV20090506
3447
result.tag_updates, result.tag_conflicts = (
3448
self.source.tags.merge_to(self.target.tags,
3449
"tags" in overwrite,
3450
ignore_master=not merge_tags_to_master))
3451
result.new_revno, result.new_revid = self.target.last_revision_info()
3453
result.master_branch = _hook_master
3454
result.local_branch = result.target_branch
3456
result.master_branch = result.target_branch
3457
result.local_branch = None
3459
for hook in Branch.hooks['post_pull']:
3462
self.source.unlock()
3466
InterBranch.register_optimiser(GenericInterBranch)