55
73
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
58
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
59
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
60
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
63
# TODO: Maybe include checks for common corruption of newlines, etc?
65
# TODO: Some operations like log might retrieve the same revisions
66
# repeatedly to calculate deltas. We could perhaps have a weakref
67
# cache in memory to make this faster. In general anything can be
68
# cached in memory between lock and unlock operations. .. nb thats
69
# what the transaction identity map provides
72
######################################################################
76
class Branch(controldir.ControlComponent):
76
77
"""Branch holding a history of revisions.
79
Base directory/url of the branch.
81
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
83
86
# this is really an instance variable - FIXME move it there
87
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):
88
99
self.tags = self._format.make_tags(self)
89
100
self._revision_history_cache = None
90
101
self._revision_id_to_revno_cache = None
91
102
self._partial_revision_id_to_revno_cache = {}
103
self._partial_revision_history_cache = []
104
self._tags_bytes = None
92
105
self._last_revision_info_cache = None
106
self._master_branch_cache = None
93
107
self._merge_sorted_revisions_cache = None
108
self._open_hook(possible_transports)
95
109
hooks = Branch.hooks['open']
96
110
for hook in hooks:
113
def _open_hook(self, possible_transports):
100
114
"""Called by init to allow simpler extension of the base class."""
102
def _activate_fallback_location(self, url, lock_style):
116
def _activate_fallback_location(self, url, possible_transports):
103
117
"""Activate the branch/repository from url as a fallback repository."""
104
repo = self._get_fallback_repository(url)
105
if lock_style == 'write':
107
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)
109
128
self.repository.add_fallback_repository(repo)
111
130
def break_lock(self):
440
544
"""Iterate over an inclusive range of sorted revisions."""
441
545
rev_iter = iter(merge_sorted_revisions)
442
546
if start_revision_id is not None:
443
for rev_id, depth, revno, end_of_merge in rev_iter:
547
for node in rev_iter:
444
549
if rev_id != start_revision_id:
447
552
# The decision to include the start or not
448
553
# depends on the stop_rule if a stop is provided
450
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)
453
557
if stop_revision_id is None:
454
for rev_id, depth, revno, end_of_merge in rev_iter:
455
yield rev_id, depth, revno, end_of_merge
559
for node in rev_iter:
561
yield (rev_id, node.merge_depth, node.revno,
456
563
elif stop_rule == 'exclude':
457
for rev_id, depth, revno, end_of_merge in rev_iter:
564
for node in rev_iter:
458
566
if rev_id == stop_revision_id:
460
yield rev_id, depth, revno, end_of_merge
568
yield (rev_id, node.merge_depth, node.revno,
461
570
elif stop_rule == 'include':
462
for rev_id, depth, revno, end_of_merge in rev_iter:
463
yield rev_id, depth, revno, end_of_merge
571
for node in rev_iter:
573
yield (rev_id, node.merge_depth, node.revno,
464
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,
466
589
elif stop_rule == 'with-merges':
467
590
stop_rev = self.repository.get_revision(stop_revision_id)
468
591
if stop_rev.parent_ids:
469
592
left_parent = stop_rev.parent_ids[0]
471
594
left_parent = _mod_revision.NULL_REVISION
472
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:
473
601
if rev_id == left_parent:
602
# reached the left parent after the stop_revision
475
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)
477
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)
479
662
def leave_lock_in_place(self):
480
663
"""Tell this branch object not to release the physical lock when this
481
664
object is unlocked.
630
854
except (errors.NotStacked, errors.UnstackableBranchFormat,
631
855
errors.UnstackableRepositoryFormat):
634
# XXX: Lock correctness - should unlock our old repo if we were
636
# repositories don't offer an interface to remove fallback
637
# repositories today; take the conceptually simpler option and just
639
self.repository = self.bzrdir.find_repository()
640
self.repository.lock_write()
641
# for every revision reference the branch has, ensure it is pulled
643
source_repository = self._get_fallback_repository(old_url)
644
for revision_id in chain([self.last_revision()],
645
self.tags.get_reverse_tag_dict()):
646
self.repository.fetch(source_repository, revision_id,
649
self._activate_fallback_location(url, 'write')
859
self._activate_fallback_location(url,
860
possible_transports=[self.bzrdir.root_transport])
650
861
# write this out after the repository is stacked to avoid setting a
651
862
# stacked config that doesn't work.
652
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()
655
951
def _set_tags_bytes(self, bytes):
656
952
"""Mirror method for _get_tags_bytes.
658
954
:seealso: Branch._get_tags_bytes.
660
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)
663
964
def _cache_revision_history(self, rev_history):
664
965
"""Set the cached revision history to rev_history.
751
1046
:return: A tuple (revno, revision_id).
753
1048
if self._last_revision_info_cache is None:
754
self._last_revision_info_cache = self._last_revision_info()
1049
self._last_revision_info_cache = self._read_last_revision_info()
755
1050
return self._last_revision_info_cache
757
def _last_revision_info(self):
758
rh = self.revision_history()
761
return (revno, rh[-1])
763
return (0, _mod_revision.NULL_REVISION)
765
@deprecated_method(deprecated_in((1, 6, 0)))
766
def missing_revisions(self, other, stop_revision=None):
767
"""Return a list of new revisions that would perfectly fit.
769
If self and other have not diverged, return a list of the revisions
770
present in other, but missing from self.
772
self_history = self.revision_history()
773
self_len = len(self_history)
774
other_history = other.revision_history()
775
other_len = len(other_history)
776
common_index = min(self_len, other_len) -1
777
if common_index >= 0 and \
778
self_history[common_index] != other_history[common_index]:
779
raise errors.DivergedBranches(self, other)
781
if stop_revision is None:
782
stop_revision = other_len
784
if stop_revision > other_len:
785
raise errors.NoSuchRevision(self, stop_revision)
786
return other_history[self_len:stop_revision]
789
def update_revisions(self, other, stop_revision=None, overwrite=False,
791
"""Pull in new perfect-fit revisions.
793
:param other: Another Branch to pull from
794
:param stop_revision: Updated until the given revision
795
:param overwrite: Always set the branch pointer, rather than checking
796
to see if it is a proper descendant.
797
:param graph: A Graph object that can be used to query history
798
information. This can be None.
801
return InterBranch.get(other, self).update_revisions(stop_revision,
804
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,
805
1057
"""Set the last revision info, importing from another repo if necessary.
807
1059
This is used by the bound branch code to upload a revision to
808
1060
the master branch first before updating the tip of the local branch.
1061
Revisions referenced by source's tags are also transferred.
810
:param source_repo: Source repository to optionally fetch from
1063
:param source: Source branch to optionally fetch from
811
1064
:param revno: Revision number of the new tip
812
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)
814
if not self.repository.has_same_location(source_repo):
815
self.repository.fetch(source_repo, revision_id=revid)
1071
if not self.repository.has_same_location(source.repository):
1072
self.fetch(source, revid)
816
1073
self.set_last_revision_info(revno, revid)
1074
return (revno, revid)
818
1076
def revision_id_to_revno(self, revision_id):
819
1077
"""Given a revision id, return its revno"""
820
1078
if _mod_revision.is_null(revision_id):
822
history = self.revision_history()
1080
history = self._revision_history()
824
1082
return history.index(revision_id) + 1
825
1083
except ValueError:
826
1084
raise errors.NoSuchRevision(self, revision_id)
828
1087
def get_rev_id(self, revno, history=None):
829
1088
"""Find the revision id of the specified revno."""
831
1090
return _mod_revision.NULL_REVISION
833
history = self.revision_history()
834
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:
835
1095
raise errors.NoSuchRevision(self, revno)
836
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]
838
1101
def pull(self, source, overwrite=False, stop_revision=None,
839
possible_transports=None, _override_hook_target=None):
1102
possible_transports=None, *args, **kwargs):
840
1103
"""Mirror source into this branch.
842
1105
This branch is considered to be 'local', having low latency.
844
1107
:returns: PullResult instance
846
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)
848
def push(self, target, overwrite=False, stop_revision=None):
1113
def push(self, target, overwrite=False, stop_revision=None, lossy=False,
849
1115
"""Mirror this branch into target.
851
1117
This branch is considered to be 'local', having low latency.
853
raise NotImplementedError(self.push)
1119
return InterBranch.get(self, target).push(overwrite, stop_revision,
1120
lossy, *args, **kwargs)
855
1122
def basis_tree(self):
856
1123
"""Return `Tree` object for last revision."""
1092
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()
1094
1369
:return: A BranchCheckResult.
1096
mainline_parent_id = None
1371
result = BranchCheckResult(self)
1097
1372
last_revno, last_revision_id = self.last_revision_info()
1098
real_rev_history = list(self.repository.iter_reverse_revision_history(
1100
real_rev_history.reverse()
1101
if len(real_rev_history) != last_revno:
1102
raise errors.BzrCheckError('revno does not match len(mainline)'
1103
' %s != %s' % (last_revno, len(real_rev_history)))
1104
# TODO: We should probably also check that real_rev_history actually
1105
# matches self.revision_history()
1106
for revision_id in real_rev_history:
1108
revision = self.repository.get_revision(revision_id)
1109
except errors.NoSuchRevision, e:
1110
raise errors.BzrCheckError("mainline revision {%s} not in repository"
1112
# In general the first entry on the revision history has no parents.
1113
# But it's not illegal for it to have parents listed; this can happen
1114
# in imports from Arch when the parents weren't reachable.
1115
if mainline_parent_id is not None:
1116
if mainline_parent_id not in revision.parent_ids:
1117
raise errors.BzrCheckError("previous revision {%s} not listed among "
1119
% (mainline_parent_id, revision_id))
1120
mainline_parent_id = revision_id
1121
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
1123
def _get_checkout_format(self):
1385
def _get_checkout_format(self, lightweight=False):
1124
1386
"""Return the most suitable metadir for a checkout of this branch.
1125
1387
Weaves are used if this branch's repository uses weaves.
1127
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1128
from bzrlib.repofmt import weaverepo
1129
format = bzrdir.BzrDirMetaFormat1()
1130
format.repository_format = weaverepo.RepositoryFormat7()
1132
format = self.repository.bzrdir.checkout_metadir()
1133
format.set_branch_format(self._format)
1389
format = self.repository.bzrdir.checkout_metadir()
1390
format.set_branch_format(self._format)
1136
1393
def create_clone_on_transport(self, to_transport, revision_id=None,
1394
stacked_on=None, create_prefix=False, use_existing_dir=False,
1138
1396
"""Create a clone of this branch and its bzrdir.
1140
1398
:param to_transport: The transport to clone onto.
1141
1399
:param revision_id: The revision id to use as tip in the new branch.
1142
1400
If None the tip is obtained from this branch.
1143
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.
1145
1406
# XXX: Fix the bzrdir API to allow getting the branch back from the
1146
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()
1147
1412
dir_to = self.bzrdir.clone_on_transport(to_transport,
1148
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,
1149
1416
return dir_to.open_branch()
1151
1418
def create_checkout(self, to_location, revision_id=None,
1271
1589
object will be created every time regardless.
1274
_default_format = None
1275
"""The default format used for new branches."""
1278
"""The known formats."""
1280
1592
def __eq__(self, other):
1281
1593
return self.__class__ is other.__class__
1283
1595
def __ne__(self, other):
1284
1596
return not (self == other)
1287
def find_format(klass, a_bzrdir):
1288
"""Return the format for the branch object in a_bzrdir."""
1290
transport = a_bzrdir.get_branch_transport(None)
1291
format_string = transport.get("format").read()
1292
return klass._formats[format_string]
1293
except errors.NoSuchFile:
1294
raise errors.NotBranchError(path=transport.base)
1296
raise errors.UnknownFormatError(format=format_string, kind='branch')
1299
def get_default_format(klass):
1300
"""Return the current default format."""
1301
return klass._default_format
1303
def get_reference(self, a_bzrdir):
1304
"""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.
1306
1601
format probing must have been completed before calling
1307
1602
this method - it is assumed that the format of the branch
1308
in a_bzrdir is correct.
1603
in controldir is correct.
1310
: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
1311
1607
:return: None if the branch is not a reference branch.
1316
def set_reference(self, a_bzrdir, to_branch):
1317
"""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.
1319
1615
format probing must have been completed before calling
1320
1616
this method - it is assumed that the format of the branch
1321
in a_bzrdir is correct.
1617
in controldir is correct.
1323
: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
1324
1621
:param to_branch: branch that the checkout is to reference
1326
1623
raise NotImplementedError(self.set_reference)
1328
def get_format_string(self):
1329
"""Return the ASCII format string that identifies this format."""
1330
raise NotImplementedError(self.get_format_string)
1332
1625
def get_format_description(self):
1333
1626
"""Return the short format description for this format."""
1334
1627
raise NotImplementedError(self.get_format_description)
1336
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1338
"""Initialize a branch in a bzrdir, with specified files
1340
:param a_bzrdir: The bzrdir to initialize the branch in
1341
:param utf8_files: The files to create as a list of
1342
(filename, content) tuples
1343
:param set_format: If True, set the format with
1344
self.get_format_string. (BzrBranch4 has its format set
1346
: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.
1348
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1349
branch_transport = a_bzrdir.get_branch_transport(self)
1351
'metadir': ('lock', lockdir.LockDir),
1352
'branch4': ('branch-lock', lockable_files.TransportLock),
1354
lock_name, lock_class = lock_map[lock_type]
1355
control_files = lockable_files.LockableFiles(branch_transport,
1356
lock_name, lock_class)
1357
control_files.create_lock()
1358
control_files.lock_write()
1360
utf8_files += [('format', self.get_format_string())]
1362
for (filename, content) in utf8_files:
1363
branch_transport.put_bytes(
1365
mode=a_bzrdir._get_file_mode())
1367
control_files.unlock()
1368
return self.open(a_bzrdir, _found=True)
1370
def initialize(self, a_bzrdir):
1371
"""Create a branch of this format in a_bzrdir."""
1372
1643
raise NotImplementedError(self.initialize)
1374
1645
def is_supported(self):
1561
1873
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1564
class BzrBranchFormat4(BranchFormat):
1565
"""Bzr branch format 4.
1568
- a revision-history file.
1569
- a branch-lock lock file [ to be shared with the bzrdir ]
1572
def get_format_description(self):
1573
"""See BranchFormat.get_format_description()."""
1574
return "Branch format 4"
1576
def initialize(self, a_bzrdir):
1577
"""Create a branch of this format in a_bzrdir."""
1578
utf8_files = [('revision-history', ''),
1579
('branch-name', ''),
1581
return self._initialize_helper(a_bzrdir, utf8_files,
1582
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.
1584
1953
def __init__(self):
1585
super(BzrBranchFormat4, self).__init__()
1586
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1588
def network_name(self):
1589
"""The network name for this format is the control dirs disk label."""
1590
return self._matchingbzrdir.get_format_string()
1592
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1593
"""See BranchFormat.open()."""
1595
# we are being called directly and must probe.
1596
raise NotImplementedError
1597
return BzrBranch(_format=self,
1598
_control_files=a_bzrdir._control_files,
1600
_repository=a_bzrdir.open_repository())
1603
return "Bazaar-NG branch format 4"
1606
class BranchFormatMetadir(BranchFormat):
1607
"""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)
1609
1970
def _branch_class(self):
1610
1971
"""What class to instantiate on open calls."""
1611
1972
raise NotImplementedError(self._branch_class)
1613
def network_name(self):
1614
"""A simple byte string uniquely identifying this format for RPC calls.
1616
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
1618
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)
1620
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):
1621
2015
"""See BranchFormat.open()."""
2017
name = a_bzrdir._get_selected_branch()
1623
format = BranchFormat.find_format(a_bzrdir)
2019
format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
1624
2020
if format.__class__ != self.__class__:
1625
2021
raise AssertionError("wrong format %r found for %r" %
1626
2022
(format, self))
2023
transport = a_bzrdir.get_branch_transport(None, name=name)
1628
transport = a_bzrdir.get_branch_transport(None)
1629
2025
control_files = lockable_files.LockableFiles(transport, 'lock',
1630
2026
lockdir.LockDir)
2027
if found_repository is None:
2028
found_repository = a_bzrdir.find_repository()
1631
2029
return self._branch_class()(_format=self,
1632
2030
_control_files=control_files,
1633
2032
a_bzrdir=a_bzrdir,
1634
_repository=a_bzrdir.find_repository(),
1635
ignore_fallbacks=ignore_fallbacks)
2033
_repository=found_repository,
2034
ignore_fallbacks=ignore_fallbacks,
2035
possible_transports=possible_transports)
1636
2036
except errors.NoSuchFile:
1637
raise errors.NotBranchError(path=transport.base)
1640
super(BranchFormatMetadir, self).__init__()
1641
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1642
self._matchingbzrdir.set_branch_format(self)
1644
def supports_tags(self):
1648
class BzrBranchFormat5(BranchFormatMetadir):
1649
"""Bzr branch format 5.
1652
- a revision-history file.
1654
- a lock dir guarding the branch itself
1655
- all of this stored in a branch/ subdirectory
1656
- works with shared repositories.
1658
This format is new in bzr 0.8.
1661
def _branch_class(self):
1664
def get_format_string(self):
1665
"""See BranchFormat.get_format_string()."""
1666
return "Bazaar-NG branch format 5\n"
1668
def get_format_description(self):
1669
"""See BranchFormat.get_format_description()."""
1670
return "Branch format 5"
1672
def initialize(self, a_bzrdir):
1673
"""Create a branch of this format in a_bzrdir."""
1674
utf8_files = [('revision-history', ''),
1675
('branch-name', ''),
1677
return self._initialize_helper(a_bzrdir, utf8_files)
1679
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)
1683
2060
class BzrBranchFormat6(BranchFormatMetadir):
1776
2201
"""See BranchFormat.get_format_description()."""
1777
2202
return "Checkout reference format 1"
1779
def get_reference(self, a_bzrdir):
2204
def get_reference(self, a_bzrdir, name=None):
1780
2205
"""See BranchFormat.get_reference()."""
1781
transport = a_bzrdir.get_branch_transport(None)
1782
return transport.get('location').read()
2206
transport = a_bzrdir.get_branch_transport(None, name=name)
2207
return transport.get_bytes('location')
1784
def set_reference(self, a_bzrdir, to_branch):
2209
def set_reference(self, a_bzrdir, name, to_branch):
1785
2210
"""See BranchFormat.set_reference()."""
1786
transport = a_bzrdir.get_branch_transport(None)
2211
transport = a_bzrdir.get_branch_transport(None, name=name)
1787
2212
location = transport.put_bytes('location', to_branch.base)
1789
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):
1790
2216
"""Create a branch of this format in a_bzrdir."""
1791
2217
if target_branch is None:
1792
2218
# this format does not implement branch itself, thus the implicit
1793
2219
# creation contract must see it as uninitializable
1794
2220
raise errors.UninitializableFormat(self)
1795
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1796
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)
1797
2227
branch_transport.put_bytes('location',
1798
target_branch.bzrdir.root_transport.base)
1799
branch_transport.put_bytes('format', self.get_format_string())
1801
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,
1802
2231
possible_transports=[target_branch.bzrdir.root_transport])
1805
super(BranchReferenceFormat, self).__init__()
1806
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1807
self._matchingbzrdir.set_branch_format(self)
2232
self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1809
2235
def _make_reference_clone_function(format, a_branch):
1810
2236
"""Create a clone() routine for a branch dynamically."""
1811
2237
def clone(to_bzrdir, revision_id=None,
1812
2238
repository_policy=None):
1813
2239
"""See Branch.clone()."""
1814
return format.initialize(to_bzrdir, a_branch)
2240
return format.initialize(to_bzrdir, target_branch=a_branch)
1815
2241
# cannot obey revision_id limits when cloning a reference ...
1816
2242
# FIXME RBC 20060210 either nuke revision_id for clone, or
1817
2243
# emit some sort of warning/error to the caller ?!
1820
def open(self, a_bzrdir, _found=False, location=None,
1821
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):
1822
2249
"""Return the branch that the branch reference in a_bzrdir points at.
1824
2251
:param a_bzrdir: A BzrDir that contains a branch.
2252
:param name: Name of colocated branch to open, if any
1825
2253
:param _found: a private parameter, do not use it. It is used to
1826
2254
indicate if format probing has already be done.
1827
2255
:param ignore_fallbacks: when set, no fallback branches will be opened
1924
2393
base = property(_get_base, doc="The URL for the root of this branch.")
2396
def user_transport(self):
2397
return self._user_transport
1926
2399
def _get_config(self):
1927
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)
1929
2446
def is_locked(self):
1930
2447
return self.control_files.is_locked()
1932
2449
def lock_write(self, token=None):
1933
# All-in-one needs to always unlock/lock.
1934
repo_control = getattr(self.repository, 'control_files', None)
1935
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)
1936
2459
self.repository.lock_write()
1937
2460
took_lock = True
1939
2462
took_lock = False
1941
return self.control_files.lock_write(token=token)
2464
return BranchWriteLockResult(self.unlock,
2465
self.control_files.lock_write(token=token))
1944
2468
self.repository.unlock()
1947
2471
def lock_read(self):
1948
# All-in-one needs to always unlock/lock.
1949
repo_control = getattr(self.repository, 'control_files', None)
1950
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)
1951
2479
self.repository.lock_read()
1952
2480
took_lock = True
1954
2482
took_lock = False
1956
2484
self.control_files.lock_read()
2485
return LogicalLockResult(self.unlock)
1959
2488
self.repository.unlock()
2491
@only_raises(errors.LockNotHeld, errors.LockBroken)
1962
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()
1964
2496
self.control_files.unlock()
1966
# All-in-one needs to always unlock/lock.
1967
repo_control = getattr(self.repository, 'control_files', None)
1968
if (self.control_files == repo_control or
1969
not self.control_files.is_locked()):
1970
self.repository.unlock()
1971
2498
if not self.control_files.is_locked():
2499
self.repository.unlock()
1972
2500
# we just released the lock
1973
2501
self._clear_cached_state()
1986
2514
"""See Branch.print_file."""
1987
2515
return self.repository.print_file(file, revision_id)
1989
def _write_revision_history(self, history):
1990
"""Factored out of set_revision_history.
1992
This performs the actual writing to disk.
1993
It is intended to be called by BzrBranch5.set_revision_history."""
1994
self._transport.put_bytes(
1995
'revision-history', '\n'.join(history),
1996
mode=self.bzrdir._get_file_mode())
1999
def set_revision_history(self, rev_history):
2000
"""See Branch.set_revision_history."""
2001
if 'evil' in debug.debug_flags:
2002
mutter_callsite(3, "set_revision_history scales with history.")
2003
check_not_reserved_id = _mod_revision.check_not_reserved_id
2004
for rev_id in rev_history:
2005
check_not_reserved_id(rev_id)
2006
if Branch.hooks['post_change_branch_tip']:
2007
# Don't calculate the last_revision_info() if there are no hooks
2009
old_revno, old_revid = self.last_revision_info()
2010
if len(rev_history) == 0:
2011
revid = _mod_revision.NULL_REVISION
2013
revid = rev_history[-1]
2014
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2015
self._write_revision_history(rev_history)
2016
self._clear_cached_state()
2017
self._cache_revision_history(rev_history)
2018
for hook in Branch.hooks['set_rh']:
2019
hook(self, rev_history)
2020
if Branch.hooks['post_change_branch_tip']:
2021
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2023
def _synchronize_history(self, destination, revision_id):
2024
"""Synchronize last revision and revision history between branches.
2026
This version is most efficient when the destination is also a
2027
BzrBranch5, but works for BzrBranch6 as long as the revision
2028
history is the true lefthand parent history, and all of the revisions
2029
are in the destination's repository. If not, set_revision_history
2032
:param destination: The branch to copy the history into
2033
:param revision_id: The revision-id to truncate history at. May
2034
be None to copy complete history.
2036
if not isinstance(destination._format, BzrBranchFormat5):
2037
super(BzrBranch, self)._synchronize_history(
2038
destination, revision_id)
2040
if revision_id == _mod_revision.NULL_REVISION:
2043
new_history = self.revision_history()
2044
if revision_id is not None and new_history != []:
2046
new_history = new_history[:new_history.index(revision_id) + 1]
2048
rev = self.repository.get_revision(revision_id)
2049
new_history = rev.get_history(self.repository)[1:]
2050
destination.set_revision_history(new_history)
2052
2517
@needs_write_lock
2053
2518
def set_last_revision_info(self, revno, revision_id):
2054
"""Set the last revision of this branch.
2056
The caller is responsible for checking that the revno is correct
2057
for this revision id.
2059
It may be possible to set the branch last revision to an id not
2060
present in the repository. However, branches can also be
2061
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)
2064
2521
revision_id = _mod_revision.ensure_null(revision_id)
2065
# this old format stores the full history, but this api doesn't
2066
# provide it, so we must generate, and might as well check it's
2068
history = self._lefthand_history(revision_id)
2069
if len(history) != revno:
2070
raise AssertionError('%d != %d' % (len(history), revno))
2071
self.set_revision_history(history)
2073
def _gen_revision_history(self):
2074
history = self._transport.get_bytes('revision-history').split('\n')
2075
if history[-1:] == ['']:
2076
# There shouldn't be a trailing newline, but just in case.
2081
def generate_revision_history(self, revision_id, last_rev=None,
2083
"""Create a new revision history that will finish with revision_id.
2085
:param revision_id: the new tip to use.
2086
:param last_rev: The previous last_revision. If not None, then this
2087
must be a ancestory of revision_id, or DivergedBranches is raised.
2088
:param other_branch: The other branch that DivergedBranches should
2089
raise with respect to.
2091
self.set_revision_history(self._lefthand_history(revision_id,
2092
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)
2094
2531
def basis_tree(self):
2095
2532
"""See Branch.basis_tree."""
2096
2533
return self.repository.revision_tree(self.last_revision())
2099
def pull(self, source, overwrite=False, stop_revision=None,
2100
_hook_master=None, run_hooks=True, possible_transports=None,
2101
_override_hook_target=None):
2104
:param _hook_master: Private parameter - set the branch to
2105
be supplied as the master to pull hooks.
2106
:param run_hooks: Private parameter - if false, this branch
2107
is being called because it's the master of the primary branch,
2108
so it should not run its hooks.
2109
:param _override_hook_target: Private parameter - set the branch to be
2110
supplied as the target_branch to pull hooks.
2112
result = PullResult()
2113
result.source_branch = source
2114
if _override_hook_target is None:
2115
result.target_branch = self
2117
result.target_branch = _override_hook_target
2120
# We assume that during 'pull' the local repository is closer than
2122
graph = self.repository.get_graph(source.repository)
2123
result.old_revno, result.old_revid = self.last_revision_info()
2124
self.update_revisions(source, stop_revision, overwrite=overwrite,
2126
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
2127
result.new_revno, result.new_revid = self.last_revision_info()
2129
result.master_branch = _hook_master
2130
result.local_branch = result.target_branch
2132
result.master_branch = result.target_branch
2133
result.local_branch = None
2135
for hook in Branch.hooks['post_pull']:
2141
2535
def _get_parent_location(self):
2142
2536
_locs = ['parent', 'pull', 'x-pull']
2143
2537
for l in _locs:
2151
def push(self, target, overwrite=False, stop_revision=None,
2152
_override_hook_source_branch=None):
2155
This is the basic concrete implementation of push()
2157
:param _override_hook_source_branch: If specified, run
2158
the hooks passing this Branch as the source, rather than self.
2159
This is for use of RemoteBranch, where push is delegated to the
2160
underlying vfs-based Branch.
2162
# TODO: Public option to disable running hooks - should be trivial but
2164
return _run_with_write_locked_target(
2165
target, self._push_with_bound_branches, target, overwrite,
2167
_override_hook_source_branch=_override_hook_source_branch)
2169
def _push_with_bound_branches(self, target, overwrite,
2171
_override_hook_source_branch=None):
2172
"""Push from self into target, and into target's master if any.
2174
This is on the base BzrBranch class even though it doesn't support
2175
bound branches because the *target* might be bound.
2178
if _override_hook_source_branch:
2179
result.source_branch = _override_hook_source_branch
2180
for hook in Branch.hooks['post_push']:
2183
bound_location = target.get_bound_location()
2184
if bound_location and target.base != bound_location:
2185
# there is a master branch.
2187
# XXX: Why the second check? Is it even supported for a branch to
2188
# be bound to itself? -- mbp 20070507
2189
master_branch = target.get_master_branch()
2190
master_branch.lock_write()
2192
# push into the master from this branch.
2193
self._basic_push(master_branch, overwrite, stop_revision)
2194
# and push into the target branch from this. Note that we push from
2195
# this branch again, because its considered the highest bandwidth
2197
result = self._basic_push(target, overwrite, stop_revision)
2198
result.master_branch = master_branch
2199
result.local_branch = target
2203
master_branch.unlock()
2206
result = self._basic_push(target, overwrite, stop_revision)
2207
# TODO: Why set master_branch and local_branch if there's no
2208
# binding? Maybe cleaner to just leave them unset? -- mbp
2210
result.master_branch = target
2211
result.local_branch = None
2215
def _basic_push(self, target, overwrite, stop_revision):
2216
"""Basic implementation of push without bound branches or hooks.
2218
Must be called with self read locked and target write locked.
2220
result = BranchPushResult()
2221
result.source_branch = self
2222
result.target_branch = target
2223
result.old_revno, result.old_revid = target.last_revision_info()
2224
if result.old_revid != self.last_revision():
2225
# We assume that during 'push' this repository is closer than
2227
graph = self.repository.get_graph(target.repository)
2228
target.update_revisions(self, stop_revision, overwrite=overwrite,
2230
if self._push_should_merge_tags():
2231
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2232
result.new_revno, result.new_revid = target.last_revision_info()
2235
2544
def get_stacked_on_url(self):
2236
raise errors.UnstackableBranchFormat(self._format, self.base)
2545
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2238
2547
def set_push_location(self, location):
2239
2548
"""See Branch.set_push_location."""
2395
2701
raise AssertionError(
2396
2702
"'transform_fallback_location' hook %s returned "
2397
2703
"None, not a URL." % hook_name)
2398
self._activate_fallback_location(url, None)
2704
self._activate_fallback_location(url,
2705
possible_transports=possible_transports)
2400
2707
def __init__(self, *args, **kwargs):
2401
2708
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2402
super(BzrBranch7, self).__init__(*args, **kwargs)
2709
super(BzrBranch8, self).__init__(*args, **kwargs)
2403
2710
self._last_revision_info_cache = None
2404
self._partial_revision_history_cache = []
2711
self._reference_info = None
2406
2713
def _clear_cached_state(self):
2407
super(BzrBranch7, self)._clear_cached_state()
2714
super(BzrBranch8, self)._clear_cached_state()
2408
2715
self._last_revision_info_cache = None
2409
self._partial_revision_history_cache = []
2411
def _last_revision_info(self):
2412
revision_string = self._transport.get_bytes('last-revision')
2413
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2414
revision_id = cache_utf8.get_cached_utf8(revision_id)
2416
return revno, revision_id
2418
def _write_last_revision_info(self, revno, revision_id):
2419
"""Simply write out the revision id, with no checks.
2421
Use set_last_revision_info to perform this safely.
2423
Does not update the revision_history cache.
2424
Intended to be called by set_last_revision_info and
2425
_write_revision_history.
2427
revision_id = _mod_revision.ensure_null(revision_id)
2428
out_string = '%d %s\n' % (revno, revision_id)
2429
self._transport.put_bytes('last-revision', out_string,
2430
mode=self.bzrdir._get_file_mode())
2433
def set_last_revision_info(self, revno, revision_id):
2434
revision_id = _mod_revision.ensure_null(revision_id)
2435
old_revno, old_revid = self.last_revision_info()
2436
if self._get_append_revisions_only():
2437
self._check_history_violation(revision_id)
2438
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2439
self._write_last_revision_info(revno, revision_id)
2440
self._clear_cached_state()
2441
self._last_revision_info_cache = revno, revision_id
2442
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2444
def _synchronize_history(self, destination, revision_id):
2445
"""Synchronize last revision and revision history between branches.
2447
:see: Branch._synchronize_history
2449
# XXX: The base Branch has a fast implementation of this method based
2450
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2451
# that uses set_revision_history. This class inherits from BzrBranch5,
2452
# but wants the fast implementation, so it calls
2453
# Branch._synchronize_history directly.
2454
Branch._synchronize_history(self, destination, revision_id)
2716
self._reference_info = None
2456
2718
def _check_history_violation(self, revision_id):
2457
last_revision = _mod_revision.ensure_null(self.last_revision())
2719
current_revid = self.last_revision()
2720
last_revision = _mod_revision.ensure_null(current_revid)
2458
2721
if _mod_revision.is_null(last_revision):
2460
if last_revision not in self._lefthand_history(revision_id):
2461
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)
2463
2729
def _gen_revision_history(self):
2464
2730
"""Generate the revision history from last revision
2522
2743
"""Set the parent branch"""
2523
2744
return self._get_config_location('parent_location')
2747
def _set_all_reference_info(self, info_dict):
2748
"""Replace all reference info stored in a branch.
2750
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2753
writer = rio.RioWriter(s)
2754
for key, (tree_path, branch_location) in info_dict.iteritems():
2755
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2756
branch_location=branch_location)
2757
writer.write_stanza(stanza)
2758
self._transport.put_bytes('references', s.getvalue())
2759
self._reference_info = info_dict
2762
def _get_all_reference_info(self):
2763
"""Return all the reference info stored in a branch.
2765
:return: A dict of {file_id: (tree_path, branch_location)}
2767
if self._reference_info is not None:
2768
return self._reference_info
2769
rio_file = self._transport.get('references')
2771
stanzas = rio.read_stanzas(rio_file)
2772
info_dict = dict((s['file_id'], (s['tree_path'],
2773
s['branch_location'])) for s in stanzas)
2776
self._reference_info = info_dict
2779
def set_reference_info(self, file_id, tree_path, branch_location):
2780
"""Set the branch location to use for a tree reference.
2782
:param file_id: The file-id of the tree reference.
2783
:param tree_path: The path of the tree reference in the tree.
2784
:param branch_location: The location of the branch to retrieve tree
2787
info_dict = self._get_all_reference_info()
2788
info_dict[file_id] = (tree_path, branch_location)
2789
if None in (tree_path, branch_location):
2790
if tree_path is not None:
2791
raise ValueError('tree_path must be None when branch_location'
2793
if branch_location is not None:
2794
raise ValueError('branch_location must be None when tree_path'
2796
del info_dict[file_id]
2797
self._set_all_reference_info(info_dict)
2799
def get_reference_info(self, file_id):
2800
"""Get the tree_path and branch_location for a tree reference.
2802
:return: a tuple of (tree_path, branch_location)
2804
return self._get_all_reference_info().get(file_id, (None, None))
2806
def reference_parent(self, file_id, path, possible_transports=None):
2807
"""Return the parent branch for a tree-reference file_id.
2809
:param file_id: The file_id of the tree reference
2810
:param path: The path of the file_id in the tree
2811
:return: A branch associated with the file_id
2813
branch_location = self.get_reference_info(file_id)[1]
2814
if branch_location is None:
2815
return Branch.reference_parent(self, file_id, path,
2816
possible_transports)
2817
branch_location = urlutils.join(self.user_url, branch_location)
2818
return Branch.open(branch_location,
2819
possible_transports=possible_transports)
2525
2821
def set_push_location(self, location):
2526
2822
"""See Branch.set_push_location."""
2527
2823
self._set_config_location('push_location', location)
2529
2825
def set_bound_location(self, location):
2530
2826
"""See Branch.set_push_location."""
2827
self._master_branch_cache = None
2532
config = self.get_config()
2829
conf = self.get_config_stack()
2533
2830
if location is None:
2534
if config.get_user_option('bound') != 'True':
2831
if not conf.get('bound'):
2537
config.set_user_option('bound', 'False', warn_masked=True)
2834
conf.set('bound', 'False')
2540
2837
self._set_config_location('bound_location', location,
2542
config.set_user_option('bound', 'True', warn_masked=True)
2839
conf.set('bound', 'True')
2545
2842
def _get_bound_location(self, bound):
2546
2843
"""Return the bound location in the config file.
2548
2845
Return None if the bound parameter does not match"""
2549
config = self.get_config()
2550
config_bound = (config.get_user_option('bound') == 'True')
2551
if config_bound != bound:
2846
conf = self.get_config_stack()
2847
if conf.get('bound') != bound:
2553
return self._get_config_location('bound_location', config=config)
2849
return self._get_config_location('bound_location', config=conf)
2555
2851
def get_bound_location(self):
2556
"""See Branch.set_push_location."""
2852
"""See Branch.get_bound_location."""
2557
2853
return self._get_bound_location(True)
2559
2855
def get_old_bound_location(self):
2816
3112
_optimisers = []
2817
3113
"""The available optimised InterBranch types."""
2820
def _get_branch_formats_to_test():
2821
"""Return a tuple with the Branch formats to use when testing."""
2822
raise NotImplementedError(self._get_branch_formats_to_test)
2824
def update_revisions(self, stop_revision=None, overwrite=False,
2826
"""Pull in new perfect-fit revisions.
2828
:param stop_revision: Updated until the given revision
2829
:param overwrite: Always set the branch pointer, rather than checking
2830
to see if it is a proper descendant.
2831
:param graph: A Graph object that can be used to query history
2832
information. This can be None.
2835
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"]
2838
3173
class GenericInterBranch(InterBranch):
2839
"""InterBranch implementation that uses public Branch functions.
2843
def _get_branch_formats_to_test():
2844
return BranchFormat._default_format, BranchFormat._default_format
2846
def update_revisions(self, stop_revision=None, overwrite=False,
2848
"""See InterBranch.update_revisions()."""
2849
self.source.lock_read()
2851
other_revno, other_last_revision = self.source.last_revision_info()
2852
stop_revno = None # unknown
2853
if stop_revision is None:
2854
stop_revision = other_last_revision
2855
if _mod_revision.is_null(stop_revision):
2856
# if there are no commits, we're done.
2858
stop_revno = other_revno
2860
# what's the current last revision, before we fetch [and change it
2862
last_rev = _mod_revision.ensure_null(self.target.last_revision())
2863
# we fetch here so that we don't process data twice in the common
2864
# case of having something to pull, and so that the check for
2865
# already merged can operate on the just fetched graph, which will
2866
# be cached in memory.
2867
self.target.fetch(self.source, stop_revision)
2868
# Check to see if one is an ancestor of the other
2871
graph = self.target.repository.get_graph()
2872
if self.target._check_if_descendant_or_diverged(
2873
stop_revision, last_rev, graph, self.source):
2874
# stop_revision is a descendant of last_rev, but we aren't
2875
# overwriting, so we're done.
2877
if stop_revno is None:
2879
graph = self.target.repository.get_graph()
2880
this_revno, this_last_revision = \
2881
self.target.last_revision_info()
2882
stop_revno = graph.find_distance_to_null(stop_revision,
2883
[(other_last_revision, other_revno),
2884
(this_last_revision, this_revno)])
2885
self.target.set_last_revision_info(stop_revno, stop_revision)
2887
self.source.unlock()
3174
"""InterBranch implementation that uses public Branch functions."""
2890
def is_compatible(self, source, target):
3177
def is_compatible(klass, source, target):
2891
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()
2895
3464
InterBranch.register_optimiser(GenericInterBranch)