57
73
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
60
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
61
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
62
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
65
# TODO: Maybe include checks for common corruption of newlines, etc?
67
# TODO: Some operations like log might retrieve the same revisions
68
# repeatedly to calculate deltas. We could perhaps have a weakref
69
# cache in memory to make this faster. In general anything can be
70
# cached in memory between lock and unlock operations. .. nb thats
71
# what the transaction identity map provides
74
######################################################################
76
class Branch(controldir.ControlComponent):
78
77
"""Branch holding a history of revisions.
81
Base directory/url of the branch.
83
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
85
86
# this is really an instance variable - FIXME move it there
89
def __init__(self, *ignored, **ignored_too):
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):
90
99
self.tags = self._format.make_tags(self)
91
100
self._revision_history_cache = None
92
101
self._revision_id_to_revno_cache = None
93
102
self._partial_revision_id_to_revno_cache = {}
103
self._partial_revision_history_cache = []
104
self._tags_bytes = None
94
105
self._last_revision_info_cache = None
106
self._master_branch_cache = None
95
107
self._merge_sorted_revisions_cache = None
108
self._open_hook(possible_transports)
97
109
hooks = Branch.hooks['open']
98
110
for hook in hooks:
101
def _open_hook(self):
113
def _open_hook(self, possible_transports):
102
114
"""Called by init to allow simpler extension of the base class."""
104
def _activate_fallback_location(self, url, lock_style):
116
def _activate_fallback_location(self, url, possible_transports):
105
117
"""Activate the branch/repository from url as a fallback repository."""
106
repo = self._get_fallback_repository(url)
107
if lock_style == 'write':
109
elif lock_style == 'read':
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)
111
128
self.repository.add_fallback_repository(repo)
113
130
def break_lock(self):
442
544
"""Iterate over an inclusive range of sorted revisions."""
443
545
rev_iter = iter(merge_sorted_revisions)
444
546
if start_revision_id is not None:
445
for rev_id, depth, revno, end_of_merge in rev_iter:
547
for node in rev_iter:
446
549
if rev_id != start_revision_id:
449
552
# The decision to include the start or not
450
553
# depends on the stop_rule if a stop is provided
452
iter([(rev_id, depth, revno, end_of_merge)]),
554
# so pop this node back into the iterator
555
rev_iter = itertools.chain(iter([node]), rev_iter)
455
557
if stop_revision_id is None:
456
for rev_id, depth, revno, end_of_merge in rev_iter:
457
yield rev_id, depth, revno, end_of_merge
559
for node in rev_iter:
561
yield (rev_id, node.merge_depth, node.revno,
458
563
elif stop_rule == 'exclude':
459
for rev_id, depth, revno, end_of_merge in rev_iter:
564
for node in rev_iter:
460
566
if rev_id == stop_revision_id:
462
yield rev_id, depth, revno, end_of_merge
568
yield (rev_id, node.merge_depth, node.revno,
463
570
elif stop_rule == 'include':
464
for rev_id, depth, revno, end_of_merge in rev_iter:
465
yield rev_id, depth, revno, end_of_merge
571
for node in rev_iter:
573
yield (rev_id, node.merge_depth, node.revno,
466
575
if rev_id == stop_revision_id:
577
elif stop_rule == 'with-merges-without-common-ancestry':
578
# We want to exclude all revisions that are already part of the
579
# stop_revision_id ancestry.
580
graph = self.repository.get_graph()
581
ancestors = graph.find_unique_ancestors(start_revision_id,
583
for node in rev_iter:
585
if rev_id not in ancestors:
587
yield (rev_id, node.merge_depth, node.revno,
468
589
elif stop_rule == 'with-merges':
469
590
stop_rev = self.repository.get_revision(stop_revision_id)
470
591
if stop_rev.parent_ids:
471
592
left_parent = stop_rev.parent_ids[0]
473
594
left_parent = _mod_revision.NULL_REVISION
474
for rev_id, depth, revno, end_of_merge in rev_iter:
595
# left_parent is the actual revision we want to stop logging at,
596
# since we want to show the merged revisions after the stop_rev too
597
reached_stop_revision_id = False
598
revision_id_whitelist = []
599
for node in rev_iter:
475
601
if rev_id == left_parent:
602
# reached the left parent after the stop_revision
477
yield rev_id, depth, revno, end_of_merge
604
if (not reached_stop_revision_id or
605
rev_id in revision_id_whitelist):
606
yield (rev_id, node.merge_depth, node.revno,
608
if reached_stop_revision_id or rev_id == stop_revision_id:
609
# only do the merged revs of rev_id from now on
610
rev = self.repository.get_revision(rev_id)
612
reached_stop_revision_id = True
613
revision_id_whitelist.extend(rev.parent_ids)
479
615
raise ValueError('invalid stop_rule %r' % stop_rule)
617
def _filter_start_non_ancestors(self, rev_iter):
618
# If we started from a dotted revno, we want to consider it as a tip
619
# and don't want to yield revisions that are not part of its
620
# ancestry. Given the order guaranteed by the merge sort, we will see
621
# uninteresting descendants of the first parent of our tip before the
623
first = rev_iter.next()
624
(rev_id, merge_depth, revno, end_of_merge) = first
627
# We start at a mainline revision so by definition, all others
628
# revisions in rev_iter are ancestors
629
for node in rev_iter:
634
pmap = self.repository.get_parent_map([rev_id])
635
parents = pmap.get(rev_id, [])
637
whitelist.update(parents)
639
# If there is no parents, there is nothing of interest left
641
# FIXME: It's hard to test this scenario here as this code is never
642
# called in that case. -- vila 20100322
645
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
647
if rev_id in whitelist:
648
pmap = self.repository.get_parent_map([rev_id])
649
parents = pmap.get(rev_id, [])
650
whitelist.remove(rev_id)
651
whitelist.update(parents)
653
# We've reached the mainline, there is nothing left to
657
# A revision that is not part of the ancestry of our
660
yield (rev_id, merge_depth, revno, end_of_merge)
481
662
def leave_lock_in_place(self):
482
663
"""Tell this branch object not to release the physical lock when this
483
664
object is unlocked.
640
854
except (errors.NotStacked, errors.UnstackableBranchFormat,
641
855
errors.UnstackableRepositoryFormat):
644
# XXX: Lock correctness - should unlock our old repo if we were
646
# repositories don't offer an interface to remove fallback
647
# repositories today; take the conceptually simpler option and just
649
self.repository = self.bzrdir.find_repository()
650
self.repository.lock_write()
651
# for every revision reference the branch has, ensure it is pulled
653
source_repository = self._get_fallback_repository(old_url)
654
for revision_id in chain([self.last_revision()],
655
self.tags.get_reverse_tag_dict()):
656
self.repository.fetch(source_repository, revision_id,
659
self._activate_fallback_location(url, 'write')
859
self._activate_fallback_location(url,
860
possible_transports=[self.bzrdir.root_transport])
660
861
# write this out after the repository is stacked to avoid setting a
661
862
# stacked config that doesn't work.
662
863
self._set_config_location('stacked_on_location', url)
866
"""Change a branch to be unstacked, copying data as needed.
868
Don't call this directly, use set_stacked_on_url(None).
870
pb = ui.ui_factory.nested_progress_bar()
872
pb.update(gettext("Unstacking"))
873
# The basic approach here is to fetch the tip of the branch,
874
# including all available ghosts, from the existing stacked
875
# repository into a new repository object without the fallbacks.
877
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
878
# correct for CHKMap repostiories
879
old_repository = self.repository
880
if len(old_repository._fallback_repositories) != 1:
881
raise AssertionError("can't cope with fallback repositories "
882
"of %r (fallbacks: %r)" % (old_repository,
883
old_repository._fallback_repositories))
884
# Open the new repository object.
885
# Repositories don't offer an interface to remove fallback
886
# repositories today; take the conceptually simpler option and just
887
# reopen it. We reopen it starting from the URL so that we
888
# get a separate connection for RemoteRepositories and can
889
# stream from one of them to the other. This does mean doing
890
# separate SSH connection setup, but unstacking is not a
891
# common operation so it's tolerable.
892
new_bzrdir = controldir.ControlDir.open(
893
self.bzrdir.root_transport.base)
894
new_repository = new_bzrdir.find_repository()
895
if new_repository._fallback_repositories:
896
raise AssertionError("didn't expect %r to have "
897
"fallback_repositories"
898
% (self.repository,))
899
# Replace self.repository with the new repository.
900
# Do our best to transfer the lock state (i.e. lock-tokens and
901
# lock count) of self.repository to the new repository.
902
lock_token = old_repository.lock_write().repository_token
903
self.repository = new_repository
904
if isinstance(self, remote.RemoteBranch):
905
# Remote branches can have a second reference to the old
906
# repository that need to be replaced.
907
if self._real_branch is not None:
908
self._real_branch.repository = new_repository
909
self.repository.lock_write(token=lock_token)
910
if lock_token is not None:
911
old_repository.leave_lock_in_place()
912
old_repository.unlock()
913
if lock_token is not None:
914
# XXX: self.repository.leave_lock_in_place() before this
915
# function will not be preserved. Fortunately that doesn't
916
# affect the current default format (2a), and would be a
917
# corner-case anyway.
918
# - Andrew Bennetts, 2010/06/30
919
self.repository.dont_leave_lock_in_place()
923
old_repository.unlock()
924
except errors.LockNotHeld:
927
if old_lock_count == 0:
928
raise AssertionError(
929
'old_repository should have been locked at least once.')
930
for i in range(old_lock_count-1):
931
self.repository.lock_write()
932
# Fetch from the old repository into the new.
933
old_repository.lock_read()
935
# XXX: If you unstack a branch while it has a working tree
936
# with a pending merge, the pending-merged revisions will no
937
# longer be present. You can (probably) revert and remerge.
939
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
940
except errors.TagsNotSupported:
941
tags_to_fetch = set()
942
fetch_spec = vf_search.NotInOtherForRevs(self.repository,
943
old_repository, required_ids=[self.last_revision()],
944
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
945
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
947
old_repository.unlock()
665
951
def _set_tags_bytes(self, bytes):
666
952
"""Mirror method for _get_tags_bytes.
668
954
:seealso: Branch._get_tags_bytes.
670
return _run_with_write_locked_target(self, self._transport.put_bytes,
956
op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
957
op.add_cleanup(self.lock_write().unlock)
958
return op.run_simple(bytes)
960
def _set_tags_bytes_locked(self, bytes):
961
self._tags_bytes = bytes
962
return self._transport.put_bytes('tags', bytes)
673
964
def _cache_revision_history(self, rev_history):
674
965
"""Set the cached revision history to rev_history.
761
1046
:return: A tuple (revno, revision_id).
763
1048
if self._last_revision_info_cache is None:
764
self._last_revision_info_cache = self._last_revision_info()
1049
self._last_revision_info_cache = self._read_last_revision_info()
765
1050
return self._last_revision_info_cache
767
def _last_revision_info(self):
768
rh = self.revision_history()
771
return (revno, rh[-1])
773
return (0, _mod_revision.NULL_REVISION)
775
@deprecated_method(deprecated_in((1, 6, 0)))
776
def missing_revisions(self, other, stop_revision=None):
777
"""Return a list of new revisions that would perfectly fit.
779
If self and other have not diverged, return a list of the revisions
780
present in other, but missing from self.
782
self_history = self.revision_history()
783
self_len = len(self_history)
784
other_history = other.revision_history()
785
other_len = len(other_history)
786
common_index = min(self_len, other_len) -1
787
if common_index >= 0 and \
788
self_history[common_index] != other_history[common_index]:
789
raise errors.DivergedBranches(self, other)
791
if stop_revision is None:
792
stop_revision = other_len
794
if stop_revision > other_len:
795
raise errors.NoSuchRevision(self, stop_revision)
796
return other_history[self_len:stop_revision]
799
def update_revisions(self, other, stop_revision=None, overwrite=False,
801
"""Pull in new perfect-fit revisions.
803
:param other: Another Branch to pull from
804
:param stop_revision: Updated until the given revision
805
:param overwrite: Always set the branch pointer, rather than checking
806
to see if it is a proper descendant.
807
:param graph: A Graph object that can be used to query history
808
information. This can be None.
811
return InterBranch.get(other, self).update_revisions(stop_revision,
814
def import_last_revision_info(self, source_repo, revno, revid):
1052
def _read_last_revision_info(self):
1053
raise NotImplementedError(self._read_last_revision_info)
1055
def import_last_revision_info_and_tags(self, source, revno, revid,
815
1057
"""Set the last revision info, importing from another repo if necessary.
817
1059
This is used by the bound branch code to upload a revision to
818
1060
the master branch first before updating the tip of the local branch.
1061
Revisions referenced by source's tags are also transferred.
820
:param source_repo: Source repository to optionally fetch from
1063
:param source: Source branch to optionally fetch from
821
1064
:param revno: Revision number of the new tip
822
1065
:param revid: Revision id of the new tip
1066
:param lossy: Whether to discard metadata that can not be
1067
natively represented
1068
:return: Tuple with the new revision number and revision id
1069
(should only be different from the arguments when lossy=True)
824
if not self.repository.has_same_location(source_repo):
825
self.repository.fetch(source_repo, revision_id=revid)
1071
if not self.repository.has_same_location(source.repository):
1072
self.fetch(source, revid)
826
1073
self.set_last_revision_info(revno, revid)
1074
return (revno, revid)
828
1076
def revision_id_to_revno(self, revision_id):
829
1077
"""Given a revision id, return its revno"""
830
1078
if _mod_revision.is_null(revision_id):
832
history = self.revision_history()
1080
history = self._revision_history()
834
1082
return history.index(revision_id) + 1
835
1083
except ValueError:
836
1084
raise errors.NoSuchRevision(self, revision_id)
838
1087
def get_rev_id(self, revno, history=None):
839
1088
"""Find the revision id of the specified revno."""
841
1090
return _mod_revision.NULL_REVISION
843
history = self.revision_history()
844
if revno <= 0 or revno > len(history):
1091
last_revno, last_revid = self.last_revision_info()
1092
if revno == last_revno:
1094
if revno <= 0 or revno > last_revno:
845
1095
raise errors.NoSuchRevision(self, revno)
846
return history[revno - 1]
1096
distance_from_last = last_revno - revno
1097
if len(self._partial_revision_history_cache) <= distance_from_last:
1098
self._extend_partial_history(distance_from_last)
1099
return self._partial_revision_history_cache[distance_from_last]
848
1101
def pull(self, source, overwrite=False, stop_revision=None,
849
possible_transports=None, _override_hook_target=None):
1102
possible_transports=None, *args, **kwargs):
850
1103
"""Mirror source into this branch.
852
1105
This branch is considered to be 'local', having low latency.
854
1107
:returns: PullResult instance
856
raise NotImplementedError(self.pull)
1109
return InterBranch.get(source, self).pull(overwrite=overwrite,
1110
stop_revision=stop_revision,
1111
possible_transports=possible_transports, *args, **kwargs)
858
def push(self, target, overwrite=False, stop_revision=None):
1113
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
859
1115
"""Mirror this branch into target.
861
1117
This branch is considered to be 'local', having low latency.
863
raise NotImplementedError(self.push)
1119
return InterBranch.get(self, target).push(overwrite, stop_revision,
1120
lossy, *args, **kwargs)
865
1122
def basis_tree(self):
866
1123
"""Return `Tree` object for last revision."""
1120
1365
Callers will typically also want to check the repository.
1367
:param refs: Calculated refs for this branch as specified by
1368
branch._get_check_refs()
1122
1369
:return: A BranchCheckResult.
1124
mainline_parent_id = None
1371
result = BranchCheckResult(self)
1125
1372
last_revno, last_revision_id = self.last_revision_info()
1126
real_rev_history = list(self.repository.iter_reverse_revision_history(
1128
real_rev_history.reverse()
1129
if len(real_rev_history) != last_revno:
1130
raise errors.BzrCheckError('revno does not match len(mainline)'
1131
' %s != %s' % (last_revno, len(real_rev_history)))
1132
# TODO: We should probably also check that real_rev_history actually
1133
# matches self.revision_history()
1134
for revision_id in real_rev_history:
1136
revision = self.repository.get_revision(revision_id)
1137
except errors.NoSuchRevision, e:
1138
raise errors.BzrCheckError("mainline revision {%s} not in repository"
1140
# In general the first entry on the revision history has no parents.
1141
# But it's not illegal for it to have parents listed; this can happen
1142
# in imports from Arch when the parents weren't reachable.
1143
if mainline_parent_id is not None:
1144
if mainline_parent_id not in revision.parent_ids:
1145
raise errors.BzrCheckError("previous revision {%s} not listed among "
1147
% (mainline_parent_id, revision_id))
1148
mainline_parent_id = revision_id
1149
return BranchCheckResult(self)
1373
actual_revno = refs[('lefthand-distance', last_revision_id)]
1374
if actual_revno != last_revno:
1375
result.errors.append(errors.BzrCheckError(
1376
'revno does not match len(mainline) %s != %s' % (
1377
last_revno, actual_revno)))
1378
# TODO: We should probably also check that self.revision_history
1379
# matches the repository for older branch formats.
1380
# If looking for the code that cross-checks repository parents against
1381
# the Graph.iter_lefthand_ancestry output, that is now a repository
1151
def _get_checkout_format(self):
1385
def _get_checkout_format(self, lightweight=False):
1152
1386
"""Return the most suitable metadir for a checkout of this branch.
1153
1387
Weaves are used if this branch's repository uses weaves.
1155
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1156
from bzrlib.repofmt import weaverepo
1157
format = bzrdir.BzrDirMetaFormat1()
1158
format.repository_format = weaverepo.RepositoryFormat7()
1160
format = self.repository.bzrdir.checkout_metadir()
1161
format.set_branch_format(self._format)
1389
format = self.repository.bzrdir.checkout_metadir()
1390
format.set_branch_format(self._format)
1164
1393
def create_clone_on_transport(self, to_transport, revision_id=None,
1394
stacked_on=None, create_prefix=False, use_existing_dir=False,
1166
1396
"""Create a clone of this branch and its bzrdir.
1168
1398
:param to_transport: The transport to clone onto.
1169
1399
:param revision_id: The revision id to use as tip in the new branch.
1170
1400
If None the tip is obtained from this branch.
1171
1401
:param stacked_on: An optional URL to stack the clone on.
1402
:param create_prefix: Create any missing directories leading up to
1404
:param use_existing_dir: Use an existing directory if one exists.
1173
1406
# XXX: Fix the bzrdir API to allow getting the branch back from the
1174
1407
# clone call. Or something. 20090224 RBC/spiv.
1408
# XXX: Should this perhaps clone colocated branches as well,
1409
# rather than just the default branch? 20100319 JRV
1410
if revision_id is None:
1411
revision_id = self.last_revision()
1175
1412
dir_to = self.bzrdir.clone_on_transport(to_transport,
1176
revision_id=revision_id, stacked_on=stacked_on)
1413
revision_id=revision_id, stacked_on=stacked_on,
1414
create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1177
1416
return dir_to.open_branch()
1179
1418
def create_checkout(self, to_location, revision_id=None,
1300
1589
object will be created every time regardless.
1303
_default_format = None
1304
"""The default format used for new branches."""
1307
"""The known formats."""
1309
1592
def __eq__(self, other):
1310
1593
return self.__class__ is other.__class__
1312
1595
def __ne__(self, other):
1313
1596
return not (self == other)
1316
def find_format(klass, a_bzrdir):
1317
"""Return the format for the branch object in a_bzrdir."""
1319
transport = a_bzrdir.get_branch_transport(None)
1320
format_string = transport.get("format").read()
1321
return klass._formats[format_string]
1322
except errors.NoSuchFile:
1323
raise errors.NotBranchError(path=transport.base)
1325
raise errors.UnknownFormatError(format=format_string, kind='branch')
1328
def get_default_format(klass):
1329
"""Return the current default format."""
1330
return klass._default_format
1332
def get_reference(self, a_bzrdir):
1333
"""Get the target reference of the branch in a_bzrdir.
1598
def get_reference(self, controldir, name=None):
1599
"""Get the target reference of the branch in controldir.
1335
1601
format probing must have been completed before calling
1336
1602
this method - it is assumed that the format of the branch
1337
in a_bzrdir is correct.
1603
in controldir is correct.
1339
:param a_bzrdir: The bzrdir to get the branch data from.
1605
:param controldir: The controldir to get the branch data from.
1606
:param name: Name of the colocated branch to fetch
1340
1607
:return: None if the branch is not a reference branch.
1345
def set_reference(self, a_bzrdir, to_branch):
1346
"""Set the target reference of the branch in a_bzrdir.
1612
def set_reference(self, controldir, name, to_branch):
1613
"""Set the target reference of the branch in controldir.
1348
1615
format probing must have been completed before calling
1349
1616
this method - it is assumed that the format of the branch
1350
in a_bzrdir is correct.
1617
in controldir is correct.
1352
:param a_bzrdir: The bzrdir to set the branch reference for.
1619
:param controldir: The controldir to set the branch reference for.
1620
:param name: Name of colocated branch to set, None for default
1353
1621
:param to_branch: branch that the checkout is to reference
1355
1623
raise NotImplementedError(self.set_reference)
1357
def get_format_string(self):
1358
"""Return the ASCII format string that identifies this format."""
1359
raise NotImplementedError(self.get_format_string)
1361
1625
def get_format_description(self):
1362
1626
"""Return the short format description for this format."""
1363
1627
raise NotImplementedError(self.get_format_description)
1365
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1367
"""Initialize a branch in a bzrdir, with specified files
1369
:param a_bzrdir: The bzrdir to initialize the branch in
1370
:param utf8_files: The files to create as a list of
1371
(filename, content) tuples
1372
:param set_format: If True, set the format with
1373
self.get_format_string. (BzrBranch4 has its format set
1375
:return: a branch in this format
1629
def _run_post_branch_init_hooks(self, controldir, name, branch):
1630
hooks = Branch.hooks['post_branch_init']
1633
params = BranchInitHookParams(self, controldir, name, branch)
1637
def initialize(self, controldir, name=None, repository=None,
1638
append_revisions_only=None):
1639
"""Create a branch of this format in controldir.
1641
:param name: Name of the colocated branch to create.
1377
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1378
branch_transport = a_bzrdir.get_branch_transport(self)
1380
'metadir': ('lock', lockdir.LockDir),
1381
'branch4': ('branch-lock', lockable_files.TransportLock),
1383
lock_name, lock_class = lock_map[lock_type]
1384
control_files = lockable_files.LockableFiles(branch_transport,
1385
lock_name, lock_class)
1386
control_files.create_lock()
1387
control_files.lock_write()
1389
utf8_files += [('format', self.get_format_string())]
1391
for (filename, content) in utf8_files:
1392
branch_transport.put_bytes(
1394
mode=a_bzrdir._get_file_mode())
1396
control_files.unlock()
1397
return self.open(a_bzrdir, _found=True)
1399
def initialize(self, a_bzrdir):
1400
"""Create a branch of this format in a_bzrdir."""
1401
1643
raise NotImplementedError(self.initialize)
1403
1645
def is_supported(self):
1590
1873
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1593
class BzrBranchFormat4(BranchFormat):
1594
"""Bzr branch format 4.
1597
- a revision-history file.
1598
- a branch-lock lock file [ to be shared with the bzrdir ]
1601
def get_format_description(self):
1602
"""See BranchFormat.get_format_description()."""
1603
return "Branch format 4"
1605
def initialize(self, a_bzrdir):
1606
"""Create a branch of this format in a_bzrdir."""
1607
utf8_files = [('revision-history', ''),
1608
('branch-name', ''),
1610
return self._initialize_helper(a_bzrdir, utf8_files,
1611
lock_type='branch4', set_format=False)
1876
class BranchInitHookParams(object):
1877
"""Object holding parameters passed to `*_branch_init` hooks.
1879
There are 4 fields that hooks may wish to access:
1881
:ivar format: the branch format
1882
:ivar bzrdir: the ControlDir where the branch will be/has been initialized
1883
:ivar name: name of colocated branch, if any (or None)
1884
:ivar branch: the branch created
1886
Note that for lightweight checkouts, the bzrdir and format fields refer to
1887
the checkout, hence they are different from the corresponding fields in
1888
branch, which refer to the original branch.
1891
def __init__(self, format, controldir, name, branch):
1892
"""Create a group of BranchInitHook parameters.
1894
:param format: the branch format
1895
:param controldir: the ControlDir where the branch will be/has been
1897
:param name: name of colocated branch, if any (or None)
1898
:param branch: the branch created
1900
Note that for lightweight checkouts, the bzrdir and format fields refer
1901
to the checkout, hence they are different from the corresponding fields
1902
in branch, which refer to the original branch.
1904
self.format = format
1905
self.bzrdir = controldir
1907
self.branch = branch
1909
def __eq__(self, other):
1910
return self.__dict__ == other.__dict__
1913
return "<%s of %s>" % (self.__class__.__name__, self.branch)
1916
class SwitchHookParams(object):
1917
"""Object holding parameters passed to `*_switch` hooks.
1919
There are 4 fields that hooks may wish to access:
1921
:ivar control_dir: ControlDir of the checkout to change
1922
:ivar to_branch: branch that the checkout is to reference
1923
:ivar force: skip the check for local commits in a heavy checkout
1924
:ivar revision_id: revision ID to switch to (or None)
1927
def __init__(self, control_dir, to_branch, force, revision_id):
1928
"""Create a group of SwitchHook parameters.
1930
:param control_dir: ControlDir of the checkout to change
1931
:param to_branch: branch that the checkout is to reference
1932
:param force: skip the check for local commits in a heavy checkout
1933
:param revision_id: revision ID to switch to (or None)
1935
self.control_dir = control_dir
1936
self.to_branch = to_branch
1938
self.revision_id = revision_id
1940
def __eq__(self, other):
1941
return self.__dict__ == other.__dict__
1944
return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1945
self.control_dir, self.to_branch,
1949
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1950
"""Base class for branch formats that live in meta directories.
1613
1953
def __init__(self):
1614
super(BzrBranchFormat4, self).__init__()
1615
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1617
def network_name(self):
1618
"""The network name for this format is the control dirs disk label."""
1619
return self._matchingbzrdir.get_format_string()
1621
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1622
"""See BranchFormat.open()."""
1624
# we are being called directly and must probe.
1625
raise NotImplementedError
1626
return BzrBranch(_format=self,
1627
_control_files=a_bzrdir._control_files,
1629
_repository=a_bzrdir.open_repository())
1632
return "Bazaar-NG branch format 4"
1635
class BranchFormatMetadir(BranchFormat):
1636
"""Common logic for meta-dir based branch formats."""
1954
BranchFormat.__init__(self)
1955
bzrdir.BzrFormat.__init__(self)
1958
def find_format(klass, controldir, name=None):
1959
"""Return the format for the branch object in controldir."""
1961
transport = controldir.get_branch_transport(None, name=name)
1962
except errors.NoSuchFile:
1963
raise errors.NotBranchError(path=name, bzrdir=controldir)
1965
format_string = transport.get_bytes("format")
1966
except errors.NoSuchFile:
1967
raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1968
return klass._find_format(format_registry, 'branch', format_string)
1638
1970
def _branch_class(self):
1639
1971
"""What class to instantiate on open calls."""
1640
1972
raise NotImplementedError(self._branch_class)
1642
def network_name(self):
1643
"""A simple byte string uniquely identifying this format for RPC calls.
1645
Metadir branch formats use their format string.
1974
def _get_initial_config(self, append_revisions_only=None):
1975
if append_revisions_only:
1976
return "append_revisions_only = True\n"
1978
# Avoid writing anything if append_revisions_only is disabled,
1979
# as that is the default.
1982
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1984
"""Initialize a branch in a control dir, with specified files
1986
:param a_bzrdir: The bzrdir to initialize the branch in
1987
:param utf8_files: The files to create as a list of
1988
(filename, content) tuples
1989
:param name: Name of colocated branch to create, if any
1990
:return: a branch in this format
1647
return self.get_format_string()
1993
name = a_bzrdir._get_selected_branch()
1994
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1995
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1996
control_files = lockable_files.LockableFiles(branch_transport,
1997
'lock', lockdir.LockDir)
1998
control_files.create_lock()
1999
control_files.lock_write()
2001
utf8_files += [('format', self.as_string())]
2002
for (filename, content) in utf8_files:
2003
branch_transport.put_bytes(
2005
mode=a_bzrdir._get_file_mode())
2007
control_files.unlock()
2008
branch = self.open(a_bzrdir, name, _found=True,
2009
found_repository=repository)
2010
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1649
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
2013
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2014
found_repository=None, possible_transports=None):
1650
2015
"""See BranchFormat.open()."""
2017
name = a_bzrdir._get_selected_branch()
1652
format = BranchFormat.find_format(a_bzrdir)
2019
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
1653
2020
if format.__class__ != self.__class__:
1654
2021
raise AssertionError("wrong format %r found for %r" %
1655
2022
(format, self))
2023
transport = a_bzrdir.get_branch_transport(None, name=name)
1657
transport = a_bzrdir.get_branch_transport(None)
1658
2025
control_files = lockable_files.LockableFiles(transport, 'lock',
1659
2026
lockdir.LockDir)
2027
if found_repository is None:
2028
found_repository = a_bzrdir.find_repository()
1660
2029
return self._branch_class()(_format=self,
1661
2030
_control_files=control_files,
1662
2032
a_bzrdir=a_bzrdir,
1663
_repository=a_bzrdir.find_repository(),
1664
ignore_fallbacks=ignore_fallbacks)
2033
_repository=found_repository,
2034
ignore_fallbacks=ignore_fallbacks,
2035
possible_transports=possible_transports)
1665
2036
except errors.NoSuchFile:
1666
raise errors.NotBranchError(path=transport.base)
1669
super(BranchFormatMetadir, self).__init__()
1670
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1671
self._matchingbzrdir.set_branch_format(self)
1673
def supports_tags(self):
1677
class BzrBranchFormat5(BranchFormatMetadir):
1678
"""Bzr branch format 5.
1681
- a revision-history file.
1683
- a lock dir guarding the branch itself
1684
- all of this stored in a branch/ subdirectory
1685
- works with shared repositories.
1687
This format is new in bzr 0.8.
1690
def _branch_class(self):
1693
def get_format_string(self):
1694
"""See BranchFormat.get_format_string()."""
1695
return "Bazaar-NG branch format 5\n"
1697
def get_format_description(self):
1698
"""See BranchFormat.get_format_description()."""
1699
return "Branch format 5"
1701
def initialize(self, a_bzrdir):
1702
"""Create a branch of this format in a_bzrdir."""
1703
utf8_files = [('revision-history', ''),
1704
('branch-name', ''),
1706
return self._initialize_helper(a_bzrdir, utf8_files)
1708
def supports_tags(self):
2037
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2040
def _matchingbzrdir(self):
2041
ret = bzrdir.BzrDirMetaFormat1()
2042
ret.set_branch_format(self)
2045
def supports_tags(self):
2048
def supports_leaving_lock(self):
2051
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2053
BranchFormat.check_support_status(self,
2054
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2056
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2057
recommend_upgrade=recommend_upgrade, basedir=basedir)
1712
2060
class BzrBranchFormat6(BranchFormatMetadir):
1833
2201
"""See BranchFormat.get_format_description()."""
1834
2202
return "Checkout reference format 1"
1836
def get_reference(self, a_bzrdir):
2204
def get_reference(self, a_bzrdir, name=None):
1837
2205
"""See BranchFormat.get_reference()."""
1838
transport = a_bzrdir.get_branch_transport(None)
1839
return transport.get('location').read()
2206
transport = a_bzrdir.get_branch_transport(None, name=name)
2207
return transport.get_bytes('location')
1841
def set_reference(self, a_bzrdir, to_branch):
2209
def set_reference(self, a_bzrdir, name, to_branch):
1842
2210
"""See BranchFormat.set_reference()."""
1843
transport = a_bzrdir.get_branch_transport(None)
2211
transport = a_bzrdir.get_branch_transport(None, name=name)
1844
2212
location = transport.put_bytes('location', to_branch.base)
1846
def initialize(self, a_bzrdir, target_branch=None):
2214
def initialize(self, a_bzrdir, name=None, target_branch=None,
2215
repository=None, append_revisions_only=None):
1847
2216
"""Create a branch of this format in a_bzrdir."""
1848
2217
if target_branch is None:
1849
2218
# this format does not implement branch itself, thus the implicit
1850
2219
# creation contract must see it as uninitializable
1851
2220
raise errors.UninitializableFormat(self)
1852
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1853
branch_transport = a_bzrdir.get_branch_transport(self)
2221
mutter('creating branch reference in %s', a_bzrdir.user_url)
2222
if a_bzrdir._format.fixed_components:
2223
raise errors.IncompatibleFormat(self, a_bzrdir._format)
2225
name = a_bzrdir._get_selected_branch()
2226
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1854
2227
branch_transport.put_bytes('location',
1855
target_branch.bzrdir.root_transport.base)
1856
branch_transport.put_bytes('format', self.get_format_string())
1858
a_bzrdir, _found=True,
2228
target_branch.user_url)
2229
branch_transport.put_bytes('format', self.as_string())
2230
branch = self.open(a_bzrdir, name, _found=True,
1859
2231
possible_transports=[target_branch.bzrdir.root_transport])
1862
super(BranchReferenceFormat, self).__init__()
1863
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1864
self._matchingbzrdir.set_branch_format(self)
2232
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1866
2235
def _make_reference_clone_function(format, a_branch):
1867
2236
"""Create a clone() routine for a branch dynamically."""
1868
2237
def clone(to_bzrdir, revision_id=None,
1869
2238
repository_policy=None):
1870
2239
"""See Branch.clone()."""
1871
return format.initialize(to_bzrdir, a_branch)
2240
return format.initialize(to_bzrdir, target_branch=a_branch)
1872
2241
# cannot obey revision_id limits when cloning a reference ...
1873
2242
# FIXME RBC 20060210 either nuke revision_id for clone, or
1874
2243
# emit some sort of warning/error to the caller ?!
1877
def open(self, a_bzrdir, _found=False, location=None,
1878
possible_transports=None, ignore_fallbacks=False):
2246
def open(self, a_bzrdir, name=None, _found=False, location=None,
2247
possible_transports=None, ignore_fallbacks=False,
2248
found_repository=None):
1879
2249
"""Return the branch that the branch reference in a_bzrdir points at.
1881
2251
:param a_bzrdir: A BzrDir that contains a branch.
2252
:param name: Name of colocated branch to open, if any
1882
2253
:param _found: a private parameter, do not use it. It is used to
1883
2254
indicate if format probing has already be done.
1884
2255
:param ignore_fallbacks: when set, no fallback branches will be opened
1983
2393
base = property(_get_base, doc="The URL for the root of this branch.")
2396
def user_transport(self):
2397
return self._user_transport
1985
2399
def _get_config(self):
1986
return TransportConfig(self._transport, 'branch.conf')
2400
return _mod_config.TransportConfig(self._transport, 'branch.conf')
2402
def _get_config_store(self):
2403
if self.conf_store is None:
2404
self.conf_store = _mod_config.BranchStore(self)
2405
return self.conf_store
2407
def _uncommitted_branch(self):
2408
"""Return the branch that may contain uncommitted changes."""
2409
master = self.get_master_branch()
2410
if master is not None:
2415
def store_uncommitted(self, creator):
2416
"""Store uncommitted changes from a ShelfCreator.
2418
:param creator: The ShelfCreator containing uncommitted changes, or
2419
None to delete any stored changes.
2420
:raises: ChangesAlreadyStored if the branch already has changes.
2422
branch = self._uncommitted_branch()
2424
branch._transport.delete('stored-transform')
2426
if branch._transport.has('stored-transform'):
2427
raise errors.ChangesAlreadyStored
2428
transform = StringIO()
2429
creator.write_shelf(transform)
2431
branch._transport.put_file('stored-transform', transform)
2433
def get_unshelver(self, tree):
2434
"""Return a shelf.Unshelver for this branch and tree.
2436
:param tree: The tree to use to construct the Unshelver.
2437
:return: an Unshelver or None if no changes are stored.
2439
branch = self._uncommitted_branch()
2441
transform = branch._transport.get('stored-transform')
2442
except errors.NoSuchFile:
2444
return shelf.Unshelver.from_tree_and_shelf(tree, transform)
1988
2446
def is_locked(self):
1989
2447
return self.control_files.is_locked()
1991
2449
def lock_write(self, token=None):
1992
# All-in-one needs to always unlock/lock.
1993
repo_control = getattr(self.repository, 'control_files', None)
1994
if self.control_files == repo_control or not self.is_locked():
2450
"""Lock the branch for write operations.
2452
:param token: A token to permit reacquiring a previously held and
2454
:return: A BranchWriteLockResult.
2456
if not self.is_locked():
2457
self._note_lock('w')
2458
self.repository._warn_if_deprecated(self)
1995
2459
self.repository.lock_write()
1996
2460
took_lock = True
1998
2462
took_lock = False
2000
return self.control_files.lock_write(token=token)
2464
return BranchWriteLockResult(self.unlock,
2465
self.control_files.lock_write(token=token))
2003
2468
self.repository.unlock()
2006
2471
def lock_read(self):
2007
# All-in-one needs to always unlock/lock.
2008
repo_control = getattr(self.repository, 'control_files', None)
2009
if self.control_files == repo_control or not self.is_locked():
2472
"""Lock the branch for read operations.
2474
:return: A bzrlib.lock.LogicalLockResult.
2476
if not self.is_locked():
2477
self._note_lock('r')
2478
self.repository._warn_if_deprecated(self)
2010
2479
self.repository.lock_read()
2011
2480
took_lock = True
2013
2482
took_lock = False
2015
2484
self.control_files.lock_read()
2485
return LogicalLockResult(self.unlock)
2018
2488
self.repository.unlock()
2491
@only_raises(errors.LockNotHeld, errors.LockBroken)
2021
2492
def unlock(self):
2493
if self.control_files._lock_count == 1 and self.conf_store is not None:
2494
self.conf_store.save_changes()
2023
2496
self.control_files.unlock()
2025
# All-in-one needs to always unlock/lock.
2026
repo_control = getattr(self.repository, 'control_files', None)
2027
if (self.control_files == repo_control or
2028
not self.control_files.is_locked()):
2029
self.repository.unlock()
2030
2498
if not self.control_files.is_locked():
2499
self.repository.unlock()
2031
2500
# we just released the lock
2032
2501
self._clear_cached_state()
2045
2514
"""See Branch.print_file."""
2046
2515
return self.repository.print_file(file, revision_id)
2048
def _write_revision_history(self, history):
2049
"""Factored out of set_revision_history.
2051
This performs the actual writing to disk.
2052
It is intended to be called by BzrBranch5.set_revision_history."""
2053
self._transport.put_bytes(
2054
'revision-history', '\n'.join(history),
2055
mode=self.bzrdir._get_file_mode())
2058
def set_revision_history(self, rev_history):
2059
"""See Branch.set_revision_history."""
2060
if 'evil' in debug.debug_flags:
2061
mutter_callsite(3, "set_revision_history scales with history.")
2062
check_not_reserved_id = _mod_revision.check_not_reserved_id
2063
for rev_id in rev_history:
2064
check_not_reserved_id(rev_id)
2065
if Branch.hooks['post_change_branch_tip']:
2066
# Don't calculate the last_revision_info() if there are no hooks
2068
old_revno, old_revid = self.last_revision_info()
2069
if len(rev_history) == 0:
2070
revid = _mod_revision.NULL_REVISION
2072
revid = rev_history[-1]
2073
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2074
self._write_revision_history(rev_history)
2075
self._clear_cached_state()
2076
self._cache_revision_history(rev_history)
2077
for hook in Branch.hooks['set_rh']:
2078
hook(self, rev_history)
2079
if Branch.hooks['post_change_branch_tip']:
2080
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2082
def _synchronize_history(self, destination, revision_id):
2083
"""Synchronize last revision and revision history between branches.
2085
This version is most efficient when the destination is also a
2086
BzrBranch5, but works for BzrBranch6 as long as the revision
2087
history is the true lefthand parent history, and all of the revisions
2088
are in the destination's repository. If not, set_revision_history
2091
:param destination: The branch to copy the history into
2092
:param revision_id: The revision-id to truncate history at. May
2093
be None to copy complete history.
2095
if not isinstance(destination._format, BzrBranchFormat5):
2096
super(BzrBranch, self)._synchronize_history(
2097
destination, revision_id)
2099
if revision_id == _mod_revision.NULL_REVISION:
2102
new_history = self.revision_history()
2103
if revision_id is not None and new_history != []:
2105
new_history = new_history[:new_history.index(revision_id) + 1]
2107
rev = self.repository.get_revision(revision_id)
2108
new_history = rev.get_history(self.repository)[1:]
2109
destination.set_revision_history(new_history)
2111
2517
@needs_write_lock
2112
2518
def set_last_revision_info(self, revno, revision_id):
2113
"""Set the last revision of this branch.
2115
The caller is responsible for checking that the revno is correct
2116
for this revision id.
2118
It may be possible to set the branch last revision to an id not
2119
present in the repository. However, branches can also be
2120
configured to check constraints on history, in which case this may not
2519
if not revision_id or not isinstance(revision_id, basestring):
2520
raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2123
2521
revision_id = _mod_revision.ensure_null(revision_id)
2124
# this old format stores the full history, but this api doesn't
2125
# provide it, so we must generate, and might as well check it's
2127
history = self._lefthand_history(revision_id)
2128
if len(history) != revno:
2129
raise AssertionError('%d != %d' % (len(history), revno))
2130
self.set_revision_history(history)
2132
def _gen_revision_history(self):
2133
history = self._transport.get_bytes('revision-history').split('\n')
2134
if history[-1:] == ['']:
2135
# There shouldn't be a trailing newline, but just in case.
2140
def generate_revision_history(self, revision_id, last_rev=None,
2142
"""Create a new revision history that will finish with revision_id.
2144
:param revision_id: the new tip to use.
2145
:param last_rev: The previous last_revision. If not None, then this
2146
must be a ancestory of revision_id, or DivergedBranches is raised.
2147
:param other_branch: The other branch that DivergedBranches should
2148
raise with respect to.
2150
self.set_revision_history(self._lefthand_history(revision_id,
2151
last_rev, other_branch))
2522
old_revno, old_revid = self.last_revision_info()
2523
if self.get_append_revisions_only():
2524
self._check_history_violation(revision_id)
2525
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2526
self._write_last_revision_info(revno, revision_id)
2527
self._clear_cached_state()
2528
self._last_revision_info_cache = revno, revision_id
2529
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2153
2531
def basis_tree(self):
2154
2532
"""See Branch.basis_tree."""
2155
2533
return self.repository.revision_tree(self.last_revision())
2158
def pull(self, source, overwrite=False, stop_revision=None,
2159
_hook_master=None, run_hooks=True, possible_transports=None,
2160
_override_hook_target=None):
2163
:param _hook_master: Private parameter - set the branch to
2164
be supplied as the master to pull hooks.
2165
:param run_hooks: Private parameter - if false, this branch
2166
is being called because it's the master of the primary branch,
2167
so it should not run its hooks.
2168
:param _override_hook_target: Private parameter - set the branch to be
2169
supplied as the target_branch to pull hooks.
2171
result = PullResult()
2172
result.source_branch = source
2173
if _override_hook_target is None:
2174
result.target_branch = self
2176
result.target_branch = _override_hook_target
2179
# We assume that during 'pull' the local repository is closer than
2181
source.update_references(self)
2182
graph = self.repository.get_graph(source.repository)
2183
result.old_revno, result.old_revid = self.last_revision_info()
2184
self.update_revisions(source, stop_revision, overwrite=overwrite,
2186
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
2187
result.new_revno, result.new_revid = self.last_revision_info()
2189
result.master_branch = _hook_master
2190
result.local_branch = result.target_branch
2192
result.master_branch = result.target_branch
2193
result.local_branch = None
2195
for hook in Branch.hooks['post_pull']:
2201
2535
def _get_parent_location(self):
2202
2536
_locs = ['parent', 'pull', 'x-pull']
2203
2537
for l in _locs:
2211
def push(self, target, overwrite=False, stop_revision=None,
2212
_override_hook_source_branch=None):
2215
This is the basic concrete implementation of push()
2217
:param _override_hook_source_branch: If specified, run
2218
the hooks passing this Branch as the source, rather than self.
2219
This is for use of RemoteBranch, where push is delegated to the
2220
underlying vfs-based Branch.
2222
# TODO: Public option to disable running hooks - should be trivial but
2224
return _run_with_write_locked_target(
2225
target, self._push_with_bound_branches, target, overwrite,
2227
_override_hook_source_branch=_override_hook_source_branch)
2229
def _push_with_bound_branches(self, target, overwrite,
2231
_override_hook_source_branch=None):
2232
"""Push from self into target, and into target's master if any.
2234
This is on the base BzrBranch class even though it doesn't support
2235
bound branches because the *target* might be bound.
2238
if _override_hook_source_branch:
2239
result.source_branch = _override_hook_source_branch
2240
for hook in Branch.hooks['post_push']:
2243
bound_location = target.get_bound_location()
2244
if bound_location and target.base != bound_location:
2245
# there is a master branch.
2247
# XXX: Why the second check? Is it even supported for a branch to
2248
# be bound to itself? -- mbp 20070507
2249
master_branch = target.get_master_branch()
2250
master_branch.lock_write()
2252
# push into the master from this branch.
2253
self._basic_push(master_branch, overwrite, stop_revision)
2254
# and push into the target branch from this. Note that we push from
2255
# this branch again, because its considered the highest bandwidth
2257
result = self._basic_push(target, overwrite, stop_revision)
2258
result.master_branch = master_branch
2259
result.local_branch = target
2263
master_branch.unlock()
2266
result = self._basic_push(target, overwrite, stop_revision)
2267
# TODO: Why set master_branch and local_branch if there's no
2268
# binding? Maybe cleaner to just leave them unset? -- mbp
2270
result.master_branch = target
2271
result.local_branch = None
2275
def _basic_push(self, target, overwrite, stop_revision):
2276
"""Basic implementation of push without bound branches or hooks.
2278
Must be called with self read locked and target write locked.
2280
result = BranchPushResult()
2281
result.source_branch = self
2282
result.target_branch = target
2283
result.old_revno, result.old_revid = target.last_revision_info()
2284
self.update_references(target)
2285
if result.old_revid != self.last_revision():
2286
# We assume that during 'push' this repository is closer than
2288
graph = self.repository.get_graph(target.repository)
2289
target.update_revisions(self, stop_revision, overwrite=overwrite,
2291
if self._push_should_merge_tags():
2292
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2293
result.new_revno, result.new_revid = target.last_revision_info()
2296
2544
def get_stacked_on_url(self):
2297
raise errors.UnstackableBranchFormat(self._format, self.base)
2545
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2299
2547
def set_push_location(self, location):
2300
2548
"""See Branch.set_push_location."""
2456
2701
raise AssertionError(
2457
2702
"'transform_fallback_location' hook %s returned "
2458
2703
"None, not a URL." % hook_name)
2459
self._activate_fallback_location(url, None)
2704
self._activate_fallback_location(url,
2705
possible_transports=possible_transports)
2461
2707
def __init__(self, *args, **kwargs):
2462
2708
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2463
2709
super(BzrBranch8, self).__init__(*args, **kwargs)
2464
2710
self._last_revision_info_cache = None
2465
self._partial_revision_history_cache = []
2466
2711
self._reference_info = None
2468
2713
def _clear_cached_state(self):
2469
2714
super(BzrBranch8, self)._clear_cached_state()
2470
2715
self._last_revision_info_cache = None
2471
self._partial_revision_history_cache = []
2472
2716
self._reference_info = None
2474
def _last_revision_info(self):
2475
revision_string = self._transport.get_bytes('last-revision')
2476
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2477
revision_id = cache_utf8.get_cached_utf8(revision_id)
2479
return revno, revision_id
2481
def _write_last_revision_info(self, revno, revision_id):
2482
"""Simply write out the revision id, with no checks.
2484
Use set_last_revision_info to perform this safely.
2486
Does not update the revision_history cache.
2487
Intended to be called by set_last_revision_info and
2488
_write_revision_history.
2490
revision_id = _mod_revision.ensure_null(revision_id)
2491
out_string = '%d %s\n' % (revno, revision_id)
2492
self._transport.put_bytes('last-revision', out_string,
2493
mode=self.bzrdir._get_file_mode())
2496
def set_last_revision_info(self, revno, revision_id):
2497
revision_id = _mod_revision.ensure_null(revision_id)
2498
old_revno, old_revid = self.last_revision_info()
2499
if self._get_append_revisions_only():
2500
self._check_history_violation(revision_id)
2501
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2502
self._write_last_revision_info(revno, revision_id)
2503
self._clear_cached_state()
2504
self._last_revision_info_cache = revno, revision_id
2505
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2507
def _synchronize_history(self, destination, revision_id):
2508
"""Synchronize last revision and revision history between branches.
2510
:see: Branch._synchronize_history
2512
# XXX: The base Branch has a fast implementation of this method based
2513
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2514
# that uses set_revision_history. This class inherits from BzrBranch5,
2515
# but wants the fast implementation, so it calls
2516
# Branch._synchronize_history directly.
2517
Branch._synchronize_history(self, destination, revision_id)
2519
2718
def _check_history_violation(self, revision_id):
2520
last_revision = _mod_revision.ensure_null(self.last_revision())
2719
current_revid = self.last_revision()
2720
last_revision = _mod_revision.ensure_null(current_revid)
2521
2721
if _mod_revision.is_null(last_revision):
2523
if last_revision not in self._lefthand_history(revision_id):
2524
raise errors.AppendRevisionsOnlyViolation(self.base)
2723
graph = self.repository.get_graph()
2724
for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2725
if lh_ancestor == current_revid:
2727
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2526
2729
def _gen_revision_history(self):
2527
2730
"""Generate the revision history from last revision
2977
3112
_optimisers = []
2978
3113
"""The available optimised InterBranch types."""
2981
def _get_branch_formats_to_test():
2982
"""Return a tuple with the Branch formats to use when testing."""
2983
raise NotImplementedError(self._get_branch_formats_to_test)
2985
def update_revisions(self, stop_revision=None, overwrite=False,
2987
"""Pull in new perfect-fit revisions.
2989
:param stop_revision: Updated until the given revision
2990
:param overwrite: Always set the branch pointer, rather than checking
2991
to see if it is a proper descendant.
2992
:param graph: A Graph object that can be used to query history
2993
information. This can be None.
2996
raise NotImplementedError(self.update_revisions)
3116
def _get_branch_formats_to_test(klass):
3117
"""Return an iterable of format tuples for testing.
3119
:return: An iterable of (from_format, to_format) to use when testing
3120
this InterBranch class. Each InterBranch class should define this
3123
raise NotImplementedError(klass._get_branch_formats_to_test)
3126
def pull(self, overwrite=False, stop_revision=None,
3127
possible_transports=None, local=False):
3128
"""Mirror source into target branch.
3130
The target branch is considered to be 'local', having low latency.
3132
:returns: PullResult instance
3134
raise NotImplementedError(self.pull)
3137
def push(self, overwrite=False, stop_revision=None, lossy=False,
3138
_override_hook_source_branch=None):
3139
"""Mirror the source branch into the target branch.
3141
The source branch is considered to be 'local', having low latency.
3143
raise NotImplementedError(self.push)
3146
def copy_content_into(self, revision_id=None):
3147
"""Copy the content of source into target
3149
revision_id: if not None, the revision history in the new branch will
3150
be truncated to end with revision_id.
3152
raise NotImplementedError(self.copy_content_into)
3155
def fetch(self, stop_revision=None, limit=None):
3158
:param stop_revision: Last revision to fetch
3159
:param limit: Optional rough limit of revisions to fetch
3161
raise NotImplementedError(self.fetch)
3164
def _fix_overwrite_type(overwrite):
3165
if isinstance(overwrite, bool):
3167
return ["history", "tags"]
2999
3173
class GenericInterBranch(InterBranch):
3000
"""InterBranch implementation that uses public Branch functions.
3004
def _get_branch_formats_to_test():
3005
return BranchFormat._default_format, BranchFormat._default_format
3007
def update_revisions(self, stop_revision=None, overwrite=False,
3009
"""See InterBranch.update_revisions()."""
3010
self.source.lock_read()
3012
other_revno, other_last_revision = self.source.last_revision_info()
3013
stop_revno = None # unknown
3014
if stop_revision is None:
3015
stop_revision = other_last_revision
3016
if _mod_revision.is_null(stop_revision):
3017
# if there are no commits, we're done.
3019
stop_revno = other_revno
3021
# what's the current last revision, before we fetch [and change it
3023
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3024
# we fetch here so that we don't process data twice in the common
3025
# case of having something to pull, and so that the check for
3026
# already merged can operate on the just fetched graph, which will
3027
# be cached in memory.
3028
self.target.fetch(self.source, stop_revision)
3029
# Check to see if one is an ancestor of the other
3032
graph = self.target.repository.get_graph()
3033
if self.target._check_if_descendant_or_diverged(
3034
stop_revision, last_rev, graph, self.source):
3035
# stop_revision is a descendant of last_rev, but we aren't
3036
# overwriting, so we're done.
3038
if stop_revno is None:
3040
graph = self.target.repository.get_graph()
3041
this_revno, this_last_revision = \
3042
self.target.last_revision_info()
3043
stop_revno = graph.find_distance_to_null(stop_revision,
3044
[(other_last_revision, other_revno),
3045
(this_last_revision, this_revno)])
3046
self.target.set_last_revision_info(stop_revno, stop_revision)
3048
self.source.unlock()
3174
"""InterBranch implementation that uses public Branch functions."""
3051
def is_compatible(self, source, target):
3177
def is_compatible(klass, source, target):
3052
3178
# GenericBranch uses the public API, so always compatible
3182
def _get_branch_formats_to_test(klass):
3183
return [(format_registry.get_default(), format_registry.get_default())]
3186
def unwrap_format(klass, format):
3187
if isinstance(format, remote.RemoteBranchFormat):
3188
format._ensure_real()
3189
return format._custom_format
3193
def copy_content_into(self, revision_id=None):
3194
"""Copy the content of source into target
3196
revision_id: if not None, the revision history in the new branch will
3197
be truncated to end with revision_id.
3199
self.source.update_references(self.target)
3200
self.source._synchronize_history(self.target, revision_id)
3202
parent = self.source.get_parent()
3203
except errors.InaccessibleParent, e:
3204
mutter('parent was not accessible to copy: %s', e)
3207
self.target.set_parent(parent)
3208
if self.source._push_should_merge_tags():
3209
self.source.tags.merge_to(self.target.tags)
3212
def fetch(self, stop_revision=None, limit=None):
3213
if self.target.base == self.source.base:
3215
self.source.lock_read()
3217
fetch_spec_factory = fetch.FetchSpecFactory()
3218
fetch_spec_factory.source_branch = self.source
3219
fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3220
fetch_spec_factory.source_repo = self.source.repository
3221
fetch_spec_factory.target_repo = self.target.repository
3222
fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3223
fetch_spec_factory.limit = limit
3224
fetch_spec = fetch_spec_factory.make_fetch_spec()
3225
return self.target.repository.fetch(self.source.repository,
3226
fetch_spec=fetch_spec)
3228
self.source.unlock()
3231
def _update_revisions(self, stop_revision=None, overwrite=False,
3233
other_revno, other_last_revision = self.source.last_revision_info()
3234
stop_revno = None # unknown
3235
if stop_revision is None:
3236
stop_revision = other_last_revision
3237
if _mod_revision.is_null(stop_revision):
3238
# if there are no commits, we're done.
3240
stop_revno = other_revno
3242
# what's the current last revision, before we fetch [and change it
3244
last_rev = _mod_revision.ensure_null(self.target.last_revision())
3245
# we fetch here so that we don't process data twice in the common
3246
# case of having something to pull, and so that the check for
3247
# already merged can operate on the just fetched graph, which will
3248
# be cached in memory.
3249
self.fetch(stop_revision=stop_revision)
3250
# Check to see if one is an ancestor of the other
3253
graph = self.target.repository.get_graph()
3254
if self.target._check_if_descendant_or_diverged(
3255
stop_revision, last_rev, graph, self.source):
3256
# stop_revision is a descendant of last_rev, but we aren't
3257
# overwriting, so we're done.
3259
if stop_revno is None:
3261
graph = self.target.repository.get_graph()
3262
this_revno, this_last_revision = \
3263
self.target.last_revision_info()
3264
stop_revno = graph.find_distance_to_null(stop_revision,
3265
[(other_last_revision, other_revno),
3266
(this_last_revision, this_revno)])
3267
self.target.set_last_revision_info(stop_revno, stop_revision)
3270
def pull(self, overwrite=False, stop_revision=None,
3271
possible_transports=None, run_hooks=True,
3272
_override_hook_target=None, local=False):
3273
"""Pull from source into self, updating my master if any.
3275
:param run_hooks: Private parameter - if false, this branch
3276
is being called because it's the master of the primary branch,
3277
so it should not run its hooks.
3279
bound_location = self.target.get_bound_location()
3280
if local and not bound_location:
3281
raise errors.LocalRequiresBoundBranch()
3282
master_branch = None
3283
source_is_master = False
3285
# bound_location comes from a config file, some care has to be
3286
# taken to relate it to source.user_url
3287
normalized = urlutils.normalize_url(bound_location)
3289
relpath = self.source.user_transport.relpath(normalized)
3290
source_is_master = (relpath == '')
3291
except (errors.PathNotChild, errors.InvalidURL):
3292
source_is_master = False
3293
if not local and bound_location and not source_is_master:
3294
# not pulling from master, so we need to update master.
3295
master_branch = self.target.get_master_branch(possible_transports)
3296
master_branch.lock_write()
3299
# pull from source into master.
3300
master_branch.pull(self.source, overwrite, stop_revision,
3302
return self._pull(overwrite,
3303
stop_revision, _hook_master=master_branch,
3304
run_hooks=run_hooks,
3305
_override_hook_target=_override_hook_target,
3306
merge_tags_to_master=not source_is_master)
3309
master_branch.unlock()
3311
def push(self, overwrite=False, stop_revision=None, lossy=False,
3312
_override_hook_source_branch=None):
3313
"""See InterBranch.push.
3315
This is the basic concrete implementation of push()
3317
:param _override_hook_source_branch: If specified, run the hooks
3318
passing this Branch as the source, rather than self. This is for
3319
use of RemoteBranch, where push is delegated to the underlying
3323
raise errors.LossyPushToSameVCS(self.source, self.target)
3324
# TODO: Public option to disable running hooks - should be trivial but
3327
op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3328
op.add_cleanup(self.source.lock_read().unlock)
3329
op.add_cleanup(self.target.lock_write().unlock)
3330
return op.run(overwrite, stop_revision,
3331
_override_hook_source_branch=_override_hook_source_branch)
3333
def _basic_push(self, overwrite, stop_revision):
3334
"""Basic implementation of push without bound branches or hooks.
3336
Must be called with source read locked and target write locked.
3338
result = BranchPushResult()
3339
result.source_branch = self.source
3340
result.target_branch = self.target
3341
result.old_revno, result.old_revid = self.target.last_revision_info()
3342
self.source.update_references(self.target)
3343
overwrite = _fix_overwrite_type(overwrite)
3344
if result.old_revid != stop_revision:
3345
# We assume that during 'push' this repository is closer than
3347
graph = self.source.repository.get_graph(self.target.repository)
3348
self._update_revisions(stop_revision,
3349
overwrite=("history" in overwrite),
3351
if self.source._push_should_merge_tags():
3352
result.tag_updates, result.tag_conflicts = (
3353
self.source.tags.merge_to(
3354
self.target.tags, "tags" in overwrite))
3355
result.new_revno, result.new_revid = self.target.last_revision_info()
3358
def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3359
_override_hook_source_branch=None):
3360
"""Push from source into target, and into target's master if any.
3363
if _override_hook_source_branch:
3364
result.source_branch = _override_hook_source_branch
3365
for hook in Branch.hooks['post_push']:
3368
bound_location = self.target.get_bound_location()
3369
if bound_location and self.target.base != bound_location:
3370
# there is a master branch.
3372
# XXX: Why the second check? Is it even supported for a branch to
3373
# be bound to itself? -- mbp 20070507
3374
master_branch = self.target.get_master_branch()
3375
master_branch.lock_write()
3376
operation.add_cleanup(master_branch.unlock)
3377
# push into the master from the source branch.
3378
master_inter = InterBranch.get(self.source, master_branch)
3379
master_inter._basic_push(overwrite, stop_revision)
3380
# and push into the target branch from the source. Note that
3381
# we push from the source branch again, because it's considered
3382
# the highest bandwidth repository.
3383
result = self._basic_push(overwrite, stop_revision)
3384
result.master_branch = master_branch
3385
result.local_branch = self.target
3387
master_branch = None
3389
result = self._basic_push(overwrite, stop_revision)
3390
# TODO: Why set master_branch and local_branch if there's no
3391
# binding? Maybe cleaner to just leave them unset? -- mbp
3393
result.master_branch = self.target
3394
result.local_branch = None
3398
def _pull(self, overwrite=False, stop_revision=None,
3399
possible_transports=None, _hook_master=None, run_hooks=True,
3400
_override_hook_target=None, local=False,
3401
merge_tags_to_master=True):
3404
This function is the core worker, used by GenericInterBranch.pull to
3405
avoid duplication when pulling source->master and source->local.
3407
:param _hook_master: Private parameter - set the branch to
3408
be supplied as the master to pull hooks.
3409
:param run_hooks: Private parameter - if false, this branch
3410
is being called because it's the master of the primary branch,
3411
so it should not run its hooks.
3412
is being called because it's the master of the primary branch,
3413
so it should not run its hooks.
3414
:param _override_hook_target: Private parameter - set the branch to be
3415
supplied as the target_branch to pull hooks.
3416
:param local: Only update the local branch, and not the bound branch.
3418
# This type of branch can't be bound.
3420
raise errors.LocalRequiresBoundBranch()
3421
result = PullResult()
3422
result.source_branch = self.source
3423
if _override_hook_target is None:
3424
result.target_branch = self.target
3426
result.target_branch = _override_hook_target
3427
self.source.lock_read()
3429
# We assume that during 'pull' the target repository is closer than
3431
self.source.update_references(self.target)
3432
graph = self.target.repository.get_graph(self.source.repository)
3433
# TODO: Branch formats should have a flag that indicates
3434
# that revno's are expensive, and pull() should honor that flag.
3436
result.old_revno, result.old_revid = \
3437
self.target.last_revision_info()
3438
overwrite = _fix_overwrite_type(overwrite)
3439
self._update_revisions(stop_revision,
3440
overwrite=("history" in overwrite),
3442
# TODO: The old revid should be specified when merging tags,
3443
# so a tags implementation that versions tags can only
3444
# pull in the most recent changes. -- JRV20090506
3445
result.tag_updates, result.tag_conflicts = (
3446
self.source.tags.merge_to(self.target.tags,
3447
"tags" in overwrite,
3448
ignore_master=not merge_tags_to_master))
3449
result.new_revno, result.new_revid = self.target.last_revision_info()
3451
result.master_branch = _hook_master
3452
result.local_branch = result.target_branch
3454
result.master_branch = result.target_branch
3455
result.local_branch = None
3457
for hook in Branch.hooks['post_pull']:
3460
self.source.unlock()
3056
3464
InterBranch.register_optimiser(GenericInterBranch)