317
319
The delta is relative to its mainline predecessor, or the
318
320
empty tree for revision 1.
320
assert isinstance(revno, int)
321
322
rh = self.revision_history()
322
323
if not (1 <= revno <= len(rh)):
323
324
raise errors.InvalidRevisionNumber(revno)
324
325
return self.repository.get_revision_delta(rh[revno-1])
326
@deprecated_method(zero_sixteen)
327
def get_root_id(self):
328
"""Return the id of this branches root
327
def get_stacked_on_url(self):
328
"""Get the URL this branch is stacked against.
330
Deprecated: branches don't have root ids-- trees do.
331
Use basis_tree().get_root_id() instead.
330
:raises NotStacked: If the branch is not stacked.
331
:raises UnstackableBranchFormat: If the branch does not support
333
raise NotImplementedError(self.get_root_id)
334
raise NotImplementedError(self.get_stacked_on_url)
335
336
def print_file(self, file, revision_id):
336
337
"""Print `file` to stdout."""
451
470
if stop_revision is None:
452
471
stop_revision = other_len
454
assert isinstance(stop_revision, int)
455
473
if stop_revision > other_len:
456
474
raise errors.NoSuchRevision(self, stop_revision)
457
475
return other_history[self_len:stop_revision]
459
def update_revisions(self, other, stop_revision=None):
478
def update_revisions(self, other, stop_revision=None, overwrite=False,
460
480
"""Pull in new perfect-fit revisions.
462
482
:param other: Another Branch to pull from
463
483
:param stop_revision: Updated until the given revision
484
:param overwrite: Always set the branch pointer, rather than checking
485
to see if it is a proper descendant.
486
:param graph: A Graph object that can be used to query history
487
information. This can be None.
466
raise NotImplementedError(self.update_revisions)
492
other_revno, other_last_revision = other.last_revision_info()
493
stop_revno = None # unknown
494
if stop_revision is None:
495
stop_revision = other_last_revision
496
if _mod_revision.is_null(stop_revision):
497
# if there are no commits, we're done.
499
stop_revno = other_revno
501
# what's the current last revision, before we fetch [and change it
503
last_rev = _mod_revision.ensure_null(self.last_revision())
504
# we fetch here so that we don't process data twice in the common
505
# case of having something to pull, and so that the check for
506
# already merged can operate on the just fetched graph, which will
507
# be cached in memory.
508
self.fetch(other, stop_revision)
509
# Check to see if one is an ancestor of the other
512
graph = self.repository.get_graph()
513
if self._check_if_descendant_or_diverged(
514
stop_revision, last_rev, graph, other):
515
# stop_revision is a descendant of last_rev, but we aren't
516
# overwriting, so we're done.
518
if stop_revno is None:
520
graph = self.repository.get_graph()
521
this_revno, this_last_revision = self.last_revision_info()
522
stop_revno = graph.find_distance_to_null(stop_revision,
523
[(other_last_revision, other_revno),
524
(this_last_revision, this_revno)])
525
self.set_last_revision_info(stop_revno, stop_revision)
468
529
def revision_id_to_revno(self, revision_id):
469
530
"""Given a revision id, return its revno"""
506
567
"""Return `Tree` object for last revision."""
507
568
return self.repository.revision_tree(self.last_revision())
509
def rename_one(self, from_rel, to_rel):
512
This can change the directory or the filename or both.
514
raise NotImplementedError(self.rename_one)
516
def move(self, from_paths, to_name):
519
to_name must exist as a versioned directory.
521
If to_name exists and is a directory, the files are moved into
522
it, keeping their old names. If it is a directory,
524
Note that to_name is only the last component of the new name;
525
this doesn't change the directory.
527
This returns a list of (from_path, to_path) pairs for each
530
raise NotImplementedError(self.move)
532
570
def get_parent(self):
533
571
"""Return the parent location of the branch.
634
672
revision_id: if not None, the revision history in the new branch will
635
673
be truncated to end with revision_id.
637
result = self._format.initialize(to_bzrdir)
675
result = to_bzrdir.create_branch()
638
676
self.copy_content_into(result, revision_id=revision_id)
642
680
def sprout(self, to_bzrdir, revision_id=None):
643
681
"""Create a new line of development from the branch, into to_bzrdir.
683
to_bzrdir controls the branch format.
645
685
revision_id: if not None, the revision history in the new branch will
646
686
be truncated to end with revision_id.
648
result = self._format.initialize(to_bzrdir)
688
result = to_bzrdir.create_branch()
649
689
self.copy_content_into(result, revision_id=revision_id)
650
690
result.set_parent(self.bzrdir.root_transport.base)
704
745
:return: A BranchCheckResult.
706
747
mainline_parent_id = None
707
for revision_id in self.revision_history():
748
last_revno, last_revision_id = self.last_revision_info()
749
real_rev_history = list(self.repository.iter_reverse_revision_history(
751
real_rev_history.reverse()
752
if len(real_rev_history) != last_revno:
753
raise errors.BzrCheckError('revno does not match len(mainline)'
754
' %s != %s' % (last_revno, len(real_rev_history)))
755
# TODO: We should probably also check that real_rev_history actually
756
# matches self.revision_history()
757
for revision_id in real_rev_history:
709
759
revision = self.repository.get_revision(revision_id)
710
760
except errors.NoSuchRevision, e:
795
853
def supports_tags(self):
796
854
return self._format.supports_tags()
856
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
858
"""Ensure that revision_b is a descendant of revision_a.
860
This is a helper function for update_revisions.
862
:raises: DivergedBranches if revision_b has diverged from revision_a.
863
:returns: True if revision_b is a descendant of revision_a.
865
relation = self._revision_relations(revision_a, revision_b, graph)
866
if relation == 'b_descends_from_a':
868
elif relation == 'diverged':
869
raise errors.DivergedBranches(self, other_branch)
870
elif relation == 'a_descends_from_b':
873
raise AssertionError("invalid relation: %r" % (relation,))
875
def _revision_relations(self, revision_a, revision_b, graph):
876
"""Determine the relationship between two revisions.
878
:returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
880
heads = graph.heads([revision_a, revision_b])
881
if heads == set([revision_b]):
882
return 'b_descends_from_a'
883
elif heads == set([revision_a, revision_b]):
884
# These branches have diverged
886
elif heads == set([revision_a]):
887
return 'a_descends_from_b'
889
raise AssertionError("invalid heads: %r" % (heads,))
799
892
class BranchFormat(object):
800
893
"""An encapsulation of the initialization and open routines for a format.
949
1047
"""True if this format supports tags stored in the branch"""
950
1048
return False # by default
952
# XXX: Probably doesn't really belong here -- mbp 20070212
953
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
955
branch_transport = a_bzrdir.get_branch_transport(self)
956
control_files = lockable_files.LockableFiles(branch_transport,
957
lock_filename, lock_class)
958
control_files.create_lock()
959
control_files.lock_write()
961
for filename, content in utf8_files:
962
control_files.put_utf8(filename, content)
964
control_files.unlock()
967
1051
class BranchHooks(Hooks):
968
1052
"""A dictionary mapping hook name to a list of callables for branch hooks.
1025
1109
# local is the local branch or None, master is the target branch,
1026
1110
# and an empty branch recieves new_revno of 0, new_revid of None.
1027
1111
self['post_uncommit'] = []
1113
# Invoked before the tip of a branch changes.
1114
# the api signature is
1115
# (params) where params is a ChangeBranchTipParams with the members
1116
# (branch, old_revno, new_revno, old_revid, new_revid)
1117
self['pre_change_branch_tip'] = []
1119
# Invoked after the tip of a branch changes.
1120
# the api signature is
1121
# (params) where params is a ChangeBranchTipParams with the members
1122
# (branch, old_revno, new_revno, old_revid, new_revid)
1123
self['post_change_branch_tip'] = []
1030
1126
# install the default hooks into the Branch class.
1031
1127
Branch.hooks = BranchHooks()
1130
class ChangeBranchTipParams(object):
1131
"""Object holding parameters passed to *_change_branch_tip hooks.
1133
There are 5 fields that hooks may wish to access:
1135
:ivar branch: the branch being changed
1136
:ivar old_revno: revision number before the change
1137
:ivar new_revno: revision number after the change
1138
:ivar old_revid: revision id before the change
1139
:ivar new_revid: revision id after the change
1141
The revid fields are strings. The revno fields are integers.
1144
def __init__(self, branch, old_revno, new_revno, old_revid, new_revid):
1145
"""Create a group of ChangeBranchTip parameters.
1147
:param branch: The branch being changed.
1148
:param old_revno: Revision number before the change.
1149
:param new_revno: Revision number after the change.
1150
:param old_revid: Tip revision id before the change.
1151
:param new_revid: Tip revision id after the change.
1153
self.branch = branch
1154
self.old_revno = old_revno
1155
self.new_revno = new_revno
1156
self.old_revid = old_revid
1157
self.new_revid = new_revid
1159
def __eq__(self, other):
1160
return self.__dict__ == other.__dict__
1163
return "<%s of %s from (%s, %s) to (%s, %s)>" % (
1164
self.__class__.__name__, self.branch,
1165
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1034
1168
class BzrBranchFormat4(BranchFormat):
1035
1169
"""Bzr branch format 4.
1073
1207
return "Bazaar-NG branch format 4"
1076
class BzrBranchFormat5(BranchFormat):
1210
class BranchFormatMetadir(BranchFormat):
1211
"""Common logic for meta-dir based branch formats."""
1213
def _branch_class(self):
1214
"""What class to instantiate on open calls."""
1215
raise NotImplementedError(self._branch_class)
1217
def open(self, a_bzrdir, _found=False):
1218
"""Return the branch object for a_bzrdir.
1220
_found is a private parameter, do not use it. It is used to indicate
1221
if format probing has already be done.
1224
format = BranchFormat.find_format(a_bzrdir)
1225
if format.__class__ != self.__class__:
1226
raise AssertionError("wrong format %r found for %r" %
1229
transport = a_bzrdir.get_branch_transport(None)
1230
control_files = lockable_files.LockableFiles(transport, 'lock',
1232
return self._branch_class()(_format=self,
1233
_control_files=control_files,
1235
_repository=a_bzrdir.find_repository())
1236
except errors.NoSuchFile:
1237
raise errors.NotBranchError(path=transport.base)
1240
super(BranchFormatMetadir, self).__init__()
1241
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1243
def supports_tags(self):
1247
class BzrBranchFormat5(BranchFormatMetadir):
1077
1248
"""Bzr branch format 5.
1079
1250
This format has:
1102
1276
return self._initialize_helper(a_bzrdir, utf8_files)
1105
super(BzrBranchFormat5, self).__init__()
1106
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1108
def open(self, a_bzrdir, _found=False):
1109
"""Return the branch object for a_bzrdir
1111
_found is a private parameter, do not use it. It is used to indicate
1112
if format probing has already be done.
1115
format = BranchFormat.find_format(a_bzrdir)
1116
assert format.__class__ == self.__class__
1118
transport = a_bzrdir.get_branch_transport(None)
1119
control_files = lockable_files.LockableFiles(transport, 'lock',
1121
return BzrBranch5(_format=self,
1122
_control_files=control_files,
1124
_repository=a_bzrdir.find_repository())
1125
except errors.NoSuchFile:
1126
raise errors.NotBranchError(path=transport.base)
1129
class BzrBranchFormat6(BzrBranchFormat5):
1278
def supports_tags(self):
1282
class BzrBranchFormat6(BranchFormatMetadir):
1130
1283
"""Branch format with last-revision and tags.
1132
1285
Unlike previous formats, this has no explicit revision history. Instead,
1154
1310
return self._initialize_helper(a_bzrdir, utf8_files)
1156
def open(self, a_bzrdir, _found=False):
1157
"""Return the branch object for a_bzrdir
1159
_found is a private parameter, do not use it. It is used to indicate
1160
if format probing has already be done.
1163
format = BranchFormat.find_format(a_bzrdir)
1164
assert format.__class__ == self.__class__
1165
transport = a_bzrdir.get_branch_transport(None)
1166
control_files = lockable_files.LockableFiles(transport, 'lock',
1168
return BzrBranch6(_format=self,
1169
_control_files=control_files,
1171
_repository=a_bzrdir.find_repository())
1173
def supports_tags(self):
1313
class BzrBranchFormat7(BranchFormatMetadir):
1314
"""Branch format with last-revision, tags, and a stacked location pointer.
1316
The stacked location pointer is passed down to the repository and requires
1317
a repository format with supports_external_lookups = True.
1319
This format was introduced in bzr 1.6.
1322
def _branch_class(self):
1325
def get_format_string(self):
1326
"""See BranchFormat.get_format_string()."""
1327
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
1329
def get_format_description(self):
1330
"""See BranchFormat.get_format_description()."""
1331
return "Branch format 7"
1333
def initialize(self, a_bzrdir):
1334
"""Create a branch of this format in a_bzrdir."""
1335
utf8_files = [('last-revision', '0 null:\n'),
1336
('branch.conf', ''),
1339
return self._initialize_helper(a_bzrdir, utf8_files)
1342
super(BzrBranchFormat7, self).__init__()
1343
self._matchingbzrdir.repository_format = \
1344
RepositoryFormatPackDevelopment1Subtree()
1346
def supports_stacking(self):
1276
1453
Note that it's "local" in the context of the filesystem; it doesn't
1277
1454
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1278
1455
it's writable, and can be accessed via the normal filesystem API.
1457
:ivar _transport: Transport for file operations on this branch's
1458
control files, typically pointing to the .bzr/branch directory.
1459
:ivar repository: Repository for this branch.
1460
:ivar base: The url of the base directory for this branch; the one
1461
containing the .bzr directory.
1281
1464
def __init__(self, _format=None,
1282
1465
_control_files=None, a_bzrdir=None, _repository=None):
1283
1466
"""Create new branch object at a particular location."""
1284
Branch.__init__(self)
1285
1467
if a_bzrdir is None:
1286
1468
raise ValueError('a_bzrdir must be supplied')
1288
1470
self.bzrdir = a_bzrdir
1289
# self._transport used to point to the directory containing the
1290
# control directory, but was not used - now it's just the transport
1291
# for the branch control files. mbp 20070212
1292
1471
self._base = self.bzrdir.transport.clone('..').base
1472
# XXX: We should be able to just do
1473
# self.base = self.bzrdir.root_transport.base
1474
# but this does not quite work yet -- mbp 20080522
1293
1475
self._format = _format
1294
1476
if _control_files is None:
1295
1477
raise ValueError('BzrBranch _control_files is None')
1296
1478
self.control_files = _control_files
1297
1479
self._transport = _control_files._transport
1298
1480
self.repository = _repository
1481
Branch.__init__(self)
1300
1483
def __str__(self):
1301
1484
return '%s(%r)' % (self.__class__.__name__, self.base)
1370
1541
This performs the actual writing to disk.
1371
1542
It is intended to be called by BzrBranch5.set_revision_history."""
1372
self.control_files.put_bytes(
1373
'revision-history', '\n'.join(history))
1543
self._transport.put_bytes(
1544
'revision-history', '\n'.join(history),
1545
mode=self.bzrdir._get_file_mode())
1375
1547
@needs_write_lock
1376
1548
def set_revision_history(self, rev_history):
1377
1549
"""See Branch.set_revision_history."""
1378
1550
if 'evil' in debug.debug_flags:
1379
1551
mutter_callsite(3, "set_revision_history scales with history.")
1552
check_not_reserved_id = _mod_revision.check_not_reserved_id
1553
for rev_id in rev_history:
1554
check_not_reserved_id(rev_id)
1555
if Branch.hooks['post_change_branch_tip']:
1556
# Don't calculate the last_revision_info() if there are no hooks
1558
old_revno, old_revid = self.last_revision_info()
1559
if len(rev_history) == 0:
1560
revid = _mod_revision.NULL_REVISION
1562
revid = rev_history[-1]
1563
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
1380
1564
self._write_revision_history(rev_history)
1381
1565
self._clear_cached_state()
1382
1566
self._cache_revision_history(rev_history)
1383
1567
for hook in Branch.hooks['set_rh']:
1384
1568
hook(self, rev_history)
1569
if Branch.hooks['post_change_branch_tip']:
1570
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1572
def _run_pre_change_branch_tip_hooks(self, new_revno, new_revid):
1573
"""Run the pre_change_branch_tip hooks."""
1574
hooks = Branch.hooks['pre_change_branch_tip']
1577
old_revno, old_revid = self.last_revision_info()
1578
params = ChangeBranchTipParams(
1579
self, old_revno, new_revno, old_revid, new_revid)
1583
except errors.TipChangeRejected:
1586
exc_info = sys.exc_info()
1587
hook_name = Branch.hooks.get_hook_name(hook)
1588
raise errors.HookFailed(
1589
'pre_change_branch_tip', hook_name, exc_info)
1591
def _run_post_change_branch_tip_hooks(self, old_revno, old_revid):
1592
"""Run the post_change_branch_tip hooks."""
1593
hooks = Branch.hooks['post_change_branch_tip']
1596
new_revno, new_revid = self.last_revision_info()
1597
params = ChangeBranchTipParams(
1598
self, old_revno, new_revno, old_revid, new_revid)
1386
1602
@needs_write_lock
1387
1603
def set_last_revision_info(self, revno, revision_id):
1388
1604
"""Set the last revision of this branch.
1398
1614
revision_id = _mod_revision.ensure_null(revision_id)
1615
# this old format stores the full history, but this api doesn't
1616
# provide it, so we must generate, and might as well check it's
1399
1618
history = self._lefthand_history(revision_id)
1400
assert len(history) == revno, '%d != %d' % (len(history), revno)
1619
if len(history) != revno:
1620
raise AssertionError('%d != %d' % (len(history), revno))
1401
1621
self.set_revision_history(history)
1403
1623
def _gen_revision_history(self):
1404
history = self.control_files.get('revision-history').read().split('\n')
1624
history = self._transport.get_bytes('revision-history').split('\n')
1405
1625
if history[-1:] == ['']:
1406
1626
# There shouldn't be a trailing newline, but just in case.
1446
1668
self.set_revision_history(self._lefthand_history(revision_id,
1447
1669
last_rev, other_branch))
1450
def update_revisions(self, other, stop_revision=None, overwrite=False):
1451
"""See Branch.update_revisions."""
1454
other_last_revno, other_last_revision = other.last_revision_info()
1455
if stop_revision is None:
1456
stop_revision = other_last_revision
1457
if _mod_revision.is_null(stop_revision):
1458
# if there are no commits, we're done.
1460
# whats the current last revision, before we fetch [and change it
1462
last_rev = _mod_revision.ensure_null(self.last_revision())
1463
# we fetch here so that we don't process data twice in the common
1464
# case of having something to pull, and so that the check for
1465
# already merged can operate on the just fetched graph, which will
1466
# be cached in memory.
1467
self.fetch(other, stop_revision)
1468
# Check to see if one is an ancestor of the other
1470
heads = self.repository.get_graph().heads([stop_revision,
1472
if heads == set([last_rev]):
1473
# The current revision is a decendent of the target,
1476
elif heads == set([stop_revision, last_rev]):
1477
# These branches have diverged
1478
raise errors.DivergedBranches(self, other)
1479
assert heads == set([stop_revision])
1480
if other_last_revision == stop_revision:
1481
self.set_last_revision_info(other_last_revno,
1482
other_last_revision)
1484
# TODO: jam 2007-11-29 Is there a way to determine the
1485
# revno without searching all of history??
1487
self.generate_revision_history(stop_revision)
1489
self.generate_revision_history(stop_revision,
1490
last_rev=last_rev, other_branch=other)
1494
1671
def basis_tree(self):
1495
1672
"""See Branch.basis_tree."""
1496
1673
return self.repository.revision_tree(self.last_revision())
1498
1675
@needs_write_lock
1499
1676
def pull(self, source, overwrite=False, stop_revision=None,
1500
_hook_master=None, run_hooks=True, possible_transports=None):
1677
_hook_master=None, run_hooks=True, possible_transports=None,
1678
_override_hook_target=None):
1501
1679
"""See Branch.pull.
1503
1681
:param _hook_master: Private parameter - set the branch to
1504
be supplied as the master to push hooks.
1682
be supplied as the master to pull hooks.
1505
1683
:param run_hooks: Private parameter - if false, this branch
1506
1684
is being called because it's the master of the primary branch,
1507
1685
so it should not run its hooks.
1686
:param _override_hook_target: Private parameter - set the branch to be
1687
supplied as the target_branch to pull hooks.
1509
1689
result = PullResult()
1510
1690
result.source_branch = source
1511
result.target_branch = self
1691
if _override_hook_target is None:
1692
result.target_branch = self
1694
result.target_branch = _override_hook_target
1512
1695
source.lock_read()
1697
# We assume that during 'pull' the local repository is closer than
1699
graph = self.repository.get_graph(source.repository)
1514
1700
result.old_revno, result.old_revid = self.last_revision_info()
1515
self.update_revisions(source, stop_revision, overwrite=overwrite)
1701
self.update_revisions(source, stop_revision, overwrite=overwrite,
1516
1703
result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
1517
1704
result.new_revno, result.new_revid = self.last_revision_info()
1518
1705
if _hook_master:
1519
1706
result.master_branch = _hook_master
1520
result.local_branch = self
1707
result.local_branch = result.target_branch
1522
result.master_branch = self
1709
result.master_branch = result.target_branch
1523
1710
result.local_branch = None
1525
1712
for hook in Branch.hooks['post_pull']:
1615
1802
result.source_branch = self
1616
1803
result.target_branch = target
1617
1804
result.old_revno, result.old_revid = target.last_revision_info()
1619
target.update_revisions(self, stop_revision)
1620
except errors.DivergedBranches:
1624
target.set_revision_history(self.revision_history())
1806
# We assume that during 'push' this repository is closer than
1808
graph = self.repository.get_graph(target.repository)
1809
target.update_revisions(self, stop_revision, overwrite=overwrite,
1625
1811
result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
1626
1812
result.new_revno, result.new_revid = target.last_revision_info()
1629
1815
def get_parent(self):
1630
1816
"""See Branch.get_parent."""
1632
assert self.base[-1] == '/'
1633
1817
parent = self._get_parent_location()
1634
1818
if parent is None:
1670
1856
def _set_parent_location(self, url):
1671
1857
if url is None:
1672
self.control_files._transport.delete('parent')
1858
self._transport.delete('parent')
1674
assert isinstance(url, str)
1675
self.control_files.put_bytes('parent', url + '\n')
1860
self._transport.put_bytes('parent', url + '\n',
1861
mode=self.bzrdir._get_file_mode())
1863
def set_stacked_on_url(self, url):
1864
raise errors.UnstackableBranchFormat(self._format, self.base)
1678
1867
class BzrBranch5(BzrBranch):
1681
1870
It has support for a master_branch which is the data for bound branches.
1689
super(BzrBranch5, self).__init__(_format=_format,
1690
_control_files=_control_files,
1692
_repository=_repository)
1694
1873
@needs_write_lock
1695
1874
def pull(self, source, overwrite=False, stop_revision=None,
1696
run_hooks=True, possible_transports=None):
1875
run_hooks=True, possible_transports=None,
1876
_override_hook_target=None):
1697
1877
"""Pull from source into self, updating my master if any.
1699
1879
:param run_hooks: Private parameter - if false, this branch
1812
class BzrBranch6(BzrBranch5):
1994
class BzrBranch7(BzrBranch5):
1995
"""A branch with support for a fallback repository."""
1997
def _get_fallback_repository(self, url):
1998
"""Get the repository we fallback to at url."""
1999
url = urlutils.join(self.base, url)
2000
return bzrdir.BzrDir.open(url).open_branch().repository
2002
def _activate_fallback_location(self, url):
2003
"""Activate the branch/repository from url as a fallback repository."""
2004
self.repository.add_fallback_repository(
2005
self._get_fallback_repository(url))
2007
def _open_hook(self):
2009
url = self.get_stacked_on_url()
2010
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2011
errors.UnstackableBranchFormat):
2014
self._activate_fallback_location(url)
2016
def _check_stackable_repo(self):
2017
if not self.repository._format.supports_external_lookups:
2018
raise errors.UnstackableRepositoryFormat(self.repository._format,
2019
self.repository.base)
1814
2021
def __init__(self, *args, **kwargs):
1815
super(BzrBranch6, self).__init__(*args, **kwargs)
2022
super(BzrBranch7, self).__init__(*args, **kwargs)
1816
2023
self._last_revision_info_cache = None
1817
2024
self._partial_revision_history_cache = []
1819
2026
def _clear_cached_state(self):
1820
super(BzrBranch6, self)._clear_cached_state()
2027
super(BzrBranch7, self)._clear_cached_state()
1821
2028
self._last_revision_info_cache = None
1822
2029
self._partial_revision_history_cache = []
1825
def last_revision_info(self):
1826
"""Return information about the last revision.
1828
:return: A tuple (revno, revision_id).
1830
if self._last_revision_info_cache is None:
1831
self._last_revision_info_cache = self._last_revision_info()
1832
return self._last_revision_info_cache
1834
2031
def _last_revision_info(self):
1835
revision_string = self.control_files.get('last-revision').read()
2032
revision_string = self._transport.get_bytes('last-revision')
1836
2033
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1837
2034
revision_id = cache_utf8.get_cached_utf8(revision_id)
1838
2035
revno = int(revno)
1847
2044
Intended to be called by set_last_revision_info and
1848
2045
_write_revision_history.
1850
assert revision_id is not None, "Use NULL_REVISION, not None"
2047
revision_id = _mod_revision.ensure_null(revision_id)
1851
2048
out_string = '%d %s\n' % (revno, revision_id)
1852
self.control_files.put_bytes('last-revision', out_string)
2049
self._transport.put_bytes('last-revision', out_string,
2050
mode=self.bzrdir._get_file_mode())
1854
2052
@needs_write_lock
1855
2053
def set_last_revision_info(self, revno, revision_id):
1856
2054
revision_id = _mod_revision.ensure_null(revision_id)
2055
old_revno, old_revid = self.last_revision_info()
1857
2056
if self._get_append_revisions_only():
1858
2057
self._check_history_violation(revision_id)
2058
self._run_pre_change_branch_tip_hooks(revno, revision_id)
1859
2059
self._write_last_revision_info(revno, revision_id)
1860
2060
self._clear_cached_state()
1861
2061
self._last_revision_info_cache = revno, revision_id
2062
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
1863
2064
def _check_history_violation(self, revision_id):
1864
2065
last_revision = _mod_revision.ensure_null(self.last_revision())
1975
2183
self.get_config().set_user_option('append_revisions_only', value,
1976
2184
warn_masked=True)
2186
def set_stacked_on_url(self, url):
2187
self._check_stackable_repo()
2190
old_url = self.get_stacked_on_url()
2191
except (errors.NotStacked, errors.UnstackableBranchFormat,
2192
errors.UnstackableRepositoryFormat):
2195
# repositories don't offer an interface to remove fallback
2196
# repositories today; take the conceptually simpler option and just
2198
self.repository = self.bzrdir.find_repository()
2199
# for every revision reference the branch has, ensure it is pulled
2201
source_repository = self._get_fallback_repository(old_url)
2202
for revision_id in chain([self.last_revision()],
2203
self.tags.get_reverse_tag_dict()):
2204
self.repository.fetch(source_repository, revision_id,
2207
self._activate_fallback_location(url)
2208
# write this out after the repository is stacked to avoid setting a
2209
# stacked config that doesn't work.
2210
self._set_config_location('stacked_on_location', url)
1978
2212
def _get_append_revisions_only(self):
1979
2213
value = self.get_config().get_user_option('append_revisions_only')
1980
2214
return value == 'True'
2158
2408
new_branch.tags._set_tag_dict({})
2160
2410
# Copying done; now update target format
2161
new_branch.control_files.put_utf8('format',
2162
format.get_format_string())
2411
new_branch._transport.put_bytes('format',
2412
format.get_format_string(),
2413
mode=new_branch.bzrdir._get_file_mode())
2164
2415
# Clean up old files
2165
new_branch.control_files._transport.delete('revision-history')
2416
new_branch._transport.delete('revision-history')
2167
2418
branch.set_parent(None)
2168
2419
except errors.NoSuchFile:
2170
2421
branch.set_bound_location(None)
2424
class Converter6to7(object):
2425
"""Perform an in-place upgrade of format 6 to format 7"""
2427
def convert(self, branch):
2428
format = BzrBranchFormat7()
2429
branch._set_config_location('stacked_on_location', '')
2430
# update target format
2431
branch._transport.put_bytes('format', format.get_format_string())