63
63
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
66
class Branch(bzrdir.ControlComponent):
66
# TODO: Maybe include checks for common corruption of newlines, etc?
68
# TODO: Some operations like log might retrieve the same revisions
69
# repeatedly to calculate deltas. We could perhaps have a weakref
70
# cache in memory to make this faster. In general anything can be
71
# cached in memory between lock and unlock operations. .. nb thats
72
# what the transaction identity map provides
75
######################################################################
67
79
"""Branch holding a history of revisions.
70
Base directory/url of the branch; using control_url and
71
control_transport is more standardized.
82
Base directory/url of the branch.
73
84
hooks: An instance of BranchHooks.
80
def control_transport(self):
81
return self._transport
84
def user_transport(self):
85
return self.bzrdir.user_transport
87
90
def __init__(self, *ignored, **ignored_too):
88
91
self.tags = self._format.make_tags(self)
89
92
self._revision_history_cache = None
104
107
"""Activate the branch/repository from url as a fallback repository."""
105
108
repo = self._get_fallback_repository(url)
106
109
if repo.has_same_location(self.repository):
107
raise errors.UnstackableLocationError(self.user_url, url)
110
raise errors.UnstackableLocationError(self.base, url)
108
111
self.repository.add_fallback_repository(repo)
110
113
def break_lock(self):
417
420
* 'include' - the stop revision is the last item in the result
418
421
* 'with-merges' - include the stop revision and all of its
419
422
merged revisions in the result
420
* 'with-merges-without-common-ancestry' - filter out revisions
421
that are in both ancestries
422
423
:param direction: either 'reverse' or 'forward':
423
424
* reverse means return the start_revision_id first, i.e.
424
425
start at the most recent revision and go backwards in history
446
447
# start_revision_id.
447
448
if self._merge_sorted_revisions_cache is None:
448
449
last_revision = self.last_revision()
449
known_graph = self.repository.get_known_graph_ancestry(
450
last_key = (last_revision,)
451
known_graph = self.repository.revisions.get_known_graph_ancestry(
451
453
self._merge_sorted_revisions_cache = known_graph.merge_sort(
453
455
filtered = self._filter_merge_sorted_revisions(
454
456
self._merge_sorted_revisions_cache, start_revision_id,
455
457
stop_revision_id, stop_rule)
456
# Make sure we don't return revisions that are not part of the
457
# start_revision_id ancestry.
458
filtered = self._filter_start_non_ancestors(filtered)
459
458
if direction == 'reverse':
461
460
if direction == 'forward':
498
497
node.end_of_merge)
499
498
if rev_id == stop_revision_id:
501
elif stop_rule == 'with-merges-without-common-ancestry':
502
# We want to exclude all revisions that are already part of the
503
# stop_revision_id ancestry.
504
graph = self.repository.get_graph()
505
ancestors = graph.find_unique_ancestors(start_revision_id,
507
for node in rev_iter:
508
rev_id = node.key[-1]
509
if rev_id not in ancestors:
511
yield (rev_id, node.merge_depth, node.revno,
513
500
elif stop_rule == 'with-merges':
514
501
stop_rev = self.repository.get_revision(stop_revision_id)
515
502
if stop_rev.parent_ids:
539
526
raise ValueError('invalid stop_rule %r' % stop_rule)
541
def _filter_start_non_ancestors(self, rev_iter):
542
# If we started from a dotted revno, we want to consider it as a tip
543
# and don't want to yield revisions that are not part of its
544
# ancestry. Given the order guaranteed by the merge sort, we will see
545
# uninteresting descendants of the first parent of our tip before the
547
first = rev_iter.next()
548
(rev_id, merge_depth, revno, end_of_merge) = first
551
# We start at a mainline revision so by definition, all others
552
# revisions in rev_iter are ancestors
553
for node in rev_iter:
558
pmap = self.repository.get_parent_map([rev_id])
559
parents = pmap.get(rev_id, [])
561
whitelist.update(parents)
563
# If there is no parents, there is nothing of interest left
565
# FIXME: It's hard to test this scenario here as this code is never
566
# called in that case. -- vila 20100322
569
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
571
if rev_id in whitelist:
572
pmap = self.repository.get_parent_map([rev_id])
573
parents = pmap.get(rev_id, [])
574
whitelist.remove(rev_id)
575
whitelist.update(parents)
577
# We've reached the mainline, there is nothing left to
581
# A revision that is not part of the ancestry of our
584
yield (rev_id, merge_depth, revno, end_of_merge)
586
528
def leave_lock_in_place(self):
587
529
"""Tell this branch object not to release the physical lock when this
588
530
object is unlocked.
605
547
:param other: The branch to bind to
606
548
:type other: Branch
608
raise errors.UpgradeRequired(self.user_url)
550
raise errors.UpgradeRequired(self.base)
610
552
def set_append_revisions_only(self, enabled):
611
553
if not self._format.supports_set_append_revisions_only():
612
raise errors.UpgradeRequired(self.user_url)
554
raise errors.UpgradeRequired(self.base)
663
605
def get_old_bound_location(self):
664
606
"""Return the URL of the branch we used to be bound to
666
raise errors.UpgradeRequired(self.user_url)
608
raise errors.UpgradeRequired(self.base)
668
610
def get_commit_builder(self, parents, config=None, timestamp=None,
669
611
timezone=None, committer=None, revprops=None,
749
691
if not self._format.supports_stacking():
750
raise errors.UnstackableBranchFormat(self._format, self.user_url)
692
raise errors.UnstackableBranchFormat(self._format, self.base)
751
693
# XXX: Changing from one fallback repository to another does not check
752
694
# that all the data you need is present in the new fallback.
753
695
# Possibly it should.
905
847
def unbind(self):
906
848
"""Older format branches cannot bind or unbind."""
907
raise errors.UpgradeRequired(self.user_url)
849
raise errors.UpgradeRequired(self.base)
909
851
def last_revision(self):
910
852
"""Return last revision id, or NULL_REVISION."""
1071
1013
return urlutils.join(self.base[:-1], parent)
1072
1014
except errors.InvalidURLJoin, e:
1073
raise errors.InaccessibleParent(parent, self.user_url)
1015
raise errors.InaccessibleParent(parent, self.base)
1075
1017
def _get_parent_location(self):
1076
1018
raise NotImplementedError(self._get_parent_location)
1576
1518
:return: a branch in this format
1578
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1520
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1579
1521
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1581
1523
'metadir': ('lock', lockdir.LockDir),
1968
1910
if format.__class__ != self.__class__:
1969
1911
raise AssertionError("wrong format %r found for %r" %
1970
1912
(format, self))
1971
transport = a_bzrdir.get_branch_transport(None, name=name)
1914
transport = a_bzrdir.get_branch_transport(None, name=name)
1973
1915
control_files = lockable_files.LockableFiles(transport, 'lock',
1974
1916
lockdir.LockDir)
1975
1917
return self._branch_class()(_format=self,
2173
2115
# this format does not implement branch itself, thus the implicit
2174
2116
# creation contract must see it as uninitializable
2175
2117
raise errors.UninitializableFormat(self)
2176
mutter('creating branch reference in %s', a_bzrdir.user_url)
2118
mutter('creating branch reference in %s', a_bzrdir.transport.base)
2177
2119
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2178
2120
branch_transport.put_bytes('location',
2179
target_branch.bzrdir.user_url)
2121
target_branch.bzrdir.root_transport.base)
2180
2122
branch_transport.put_bytes('format', self.get_format_string())
2181
2123
branch = self.open(
2182
2124
a_bzrdir, name, _found=True,
2305
2247
def __str__(self):
2306
2248
if self.name is None:
2307
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2249
return '%s(%r)' % (self.__class__.__name__, self.base)
2309
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2251
return '%s(%r,%r)' % (self.__class__.__name__, self.base, self.name)
2312
2253
__repr__ = __str__
2530
2471
def get_stacked_on_url(self):
2531
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2472
raise errors.UnstackableBranchFormat(self._format, self.base)
2533
2474
def set_push_location(self, location):
2534
2475
"""See Branch.set_push_location."""
2724
2665
if _mod_revision.is_null(last_revision):
2726
2667
if last_revision not in self._lefthand_history(revision_id):
2727
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2668
raise errors.AppendRevisionsOnlyViolation(self.base)
2729
2670
def _gen_revision_history(self):
2730
2671
"""Generate the revision history from last revision
2830
2771
if branch_location is None:
2831
2772
return Branch.reference_parent(self, file_id, path,
2832
2773
possible_transports)
2833
branch_location = urlutils.join(self.user_url, branch_location)
2774
branch_location = urlutils.join(self.base, branch_location)
2834
2775
return Branch.open(branch_location,
2835
2776
possible_transports=possible_transports)
2882
2823
return stacked_url
2884
2825
def _get_append_revisions_only(self):
2885
return self.get_config(
2886
).get_user_option_as_bool('append_revisions_only')
2826
value = self.get_config().get_user_option('append_revisions_only')
2827
return value == 'True'
2888
2829
@needs_write_lock
2889
2830
def generate_revision_history(self, revision_id, last_rev=None,
2953
2894
def get_stacked_on_url(self):
2954
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2895
raise errors.UnstackableBranchFormat(self._format, self.base)
2957
2898
######################################################################
3044
2985
:param verbose: Requests more detailed display of what was checked,
3047
note('checked branch %s format %s', self.branch.user_url,
2988
note('checked branch %s format %s', self.branch.base,
3048
2989
self.branch._format)
3049
2990
for error in self.errors:
3050
2991
note('found error:%s', error)
3379
3320
if local and not bound_location:
3380
3321
raise errors.LocalRequiresBoundBranch()
3381
3322
master_branch = None
3382
if not local and bound_location and self.source.user_url != bound_location:
3323
if not local and bound_location and self.source.base != bound_location:
3383
3324
# not pulling from master, so we need to update master.
3384
3325
master_branch = self.target.get_master_branch(possible_transports)
3385
3326
master_branch.lock_write()