49
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
49
from bzrlib.decorators import needs_read_lock, needs_write_lock
50
50
from bzrlib.hooks import HookPoint, Hooks
51
51
from bzrlib.inter import InterObject
52
from bzrlib.lock import _RelockDebugMixin
53
52
from bzrlib import registry
54
53
from bzrlib.symbol_versioning import (
168
167
control = bzrdir.BzrDir.open(base, _unsupported,
169
168
possible_transports=possible_transports)
170
return control.open_branch(unsupported=_unsupported)
169
return control.open_branch(_unsupported)
173
def open_from_transport(transport, name=None, _unsupported=False):
172
def open_from_transport(transport, _unsupported=False):
174
173
"""Open the branch rooted at transport"""
175
174
control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
176
return control.open_branch(name=name, unsupported=_unsupported)
175
return control.open_branch(_unsupported)
179
178
def open_containing(url, possible_transports=None):
217
216
def _get_fallback_repository(self, url):
218
217
"""Get the repository we fallback to at url."""
219
218
url = urlutils.join(self.base, url)
220
a_branch = Branch.open(url,
219
a_bzrdir = bzrdir.BzrDir.open(url,
221
220
possible_transports=[self.bzrdir.root_transport])
222
return a_branch.repository
221
return a_bzrdir.open_branch().repository
224
223
def _get_tags_bytes(self):
225
224
"""Get the bytes of a serialised tags dict.
447
446
# start_revision_id.
448
447
if self._merge_sorted_revisions_cache is None:
449
448
last_revision = self.last_revision()
450
known_graph = self.repository.get_known_graph_ancestry(
449
last_key = (last_revision,)
450
known_graph = self.repository.revisions.get_known_graph_ancestry(
452
452
self._merge_sorted_revisions_cache = known_graph.merge_sort(
454
454
filtered = self._filter_merge_sorted_revisions(
455
455
self._merge_sorted_revisions_cache, start_revision_id,
456
456
stop_revision_id, stop_rule)
457
# Make sure we don't return revisions that are not part of the
458
# start_revision_id ancestry.
459
filtered = self._filter_non_ancestors(filtered)
460
457
if direction == 'reverse':
462
459
if direction == 'forward':
505
502
left_parent = stop_rev.parent_ids[0]
507
504
left_parent = _mod_revision.NULL_REVISION
508
# left_parent is the actual revision we want to stop logging at,
509
# since we want to show the merged revisions after the stop_rev too
510
reached_stop_revision_id = False
511
revision_id_whitelist = []
512
505
for node in rev_iter:
513
506
rev_id = node.key[-1]
514
507
if rev_id == left_parent:
515
# reached the left parent after the stop_revision
517
if (not reached_stop_revision_id or
518
rev_id in revision_id_whitelist):
519
yield (rev_id, node.merge_depth, node.revno,
509
yield (rev_id, node.merge_depth, node.revno,
520
510
node.end_of_merge)
521
if reached_stop_revision_id or rev_id == stop_revision_id:
522
# only do the merged revs of rev_id from now on
523
rev = self.repository.get_revision(rev_id)
525
reached_stop_revision_id = True
526
revision_id_whitelist.extend(rev.parent_ids)
528
512
raise ValueError('invalid stop_rule %r' % stop_rule)
530
def _filter_non_ancestors(self, rev_iter):
531
# If we started from a dotted revno, we want to consider it as a tip
532
# and don't want to yield revisions that are not part of its
533
# ancestry. Given the order guaranteed by the merge sort, we will see
534
# uninteresting descendants of the first parent of our tip before the
536
first = rev_iter.next()
537
(rev_id, merge_depth, revno, end_of_merge) = first
540
# We start at a mainline revision so by definition, all others
541
# revisions in rev_iter are ancestors
542
for node in rev_iter:
547
pmap = self.repository.get_parent_map([rev_id])
548
parents = pmap.get(rev_id, [])
550
whitelist.update(parents)
552
# If there is no parents, there is nothing of interest left
554
# FIXME: It's hard to test this scenario here as this code is never
555
# called in that case. -- vila 20100322
558
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
560
if rev_id in whitelist:
561
pmap = self.repository.get_parent_map([rev_id])
562
parents = pmap.get(rev_id, [])
563
whitelist.remove(rev_id)
564
whitelist.update(parents)
566
# We've reached the mainline, there is nothing left to
570
# A revision that is not part of the ancestry of our
573
yield (rev_id, merge_depth, revno, end_of_merge)
575
514
def leave_lock_in_place(self):
576
515
"""Tell this branch object not to release the physical lock when this
577
516
object is unlocked.
1150
1089
params = ChangeBranchTipParams(
1151
1090
self, old_revno, new_revno, old_revid, new_revid)
1152
1091
for hook in hooks:
1094
except errors.TipChangeRejected:
1097
exc_info = sys.exc_info()
1098
hook_name = Branch.hooks.get_hook_name(hook)
1099
raise errors.HookFailed(
1100
'pre_change_branch_tip', hook_name, exc_info)
1155
1102
@needs_write_lock
1156
1103
def update(self):
1413
1359
def supports_tags(self):
1414
1360
return self._format.supports_tags()
1416
def automatic_tag_name(self, revision_id):
1417
"""Try to automatically find the tag name for a revision.
1419
:param revision_id: Revision id of the revision.
1420
:return: A tag name or None if no tag name could be determined.
1422
for hook in Branch.hooks['automatic_tag_name']:
1423
ret = hook(self, revision_id)
1428
1362
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1430
1364
"""Ensure that revision_b is a descendant of revision_a.
1494
1428
return not (self == other)
1497
def find_format(klass, a_bzrdir, name=None):
1431
def find_format(klass, a_bzrdir):
1498
1432
"""Return the format for the branch object in a_bzrdir."""
1500
transport = a_bzrdir.get_branch_transport(None, name=name)
1501
format_string = transport.get_bytes("format")
1434
transport = a_bzrdir.get_branch_transport(None)
1435
format_string = transport.get("format").read()
1502
1436
return klass._formats[format_string]
1503
1437
except errors.NoSuchFile:
1504
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1438
raise errors.NotBranchError(path=transport.base)
1505
1439
except KeyError:
1506
1440
raise errors.UnknownFormatError(format=format_string, kind='branch')
1543
1477
"""Return the short format description for this format."""
1544
1478
raise NotImplementedError(self.get_format_description)
1546
def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1547
lock_type='metadir', set_format=True):
1480
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1548
1482
"""Initialize a branch in a bzrdir, with specified files
1550
1484
:param a_bzrdir: The bzrdir to initialize the branch in
1551
1485
:param utf8_files: The files to create as a list of
1552
1486
(filename, content) tuples
1553
:param name: Name of colocated branch to create, if any
1554
1487
:param set_format: If True, set the format with
1555
1488
self.get_format_string. (BzrBranch4 has its format set
1557
1490
:return: a branch in this format
1559
1492
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1560
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1493
branch_transport = a_bzrdir.get_branch_transport(self)
1562
1495
'metadir': ('lock', lockdir.LockDir),
1563
1496
'branch4': ('branch-lock', lockable_files.TransportLock),
1586
1519
control_files.unlock()
1587
return self.open(a_bzrdir, name, _found=True)
1520
return self.open(a_bzrdir, _found=True)
1589
def initialize(self, a_bzrdir, name=None):
1590
"""Create a branch of this format in a_bzrdir.
1592
:param name: Name of the colocated branch to create.
1522
def initialize(self, a_bzrdir):
1523
"""Create a branch of this format in a_bzrdir."""
1594
1524
raise NotImplementedError(self.initialize)
1596
1526
def is_supported(self):
1627
1557
raise NotImplementedError(self.network_name)
1629
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1559
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1630
1560
"""Return the branch object for a_bzrdir
1632
1562
:param a_bzrdir: A BzrDir that contains a branch.
1633
:param name: Name of colocated branch to open
1634
1563
:param _found: a private parameter, do not use it. It is used to
1635
1564
indicate if format probing has already be done.
1636
1565
:param ignore_fallbacks: when set, no fallback branches will be opened
1744
1673
"multiple hooks installed for transform_fallback_location, "
1745
1674
"all are called with the url returned from the previous hook."
1746
1675
"The order is however undefined.", (1, 9), None))
1747
self.create_hook(HookPoint('automatic_tag_name',
1748
"Called to determine an automatic tag name for a revision."
1749
"automatic_tag_name is called with (branch, revision_id) and "
1750
"should return a tag name or None if no tag name could be "
1751
"determined. The first non-None tag name returned will be used.",
1756
1678
# install the default hooks into the Branch class.
1807
1729
"""See BranchFormat.get_format_description()."""
1808
1730
return "Branch format 4"
1810
def initialize(self, a_bzrdir, name=None):
1732
def initialize(self, a_bzrdir):
1811
1733
"""Create a branch of this format in a_bzrdir."""
1812
1734
utf8_files = [('revision-history', ''),
1813
1735
('branch-name', ''),
1815
return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1737
return self._initialize_helper(a_bzrdir, utf8_files,
1816
1738
lock_type='branch4', set_format=False)
1818
1740
def __init__(self):
1823
1745
"""The network name for this format is the control dirs disk label."""
1824
1746
return self._matchingbzrdir.get_format_string()
1826
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1748
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1827
1749
"""See BranchFormat.open()."""
1829
1751
# we are being called directly and must probe.
1853
1774
return self.get_format_string()
1855
def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1776
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1856
1777
"""See BranchFormat.open()."""
1858
format = BranchFormat.find_format(a_bzrdir, name=name)
1779
format = BranchFormat.find_format(a_bzrdir)
1859
1780
if format.__class__ != self.__class__:
1860
1781
raise AssertionError("wrong format %r found for %r" %
1861
1782
(format, self))
1863
transport = a_bzrdir.get_branch_transport(None, name=name)
1784
transport = a_bzrdir.get_branch_transport(None)
1864
1785
control_files = lockable_files.LockableFiles(transport, 'lock',
1865
1786
lockdir.LockDir)
1866
1787
return self._branch_class()(_format=self,
1867
1788
_control_files=control_files,
1869
1789
a_bzrdir=a_bzrdir,
1870
1790
_repository=a_bzrdir.find_repository(),
1871
1791
ignore_fallbacks=ignore_fallbacks)
1872
1792
except errors.NoSuchFile:
1873
raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1793
raise errors.NotBranchError(path=transport.base)
1875
1795
def __init__(self):
1876
1796
super(BranchFormatMetadir, self).__init__()
1905
1825
"""See BranchFormat.get_format_description()."""
1906
1826
return "Branch format 5"
1908
def initialize(self, a_bzrdir, name=None):
1828
def initialize(self, a_bzrdir):
1909
1829
"""Create a branch of this format in a_bzrdir."""
1910
1830
utf8_files = [('revision-history', ''),
1911
1831
('branch-name', ''),
1913
return self._initialize_helper(a_bzrdir, utf8_files, name)
1833
return self._initialize_helper(a_bzrdir, utf8_files)
1915
1835
def supports_tags(self):
1938
1858
"""See BranchFormat.get_format_description()."""
1939
1859
return "Branch format 6"
1941
def initialize(self, a_bzrdir, name=None):
1861
def initialize(self, a_bzrdir):
1942
1862
"""Create a branch of this format in a_bzrdir."""
1943
1863
utf8_files = [('last-revision', '0 null:\n'),
1944
1864
('branch.conf', ''),
1947
return self._initialize_helper(a_bzrdir, utf8_files, name)
1867
return self._initialize_helper(a_bzrdir, utf8_files)
1949
1869
def make_tags(self, branch):
1950
1870
"""See bzrlib.branch.BranchFormat.make_tags()."""
1968
1888
"""See BranchFormat.get_format_description()."""
1969
1889
return "Branch format 8"
1971
def initialize(self, a_bzrdir, name=None):
1891
def initialize(self, a_bzrdir):
1972
1892
"""Create a branch of this format in a_bzrdir."""
1973
1893
utf8_files = [('last-revision', '0 null:\n'),
1974
1894
('branch.conf', ''),
1976
1896
('references', '')
1978
return self._initialize_helper(a_bzrdir, utf8_files, name)
1898
return self._initialize_helper(a_bzrdir, utf8_files)
1980
1900
def __init__(self):
1981
1901
super(BzrBranchFormat8, self).__init__()
2004
1924
This format was introduced in bzr 1.6.
2007
def initialize(self, a_bzrdir, name=None):
1927
def initialize(self, a_bzrdir):
2008
1928
"""Create a branch of this format in a_bzrdir."""
2009
1929
utf8_files = [('last-revision', '0 null:\n'),
2010
1930
('branch.conf', ''),
2013
return self._initialize_helper(a_bzrdir, utf8_files, name)
1933
return self._initialize_helper(a_bzrdir, utf8_files)
2015
1935
def _branch_class(self):
2016
1936
return BzrBranch7
2051
1971
def get_reference(self, a_bzrdir):
2052
1972
"""See BranchFormat.get_reference()."""
2053
1973
transport = a_bzrdir.get_branch_transport(None)
2054
return transport.get_bytes('location')
1974
return transport.get('location').read()
2056
1976
def set_reference(self, a_bzrdir, to_branch):
2057
1977
"""See BranchFormat.set_reference()."""
2058
1978
transport = a_bzrdir.get_branch_transport(None)
2059
1979
location = transport.put_bytes('location', to_branch.base)
2061
def initialize(self, a_bzrdir, name=None, target_branch=None):
1981
def initialize(self, a_bzrdir, target_branch=None):
2062
1982
"""Create a branch of this format in a_bzrdir."""
2063
1983
if target_branch is None:
2064
1984
# this format does not implement branch itself, thus the implicit
2065
1985
# creation contract must see it as uninitializable
2066
1986
raise errors.UninitializableFormat(self)
2067
1987
mutter('creating branch reference in %s', a_bzrdir.transport.base)
2068
branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1988
branch_transport = a_bzrdir.get_branch_transport(self)
2069
1989
branch_transport.put_bytes('location',
2070
1990
target_branch.bzrdir.root_transport.base)
2071
1991
branch_transport.put_bytes('format', self.get_format_string())
2072
1992
return self.open(
2073
a_bzrdir, name, _found=True,
1993
a_bzrdir, _found=True,
2074
1994
possible_transports=[target_branch.bzrdir.root_transport])
2076
1996
def __init__(self):
2083
2003
def clone(to_bzrdir, revision_id=None,
2084
2004
repository_policy=None):
2085
2005
"""See Branch.clone()."""
2086
return format.initialize(to_bzrdir, target_branch=a_branch)
2006
return format.initialize(to_bzrdir, a_branch)
2087
2007
# cannot obey revision_id limits when cloning a reference ...
2088
2008
# FIXME RBC 20060210 either nuke revision_id for clone, or
2089
2009
# emit some sort of warning/error to the caller ?!
2092
def open(self, a_bzrdir, name=None, _found=False, location=None,
2012
def open(self, a_bzrdir, _found=False, location=None,
2093
2013
possible_transports=None, ignore_fallbacks=False):
2094
2014
"""Return the branch that the branch reference in a_bzrdir points at.
2096
2016
:param a_bzrdir: A BzrDir that contains a branch.
2097
:param name: Name of colocated branch to open, if any
2098
2017
:param _found: a private parameter, do not use it. It is used to
2099
2018
indicate if format probing has already be done.
2100
2019
:param ignore_fallbacks: when set, no fallback branches will be opened
2113
2032
location = self.get_reference(a_bzrdir)
2114
2033
real_bzrdir = bzrdir.BzrDir.open(
2115
2034
location, possible_transports=possible_transports)
2116
result = real_bzrdir.open_branch(name=name,
2117
ignore_fallbacks=ignore_fallbacks)
2035
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2118
2036
# this changes the behaviour of result.clone to create a new reference
2119
2037
# rather than a copy of the content of the branch.
2120
2038
# I did not use a proxy object because that needs much more extensive
2166
2084
:ivar repository: Repository for this branch.
2167
2085
:ivar base: The url of the base directory for this branch; the one
2168
2086
containing the .bzr directory.
2169
:ivar name: Optional colocated branch name as it exists in the control
2173
2089
def __init__(self, _format=None,
2174
_control_files=None, a_bzrdir=None, name=None,
2175
_repository=None, ignore_fallbacks=False):
2090
_control_files=None, a_bzrdir=None, _repository=None,
2091
ignore_fallbacks=False):
2176
2092
"""Create new branch object at a particular location."""
2177
2093
if a_bzrdir is None:
2178
2094
raise ValueError('a_bzrdir must be supplied')
2180
2096
self.bzrdir = a_bzrdir
2181
2097
self._base = self.bzrdir.transport.clone('..').base
2183
2098
# XXX: We should be able to just do
2184
2099
# self.base = self.bzrdir.root_transport.base
2185
2100
# but this does not quite work yet -- mbp 20080522
2212
2124
return self.control_files.is_locked()
2214
2126
def lock_write(self, token=None):
2215
if not self.is_locked():
2216
self._note_lock('w')
2217
2127
# All-in-one needs to always unlock/lock.
2218
2128
repo_control = getattr(self.repository, 'control_files', None)
2219
2129
if self.control_files == repo_control or not self.is_locked():
2220
self.repository._warn_if_deprecated(self)
2221
2130
self.repository.lock_write()
2222
2131
took_lock = True