195
195
def get_physical_lock_status(self):
196
196
raise NotImplementedError(self.get_physical_lock_status)
199
def get_revision_id_to_revno_map(self):
200
"""Return the revision_id => dotted revno map.
202
This will be regenerated on demand, but will be cached.
204
:return: A dictionary mapping revision_id => dotted revno.
205
This dictionary should not be modified by the caller.
207
if self._revision_id_to_revno_cache is not None:
208
mapping = self._revision_id_to_revno_cache
210
mapping = self._gen_revno_map()
211
self._cache_revision_id_to_revno(mapping)
212
# TODO: jam 20070417 Since this is being cached, should we be returning
214
# I would rather not, and instead just declare that users should not
215
# modify the return value.
218
def _gen_revno_map(self):
219
"""Create a new mapping from revision ids to dotted revnos.
221
Dotted revnos are generated based on the current tip in the revision
223
This is the worker function for get_revision_id_to_revno_map, which
224
just caches the return value.
226
:return: A dictionary mapping revision_id => dotted revno.
228
last_revision = self.last_revision()
229
revision_graph = self.repository.get_revision_graph(last_revision)
230
merge_sorted_revisions = tsort.merge_sort(
235
revision_id_to_revno = dict((rev_id, revno)
236
for seq_num, rev_id, depth, revno, end_of_merge
237
in merge_sorted_revisions)
238
return revision_id_to_revno
240
198
def leave_lock_in_place(self):
241
199
"""Tell this branch object not to release the physical lock when this
242
200
object is unlocked.
506
455
def revision_id_to_revno(self, revision_id):
507
456
"""Given a revision id, return its revno"""
508
if _mod_revision.is_null(revision_id):
457
if revision_id is None:
510
459
revision_id = osutils.safe_revision_id(revision_id)
511
460
history = self.revision_history()
513
462
return history.index(revision_id) + 1
514
463
except ValueError:
515
raise errors.NoSuchRevision(self, revision_id)
464
raise bzrlib.errors.NoSuchRevision(self, revision_id)
517
466
def get_rev_id(self, revno, history=None):
518
467
"""Find the revision id of the specified revno."""
1120
1050
format = BranchFormat.find_format(a_bzrdir)
1121
1051
assert format.__class__ == self.__class__
1123
transport = a_bzrdir.get_branch_transport(None)
1124
control_files = lockable_files.LockableFiles(transport, 'lock',
1126
return BzrBranch5(_format=self,
1127
_control_files=control_files,
1129
_repository=a_bzrdir.find_repository())
1131
raise NotBranchError(path=transport.base)
1052
transport = a_bzrdir.get_branch_transport(None)
1053
control_files = lockable_files.LockableFiles(transport, 'lock',
1055
return BzrBranch5(_format=self,
1056
_control_files=control_files,
1058
_repository=a_bzrdir.find_repository())
1134
1061
class BzrBranchFormat6(BzrBranchFormat5):
1483
1401
@needs_write_lock
1484
1402
def pull(self, source, overwrite=False, stop_revision=None,
1485
_hook_master=None, run_hooks=True):
1403
_hook_master=None, _run_hooks=True):
1486
1404
"""See Branch.pull.
1488
1406
:param _hook_master: Private parameter - set the branch to
1489
1407
be supplied as the master to push hooks.
1490
:param run_hooks: Private parameter - if false, this branch
1491
is being called because it's the master of the primary branch,
1492
so it should not run its hooks.
1408
:param _run_hooks: Private parameter - allow disabling of
1409
hooks, used when pushing to a master branch.
1494
1411
result = PullResult()
1495
1412
result.source_branch = source
1533
1450
@needs_read_lock
1534
1451
def push(self, target, overwrite=False, stop_revision=None,
1535
_override_hook_source_branch=None):
1452
_hook_master=None, _run_hooks=True):
1536
1453
"""See Branch.push.
1538
This is the basic concrete implementation of push()
1540
:param _override_hook_source_branch: If specified, run
1541
the hooks passing this Branch as the source, rather than self.
1542
This is for use of RemoteBranch, where push is delegated to the
1543
underlying vfs-based Branch.
1545
# TODO: Public option to disable running hooks - should be trivial but
1549
result = self._push_with_bound_branches(target, overwrite,
1551
_override_hook_source_branch=_override_hook_source_branch)
1556
def _push_with_bound_branches(self, target, overwrite,
1558
_override_hook_source_branch=None):
1559
"""Push from self into target, and into target's master if any.
1561
This is on the base BzrBranch class even though it doesn't support
1562
bound branches because the *target* might be bound.
1565
if _override_hook_source_branch:
1566
result.source_branch = _override_hook_source_branch
1567
for hook in Branch.hooks['post_push']:
1570
bound_location = target.get_bound_location()
1571
if bound_location and target.base != bound_location:
1572
# there is a master branch.
1574
# XXX: Why the second check? Is it even supported for a branch to
1575
# be bound to itself? -- mbp 20070507
1576
master_branch = target.get_master_branch()
1577
master_branch.lock_write()
1579
# push into the master from this branch.
1580
self._basic_push(master_branch, overwrite, stop_revision)
1581
# and push into the target branch from this. Note that we push from
1582
# this branch again, because its considered the highest bandwidth
1584
result = self._basic_push(target, overwrite, stop_revision)
1585
result.master_branch = master_branch
1586
result.local_branch = target
1590
master_branch.unlock()
1593
result = self._basic_push(target, overwrite, stop_revision)
1594
# TODO: Why set master_branch and local_branch if there's no
1595
# binding? Maybe cleaner to just leave them unset? -- mbp
1597
result.master_branch = target
1598
result.local_branch = None
1602
def _basic_push(self, target, overwrite, stop_revision):
1603
"""Basic implementation of push without bound branches or hooks.
1605
Must be called with self read locked and target write locked.
1455
:param _hook_master: Private parameter - set the branch to
1456
be supplied as the master to push hooks.
1457
:param _run_hooks: Private parameter - allow disabling of
1458
hooks, used when pushing to a master branch.
1607
1460
result = PushResult()
1608
1461
result.source_branch = self
1609
1462
result.target_branch = target
1610
result.old_revno, result.old_revid = target.last_revision_info()
1612
target.update_revisions(self, stop_revision)
1613
except DivergedBranches:
1617
target.set_revision_history(self.revision_history())
1618
result.tag_conflicts = self.tags.merge_to(target.tags)
1619
result.new_revno, result.new_revid = target.last_revision_info()
1465
result.old_revno, result.old_revid = target.last_revision_info()
1467
target.update_revisions(self, stop_revision)
1468
except DivergedBranches:
1472
target.set_revision_history(self.revision_history())
1473
result.tag_conflicts = self.tags.merge_to(target.tags)
1474
result.new_revno, result.new_revid = target.last_revision_info()
1476
result.master_branch = _hook_master
1477
result.local_branch = target
1479
result.master_branch = target
1480
result.local_branch = None
1482
for hook in Branch.hooks['post_push']:
1622
1488
def get_parent(self):
1693
1564
@needs_write_lock
1694
1565
def pull(self, source, overwrite=False, stop_revision=None,
1696
"""Pull from source into self, updating my master if any.
1567
"""Extends branch.pull to be bound branch aware.
1698
:param run_hooks: Private parameter - if false, this branch
1699
is being called because it's the master of the primary branch,
1700
so it should not run its hooks.
1569
:param _run_hooks: Private parameter used to force hook running
1570
off during bound branch double-pushing.
1702
1572
bound_location = self.get_bound_location()
1703
1573
master_branch = None
1709
1579
if master_branch:
1710
1580
# pull from source into master.
1711
1581
master_branch.pull(source, overwrite, stop_revision,
1713
1583
return super(BzrBranch5, self).pull(source, overwrite,
1714
1584
stop_revision, _hook_master=master_branch,
1715
run_hooks=run_hooks)
1585
_run_hooks=_run_hooks)
1588
master_branch.unlock()
1591
def push(self, target, overwrite=False, stop_revision=None):
1592
"""Updates branch.push to be bound branch aware."""
1593
bound_location = target.get_bound_location()
1594
master_branch = None
1595
if bound_location and target.base != bound_location:
1596
# not pushing to master, so we need to update master.
1597
master_branch = target.get_master_branch()
1598
master_branch.lock_write()
1601
# push into the master from this branch.
1602
super(BzrBranch5, self).push(master_branch, overwrite,
1603
stop_revision, _run_hooks=False)
1604
# and push into the target branch from this. Note that we push from
1605
# this branch again, because its considered the highest bandwidth
1607
return super(BzrBranch5, self).push(target, overwrite,
1608
stop_revision, _hook_master=master_branch)
1717
1610
if master_branch:
1718
1611
master_branch.unlock()
2089
1966
if revision_id is None:
2090
1967
revno, revision_id = self.last_revision_info()
2092
# To figure out the revno for a random revision, we need to build
2093
# the revision history, and count its length.
2094
# We don't care about the order, just how long it is.
2095
# Alternatively, we could start at the current location, and count
2096
# backwards. But there is no guarantee that we will find it since
2097
# it may be a merged revision.
2098
revno = len(list(self.repository.iter_reverse_revision_history(
1969
revno = self.revision_id_to_revno(revision_id)
2100
1970
destination.set_last_revision_info(revno, revision_id)
2102
1972
def _make_tags(self):
2103
1973
return BasicTags(self)
1976
class BranchTestProviderAdapter(object):
1977
"""A tool to generate a suite testing multiple branch formats at once.
1979
This is done by copying the test once for each transport and injecting
1980
the transport_server, transport_readonly_server, and branch_format
1981
classes into each copy. Each copy is also given a new id() to make it
1985
def __init__(self, transport_server, transport_readonly_server, formats):
1986
self._transport_server = transport_server
1987
self._transport_readonly_server = transport_readonly_server
1988
self._formats = formats
1990
def adapt(self, test):
1991
result = TestSuite()
1992
for branch_format, bzrdir_format in self._formats:
1993
new_test = deepcopy(test)
1994
new_test.transport_server = self._transport_server
1995
new_test.transport_readonly_server = self._transport_readonly_server
1996
new_test.bzrdir_format = bzrdir_format
1997
new_test.branch_format = branch_format
1998
def make_new_test_id():
1999
# the format can be either a class or an instance
2000
name = getattr(branch_format, '__name__',
2001
branch_format.__class__.__name__)
2002
new_id = "%s(%s)" % (new_test.id(), name)
2003
return lambda: new_id
2004
new_test.id = make_new_test_id()
2005
result.addTest(new_test)
2106
2009
######################################################################
2107
2010
# results of operations