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):
283
286
new_history.reverse()
284
287
return new_history
286
def lock_write(self, token=None):
287
"""Lock the branch for write operations.
289
:param token: A token to permit reacquiring a previously held and
291
:return: A BranchWriteLockResult.
289
def lock_write(self):
293
290
raise NotImplementedError(self.lock_write)
295
292
def lock_read(self):
296
"""Lock the branch for read operations.
298
:return: An object with an unlock method which will release the lock
301
293
raise NotImplementedError(self.lock_read)
303
295
def unlock(self):
428
420
* 'include' - the stop revision is the last item in the result
429
421
* 'with-merges' - include the stop revision and all of its
430
422
merged revisions in the result
431
* 'with-merges-without-common-ancestry' - filter out revisions
432
that are in both ancestries
433
423
:param direction: either 'reverse' or 'forward':
434
424
* reverse means return the start_revision_id first, i.e.
435
425
start at the most recent revision and go backwards in history
457
447
# start_revision_id.
458
448
if self._merge_sorted_revisions_cache is None:
459
449
last_revision = self.last_revision()
460
known_graph = self.repository.get_known_graph_ancestry(
450
last_key = (last_revision,)
451
known_graph = self.repository.revisions.get_known_graph_ancestry(
462
453
self._merge_sorted_revisions_cache = known_graph.merge_sort(
464
455
filtered = self._filter_merge_sorted_revisions(
465
456
self._merge_sorted_revisions_cache, start_revision_id,
466
457
stop_revision_id, stop_rule)
467
# Make sure we don't return revisions that are not part of the
468
# start_revision_id ancestry.
469
filtered = self._filter_start_non_ancestors(filtered)
470
458
if direction == 'reverse':
472
460
if direction == 'forward':
509
497
node.end_of_merge)
510
498
if rev_id == stop_revision_id:
512
elif stop_rule == 'with-merges-without-common-ancestry':
513
# We want to exclude all revisions that are already part of the
514
# stop_revision_id ancestry.
515
graph = self.repository.get_graph()
516
ancestors = graph.find_unique_ancestors(start_revision_id,
518
for node in rev_iter:
519
rev_id = node.key[-1]
520
if rev_id not in ancestors:
522
yield (rev_id, node.merge_depth, node.revno,
524
500
elif stop_rule == 'with-merges':
525
501
stop_rev = self.repository.get_revision(stop_revision_id)
526
502
if stop_rev.parent_ids:
550
526
raise ValueError('invalid stop_rule %r' % stop_rule)
552
def _filter_start_non_ancestors(self, rev_iter):
553
# If we started from a dotted revno, we want to consider it as a tip
554
# and don't want to yield revisions that are not part of its
555
# ancestry. Given the order guaranteed by the merge sort, we will see
556
# uninteresting descendants of the first parent of our tip before the
558
first = rev_iter.next()
559
(rev_id, merge_depth, revno, end_of_merge) = first
562
# We start at a mainline revision so by definition, all others
563
# revisions in rev_iter are ancestors
564
for node in rev_iter:
569
pmap = self.repository.get_parent_map([rev_id])
570
parents = pmap.get(rev_id, [])
572
whitelist.update(parents)
574
# If there is no parents, there is nothing of interest left
576
# FIXME: It's hard to test this scenario here as this code is never
577
# called in that case. -- vila 20100322
580
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
582
if rev_id in whitelist:
583
pmap = self.repository.get_parent_map([rev_id])
584
parents = pmap.get(rev_id, [])
585
whitelist.remove(rev_id)
586
whitelist.update(parents)
588
# We've reached the mainline, there is nothing left to
592
# A revision that is not part of the ancestry of our
595
yield (rev_id, merge_depth, revno, end_of_merge)
597
528
def leave_lock_in_place(self):
598
529
"""Tell this branch object not to release the physical lock when this
599
530
object is unlocked.
616
547
:param other: The branch to bind to
617
548
:type other: Branch
619
raise errors.UpgradeRequired(self.user_url)
550
raise errors.UpgradeRequired(self.base)
621
552
def set_append_revisions_only(self, enabled):
622
553
if not self._format.supports_set_append_revisions_only():
623
raise errors.UpgradeRequired(self.user_url)
554
raise errors.UpgradeRequired(self.base)
674
605
def get_old_bound_location(self):
675
606
"""Return the URL of the branch we used to be bound to
677
raise errors.UpgradeRequired(self.user_url)
608
raise errors.UpgradeRequired(self.base)
679
610
def get_commit_builder(self, parents, config=None, timestamp=None,
680
611
timezone=None, committer=None, revprops=None,
760
691
if not self._format.supports_stacking():
761
raise errors.UnstackableBranchFormat(self._format, self.user_url)
692
raise errors.UnstackableBranchFormat(self._format, self.base)
762
693
# XXX: Changing from one fallback repository to another does not check
763
694
# that all the data you need is present in the new fallback.
764
695
# Possibly it should.
916
847
def unbind(self):
917
848
"""Older format branches cannot bind or unbind."""
918
raise errors.UpgradeRequired(self.user_url)
849
raise errors.UpgradeRequired(self.base)
920
851
def last_revision(self):
921
852
"""Return last revision id, or NULL_REVISION."""
1082
1013
return urlutils.join(self.base[:-1], parent)
1083
1014
except errors.InvalidURLJoin, e:
1084
raise errors.InaccessibleParent(parent, self.user_url)
1015
raise errors.InaccessibleParent(parent, self.base)
1086
1017
def _get_parent_location(self):
1087
1018
raise NotImplementedError(self._get_parent_location)
1587
1518
:return: a branch in this format
1589
mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1520
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1590
1521
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1592
1523
'metadir': ('lock', lockdir.LockDir),
1785
1716
self.create_hook(HookPoint('post_branch_init',
1786
1717
"Called after new branch initialization completes. "
1787
"post_branch_init is called with a "
1788
"bzrlib.branch.BranchInitHookParams. "
1789
"Note that init, branch and checkout (both heavyweight and "
1790
"lightweight) will all trigger this hook.", (2, 2), None))
1718
"post_branch_init is called with a bzrlib.branch.BranchInitHookParams. "
1719
"Note that init, branch and checkout will all trigger this hook.",
1791
1721
self.create_hook(HookPoint('post_switch',
1792
1722
"Called after a checkout switches branch. "
1793
1723
"post_switch is called with a "
1843
1773
There are 4 fields that hooks may wish to access:
1845
1775
:ivar format: the branch format
1846
:ivar bzrdir: the BzrDir where the branch will be/has been initialized
1776
:ivar bzrdir: the bzrdir where the branch will be/has been initialized
1847
1777
:ivar name: name of colocated branch, if any (or None)
1848
:ivar branch: the branch created
1850
Note that for lightweight checkouts, the bzrdir and format fields refer to
1851
the checkout, hence they are different from the corresponding fields in
1852
branch, which refer to the original branch.
1778
:ivar branch: the branch
1855
1781
def __init__(self, format, a_bzrdir, name, branch):
1856
1782
"""Create a group of BranchInitHook parameters.
1858
1784
:param format: the branch format
1859
:param a_bzrdir: the BzrDir where the branch will be/has been
1785
:param a_bzrdir: the bzrdir where the branch will be/has been initialized
1861
1786
:param name: name of colocated branch, if any (or None)
1862
:param branch: the branch created
1864
Note that for lightweight checkouts, the bzrdir and format fields refer
1865
to the checkout, hence they are different from the corresponding fields
1866
in branch, which refer to the original branch.
1787
:param branch: the branch
1868
1789
self.format = format
1869
1790
self.bzrdir = a_bzrdir
1979
1900
if format.__class__ != self.__class__:
1980
1901
raise AssertionError("wrong format %r found for %r" %
1981
1902
(format, self))
1982
transport = a_bzrdir.get_branch_transport(None, name=name)
1904
transport = a_bzrdir.get_branch_transport(None, name=name)
1984
1905
control_files = lockable_files.LockableFiles(transport, 'lock',
1985
1906
lockdir.LockDir)
1986
1907
return self._branch_class()(_format=self,
2184
2105
# this format does not implement branch itself, thus the implicit
2185
2106
# creation contract must see it as uninitializable
2186
2107
raise errors.UninitializableFormat(self)
2187
mutter('creating branch reference in %s', a_bzrdir.user_url)
2108
mutter('creating branch reference in %s', a_bzrdir.transport.base)
2188
2109
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2189
2110
branch_transport.put_bytes('location',
2190
target_branch.bzrdir.user_url)
2111
target_branch.bzrdir.root_transport.base)
2191
2112
branch_transport.put_bytes('format', self.get_format_string())
2192
2113
branch = self.open(
2193
2114
a_bzrdir, name, _found=True,
2276
2197
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2279
class BranchWriteLockResult(object):
2280
"""The result of write locking a branch.
2282
:ivar branch_token: The token obtained from the underlying branch lock, or
2284
:ivar unlock: A callable which will unlock the lock.
2287
def __init__(self, unlock, branch_token):
2288
self.branch_token = branch_token
2289
self.unlock = unlock
2292
return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2296
2200
class BzrBranch(Branch, _RelockDebugMixin):
2297
2201
"""A branch stored in the actual filesystem.
2333
2237
def __str__(self):
2334
2238
if self.name is None:
2335
return '%s(%s)' % (self.__class__.__name__, self.user_url)
2239
return '%s(%r)' % (self.__class__.__name__, self.base)
2337
return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2241
return '%s(%r,%r)' % (self.__class__.__name__, self.base, self.name)
2340
2243
__repr__ = __str__
2352
2255
return self.control_files.is_locked()
2354
2257
def lock_write(self, token=None):
2355
"""Lock the branch for write operations.
2357
:param token: A token to permit reacquiring a previously held and
2359
:return: A BranchWriteLockResult.
2361
2258
if not self.is_locked():
2362
2259
self._note_lock('w')
2363
2260
# All-in-one needs to always unlock/lock.
2370
2267
took_lock = False
2372
return BranchWriteLockResult(self.unlock,
2373
self.control_files.lock_write(token=token))
2269
return self.control_files.lock_write(token=token)
2376
2272
self.repository.unlock()
2379
2275
def lock_read(self):
2380
"""Lock the branch for read operations.
2382
:return: An object with an unlock method which will release the lock
2385
2276
if not self.is_locked():
2386
2277
self._note_lock('r')
2387
2278
# All-in-one needs to always unlock/lock.
2571
2461
def get_stacked_on_url(self):
2572
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2462
raise errors.UnstackableBranchFormat(self._format, self.base)
2574
2464
def set_push_location(self, location):
2575
2465
"""See Branch.set_push_location."""
2765
2655
if _mod_revision.is_null(last_revision):
2767
2657
if last_revision not in self._lefthand_history(revision_id):
2768
raise errors.AppendRevisionsOnlyViolation(self.user_url)
2658
raise errors.AppendRevisionsOnlyViolation(self.base)
2770
2660
def _gen_revision_history(self):
2771
2661
"""Generate the revision history from last revision
2871
2761
if branch_location is None:
2872
2762
return Branch.reference_parent(self, file_id, path,
2873
2763
possible_transports)
2874
branch_location = urlutils.join(self.user_url, branch_location)
2764
branch_location = urlutils.join(self.base, branch_location)
2875
2765
return Branch.open(branch_location,
2876
2766
possible_transports=possible_transports)
2923
2813
return stacked_url
2925
2815
def _get_append_revisions_only(self):
2926
return self.get_config(
2927
).get_user_option_as_bool('append_revisions_only')
2816
value = self.get_config().get_user_option('append_revisions_only')
2817
return value == 'True'
2929
2819
@needs_write_lock
2930
2820
def generate_revision_history(self, revision_id, last_rev=None,
2994
2884
def get_stacked_on_url(self):
2995
raise errors.UnstackableBranchFormat(self._format, self.user_url)
2885
raise errors.UnstackableBranchFormat(self._format, self.base)
2998
2888
######################################################################
3085
2975
:param verbose: Requests more detailed display of what was checked,
3088
note('checked branch %s format %s', self.branch.user_url,
2978
note('checked branch %s format %s', self.branch.base,
3089
2979
self.branch._format)
3090
2980
for error in self.errors:
3091
2981
note('found error:%s', error)
3420
3310
if local and not bound_location:
3421
3311
raise errors.LocalRequiresBoundBranch()
3422
3312
master_branch = None
3423
if not local and bound_location and self.source.user_url != bound_location:
3313
if not local and bound_location and self.source.base != bound_location:
3424
3314
# not pulling from master, so we need to update master.
3425
3315
master_branch = self.target.get_master_branch(possible_transports)
3426
3316
master_branch.lock_write()