15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
20
from bzrlib.lazy_import import lazy_import
19
21
lazy_import(globals(), """
22
from itertools import chain
20
23
from bzrlib import (
86
90
self._revision_history_cache = None
87
91
self._revision_id_to_revno_cache = None
88
92
self._last_revision_info_cache = None
96
"""Called by init to allow simpler extension of the base class."""
90
98
def break_lock(self):
91
99
"""Break a lock if one is present from another instance.
325
333
raise errors.InvalidRevisionNumber(revno)
326
334
return self.repository.get_revision_delta(rh[revno-1])
336
def get_stacked_on_url(self):
337
"""Get the URL this branch is stacked against.
339
:raises NotStacked: If the branch is not stacked.
340
:raises UnstackableBranchFormat: If the branch does not support
343
raise NotImplementedError(self.get_stacked_on_url)
328
345
def print_file(self, file, revision_id):
329
346
"""Print `file` to stdout."""
330
347
raise NotImplementedError(self.print_file)
332
349
def set_revision_history(self, rev_history):
333
350
raise NotImplementedError(self.set_revision_history)
352
def set_stacked_on_url(self, url):
353
"""Set the URL this branch is stacked against.
355
:raises UnstackableBranchFormat: If the branch does not support
357
:raises UnstackableRepositoryFormat: If the repository does not support
360
raise NotImplementedError(self.set_stacked_on_url)
335
362
def _cache_revision_history(self, rev_history):
336
363
"""Set the cached revision history to rev_history.
874
899
elif relation == 'a_descends_from_b':
877
raise AssertionError("invalid heads: %r" % heads)
902
raise AssertionError("invalid relation: %r" % (relation,))
879
904
def _revision_relations(self, revision_a, revision_b, graph):
880
905
"""Determine the relationship between two revisions.
890
915
elif heads == set([revision_a]):
891
916
return 'a_descends_from_b'
893
raise AssertionError("invalid heads: %r" % heads)
918
raise AssertionError("invalid heads: %r" % (heads,))
896
921
class BranchFormat(object):
1036
1061
def set_default_format(klass, format):
1037
1062
klass._default_format = format
1064
def supports_stacking(self):
1065
"""True if this format records a stacked-on branch."""
1040
1069
def unregister_format(klass, format):
1041
1070
del klass._formats[format.get_format_string()]
1109
1138
# local is the local branch or None, master is the target branch,
1110
1139
# and an empty branch recieves new_revno of 0, new_revid of None.
1111
1140
self['post_uncommit'] = []
1142
# Invoked before the tip of a branch changes.
1143
# the api signature is
1144
# (params) where params is a ChangeBranchTipParams with the members
1145
# (branch, old_revno, new_revno, old_revid, new_revid)
1146
self['pre_change_branch_tip'] = []
1112
1147
# Introduced in 1.4
1113
1148
# Invoked after the tip of a branch changes.
1114
1149
# the api signature is
1150
1185
self.old_revid = old_revid
1151
1186
self.new_revid = new_revid
1188
def __eq__(self, other):
1189
return self.__dict__ == other.__dict__
1192
return "<%s of %s from (%s, %s) to (%s, %s)>" % (
1193
self.__class__.__name__, self.branch,
1194
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1154
1197
class BzrBranchFormat4(BranchFormat):
1155
1198
"""Bzr branch format 4.
1193
1236
return "Bazaar-NG branch format 4"
1196
class BzrBranchFormat5(BranchFormat):
1239
class BranchFormatMetadir(BranchFormat):
1240
"""Common logic for meta-dir based branch formats."""
1242
def _branch_class(self):
1243
"""What class to instantiate on open calls."""
1244
raise NotImplementedError(self._branch_class)
1246
def open(self, a_bzrdir, _found=False):
1247
"""Return the branch object for a_bzrdir.
1249
_found is a private parameter, do not use it. It is used to indicate
1250
if format probing has already be done.
1253
format = BranchFormat.find_format(a_bzrdir)
1254
if format.__class__ != self.__class__:
1255
raise AssertionError("wrong format %r found for %r" %
1258
transport = a_bzrdir.get_branch_transport(None)
1259
control_files = lockable_files.LockableFiles(transport, 'lock',
1261
return self._branch_class()(_format=self,
1262
_control_files=control_files,
1264
_repository=a_bzrdir.find_repository())
1265
except errors.NoSuchFile:
1266
raise errors.NotBranchError(path=transport.base)
1269
super(BranchFormatMetadir, self).__init__()
1270
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1272
def supports_tags(self):
1276
class BzrBranchFormat5(BranchFormatMetadir):
1197
1277
"""Bzr branch format 5.
1199
1279
This format has:
1222
1305
return self._initialize_helper(a_bzrdir, utf8_files)
1225
super(BzrBranchFormat5, self).__init__()
1226
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1228
def open(self, a_bzrdir, _found=False):
1229
"""Return the branch object for a_bzrdir
1231
_found is a private parameter, do not use it. It is used to indicate
1232
if format probing has already be done.
1235
format = BranchFormat.find_format(a_bzrdir)
1236
if format.__class__ != self.__class__:
1237
raise AssertionError("wrong format %r found for %r" %
1240
transport = a_bzrdir.get_branch_transport(None)
1241
control_files = lockable_files.LockableFiles(transport, 'lock',
1243
return BzrBranch5(_format=self,
1244
_control_files=control_files,
1246
_repository=a_bzrdir.find_repository())
1247
except errors.NoSuchFile:
1248
raise errors.NotBranchError(path=transport.base)
1251
class BzrBranchFormat6(BzrBranchFormat5):
1307
def supports_tags(self):
1311
class BzrBranchFormat6(BranchFormatMetadir):
1252
1312
"""Branch format with last-revision and tags.
1254
1314
Unlike previous formats, this has no explicit revision history. Instead,
1276
1339
return self._initialize_helper(a_bzrdir, utf8_files)
1278
def open(self, a_bzrdir, _found=False):
1279
"""Return the branch object for a_bzrdir
1281
_found is a private parameter, do not use it. It is used to indicate
1282
if format probing has already be done.
1285
format = BranchFormat.find_format(a_bzrdir)
1286
if format.__class__ != self.__class__:
1287
raise AssertionError("wrong format %r found for %r" %
1289
transport = a_bzrdir.get_branch_transport(None)
1290
control_files = lockable_files.LockableFiles(transport, 'lock',
1292
return BzrBranch6(_format=self,
1293
_control_files=control_files,
1295
_repository=a_bzrdir.find_repository())
1297
def supports_tags(self):
1342
class BzrBranchFormat7(BranchFormatMetadir):
1343
"""Branch format with last-revision, tags, and a stacked location pointer.
1345
The stacked location pointer is passed down to the repository and requires
1346
a repository format with supports_external_lookups = True.
1348
This format was introduced in bzr 1.6.
1351
def _branch_class(self):
1354
def get_format_string(self):
1355
"""See BranchFormat.get_format_string()."""
1356
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
1358
def get_format_description(self):
1359
"""See BranchFormat.get_format_description()."""
1360
return "Branch format 7"
1362
def initialize(self, a_bzrdir):
1363
"""Create a branch of this format in a_bzrdir."""
1364
utf8_files = [('last-revision', '0 null:\n'),
1365
('branch.conf', ''),
1368
return self._initialize_helper(a_bzrdir, utf8_files)
1371
super(BzrBranchFormat7, self).__init__()
1372
self._matchingbzrdir.repository_format = \
1373
RepositoryFormatPackDevelopment1Subtree()
1375
def supports_stacking(self):
1389
1467
# and not independently creatable, so are not registered.
1390
1468
__format5 = BzrBranchFormat5()
1391
1469
__format6 = BzrBranchFormat6()
1470
__format7 = BzrBranchFormat7()
1392
1471
BranchFormat.register_format(__format5)
1393
1472
BranchFormat.register_format(BranchReferenceFormat())
1394
1473
BranchFormat.register_format(__format6)
1474
BranchFormat.register_format(__format7)
1395
1475
BranchFormat.set_default_format(__format6)
1396
1476
_legacy_formats = [BzrBranchFormat4(),
1413
1493
def __init__(self, _format=None,
1414
1494
_control_files=None, a_bzrdir=None, _repository=None):
1415
1495
"""Create new branch object at a particular location."""
1416
Branch.__init__(self)
1417
1496
if a_bzrdir is None:
1418
1497
raise ValueError('a_bzrdir must be supplied')
1428
1507
self.control_files = _control_files
1429
1508
self._transport = _control_files._transport
1430
1509
self.repository = _repository
1510
Branch.__init__(self)
1432
1512
def __str__(self):
1433
1513
return '%s(%r)' % (self.__class__.__name__, self.base)
1506
1586
check_not_reserved_id = _mod_revision.check_not_reserved_id
1507
1587
for rev_id in rev_history:
1508
1588
check_not_reserved_id(rev_id)
1589
if Branch.hooks['post_change_branch_tip']:
1590
# Don't calculate the last_revision_info() if there are no hooks
1592
old_revno, old_revid = self.last_revision_info()
1593
if len(rev_history) == 0:
1594
revid = _mod_revision.NULL_REVISION
1596
revid = rev_history[-1]
1597
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
1509
1598
self._write_revision_history(rev_history)
1510
1599
self._clear_cached_state()
1511
1600
self._cache_revision_history(rev_history)
1512
1601
for hook in Branch.hooks['set_rh']:
1513
1602
hook(self, rev_history)
1603
if Branch.hooks['post_change_branch_tip']:
1604
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1606
def _run_pre_change_branch_tip_hooks(self, new_revno, new_revid):
1607
"""Run the pre_change_branch_tip hooks."""
1608
hooks = Branch.hooks['pre_change_branch_tip']
1611
old_revno, old_revid = self.last_revision_info()
1612
params = ChangeBranchTipParams(
1613
self, old_revno, new_revno, old_revid, new_revid)
1617
except errors.TipChangeRejected:
1620
exc_info = sys.exc_info()
1621
hook_name = Branch.hooks.get_hook_name(hook)
1622
raise errors.HookFailed(
1623
'pre_change_branch_tip', hook_name, exc_info)
1515
1625
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
1516
1626
"""Run the post_change_branch_tip hooks."""
1517
1627
hooks = Branch.hooks['post_change_branch_tip']
1538
1648
revision_id = _mod_revision.ensure_null(revision_id)
1539
old_revno, old_revid = self.last_revision_info()
1540
1649
# this old format stores the full history, but this api doesn't
1541
1650
# provide it, so we must generate, and might as well check it's
1544
1653
if len(history) != revno:
1545
1654
raise AssertionError('%d != %d' % (len(history), revno))
1546
1655
self.set_revision_history(history)
1547
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1549
1657
def _gen_revision_history(self):
1550
1658
history = self._transport.get_bytes('revision-history').split('\n')
1752
1860
except errors.InvalidURLJoin, e:
1753
1861
raise errors.InaccessibleParent(parent, self.base)
1863
def get_stacked_on_url(self):
1864
raise errors.UnstackableBranchFormat(self._format, self.base)
1755
1866
def set_push_location(self, location):
1756
1867
"""See Branch.set_push_location."""
1757
1868
self.get_config().set_user_option(
1783
1894
self._transport.put_bytes('parent', url + '\n',
1784
1895
mode=self.bzrdir._get_file_mode())
1897
def set_stacked_on_url(self, url):
1898
raise errors.UnstackableBranchFormat(self._format, self.base)
1787
1901
class BzrBranch5(BzrBranch):
1788
1902
"""A format 5 branch. This supports new features over plain branches.
1790
1904
It has support for a master_branch which is the data for bound branches.
1798
super(BzrBranch5, self).__init__(_format=_format,
1799
_control_files=_control_files,
1801
_repository=_repository)
1803
1907
@needs_write_lock
1804
1908
def pull(self, source, overwrite=False, stop_revision=None,
1805
1909
run_hooks=True, possible_transports=None,
1924
class BzrBranch6(BzrBranch5):
2028
class BzrBranch7(BzrBranch5):
2029
"""A branch with support for a fallback repository."""
2031
def _get_fallback_repository(self, url):
2032
"""Get the repository we fallback to at url."""
2033
url = urlutils.join(self.base, url)
2034
return bzrdir.BzrDir.open(url).open_branch().repository
2036
def _activate_fallback_location(self, url):
2037
"""Activate the branch/repository from url as a fallback repository."""
2038
self.repository.add_fallback_repository(
2039
self._get_fallback_repository(url))
2041
def _open_hook(self):
2043
url = self.get_stacked_on_url()
2044
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2045
errors.UnstackableBranchFormat):
2048
self._activate_fallback_location(url)
2050
def _check_stackable_repo(self):
2051
if not self.repository._format.supports_external_lookups:
2052
raise errors.UnstackableRepositoryFormat(self.repository._format,
2053
self.repository.base)
1926
2055
def __init__(self, *args, **kwargs):
1927
super(BzrBranch6, self).__init__(*args, **kwargs)
2056
super(BzrBranch7, self).__init__(*args, **kwargs)
2057
self._last_revision_info_cache = None
1928
2058
self._partial_revision_history_cache = []
1930
2060
def _clear_cached_state(self):
1931
super(BzrBranch6, self)._clear_cached_state()
2061
super(BzrBranch7, self)._clear_cached_state()
2062
self._last_revision_info_cache = None
1932
2063
self._partial_revision_history_cache = []
1934
2065
def _last_revision_info(self):
1958
2089
old_revno, old_revid = self.last_revision_info()
1959
2090
if self._get_append_revisions_only():
1960
2091
self._check_history_violation(revision_id)
2092
self._run_pre_change_branch_tip_hooks(revno, revision_id)
1961
2093
self._write_last_revision_info(revno, revision_id)
1962
2094
self._clear_cached_state()
1963
2095
self._last_revision_info_cache = revno, revision_id
2070
2202
"""See Branch.get_old_bound_location"""
2071
2203
return self._get_bound_location(False)
2205
def get_stacked_on_url(self):
2206
self._check_stackable_repo()
2207
stacked_url = self._get_config_location('stacked_on_location')
2208
if stacked_url is None:
2209
raise errors.NotStacked(self)
2073
2212
def set_append_revisions_only(self, enabled):
2078
2217
self.get_config().set_user_option('append_revisions_only', value,
2079
2218
warn_masked=True)
2220
def set_stacked_on_url(self, url):
2221
self._check_stackable_repo()
2224
old_url = self.get_stacked_on_url()
2225
except (errors.NotStacked, errors.UnstackableBranchFormat,
2226
errors.UnstackableRepositoryFormat):
2229
# repositories don't offer an interface to remove fallback
2230
# repositories today; take the conceptually simpler option and just
2232
self.repository = self.bzrdir.find_repository()
2233
# for every revision reference the branch has, ensure it is pulled
2235
source_repository = self._get_fallback_repository(old_url)
2236
for revision_id in chain([self.last_revision()],
2237
self.tags.get_reverse_tag_dict()):
2238
self.repository.fetch(source_repository, revision_id,
2241
self._activate_fallback_location(url)
2242
# write this out after the repository is stacked to avoid setting a
2243
# stacked config that doesn't work.
2244
self._set_config_location('stacked_on_location', url)
2081
2246
def _get_append_revisions_only(self):
2082
2247
value = self.get_config().get_user_option('append_revisions_only')
2083
2248
return value == 'True'
2158
2323
return self.revno() - index
2326
class BzrBranch6(BzrBranch7):
2327
"""See BzrBranchFormat6 for the capabilities of this branch.
2329
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2333
def get_stacked_on_url(self):
2334
raise errors.UnstackableBranchFormat(self._format, self.base)
2336
def set_stacked_on_url(self, url):
2337
raise errors.UnstackableBranchFormat(self._format, self.base)
2161
2340
######################################################################
2162
2341
# results of operations
2274
2453
except errors.NoSuchFile:
2276
2455
branch.set_bound_location(None)
2458
class Converter6to7(object):
2459
"""Perform an in-place upgrade of format 6 to format 7"""
2461
def convert(self, branch):
2462
format = BzrBranchFormat7()
2463
branch._set_config_location('stacked_on_location', '')
2464
# update target format
2465
branch._transport.put_bytes('format', format.get_format_string())