202
193
def get_physical_lock_status(self):
203
194
raise NotImplementedError(self.get_physical_lock_status)
206
def get_revision_id_to_revno_map(self):
207
"""Return the revision_id => dotted revno map.
209
This will be regenerated on demand, but will be cached.
211
:return: A dictionary mapping revision_id => dotted revno.
212
This dictionary should not be modified by the caller.
214
if self._revision_id_to_revno_cache is not None:
215
mapping = self._revision_id_to_revno_cache
217
mapping = self._gen_revno_map()
218
self._cache_revision_id_to_revno(mapping)
219
# TODO: jam 20070417 Since this is being cached, should we be returning
221
# I would rather not, and instead just declare that users should not
222
# modify the return value.
225
def _gen_revno_map(self):
226
"""Create a new mapping from revision ids to dotted revnos.
228
Dotted revnos are generated based on the current tip in the revision
230
This is the worker function for get_revision_id_to_revno_map, which
231
just caches the return value.
233
:return: A dictionary mapping revision_id => dotted revno.
235
last_revision = self.last_revision()
236
revision_graph = self.repository.get_revision_graph(last_revision)
237
merge_sorted_revisions = tsort.merge_sort(
242
revision_id_to_revno = dict((rev_id, revno)
243
for seq_num, rev_id, depth, revno, end_of_merge
244
in merge_sorted_revisions)
245
return revision_id_to_revno
247
def leave_lock_in_place(self):
248
"""Tell this branch object not to release the physical lock when this
251
If lock_write doesn't return a token, then this method is not supported.
253
self.control_files.leave_in_place()
255
def dont_leave_lock_in_place(self):
256
"""Tell this branch object to release the physical lock when this
257
object is unlocked, even if it didn't originally acquire it.
259
If lock_write doesn't return a token, then this method is not supported.
261
self.control_files.dont_leave_in_place()
263
196
def abspath(self, name):
264
197
"""Return absolute filename for something in the branch
382
310
def set_revision_history(self, rev_history):
383
311
raise NotImplementedError(self.set_revision_history)
385
def _cache_revision_history(self, rev_history):
386
"""Set the cached revision history to rev_history.
388
The revision_history method will use this cache to avoid regenerating
389
the revision history.
391
This API is semi-public; it only for use by subclasses, all other code
392
should consider it to be private.
394
self._revision_history_cache = rev_history
396
def _cache_revision_id_to_revno(self, revision_id_to_revno):
397
"""Set the cached revision_id => revno map to revision_id_to_revno.
399
This API is semi-public; it only for use by subclasses, all other code
400
should consider it to be private.
402
self._revision_id_to_revno_cache = revision_id_to_revno
404
def _clear_cached_state(self):
405
"""Clear any cached data on this branch, e.g. cached revision history.
407
This means the next call to revision_history will need to call
408
_gen_revision_history.
410
This API is semi-public; it only for use by subclasses, all other code
411
should consider it to be private.
413
self._revision_history_cache = None
414
self._revision_id_to_revno_cache = None
416
def _gen_revision_history(self):
417
"""Return sequence of revision hashes on to this branch.
419
Unlike revision_history, this method always regenerates or rereads the
420
revision history, i.e. it does not cache the result, so repeated calls
423
Concrete subclasses should override this instead of revision_history so
424
that subclasses do not need to deal with caching logic.
426
This API is semi-public; it only for use by subclasses, all other code
427
should consider it to be private.
429
raise NotImplementedError(self._gen_revision_history)
432
313
def revision_history(self):
433
"""Return sequence of revision hashes on to this branch.
435
This method will cache the revision history for as long as it is safe to
438
if self._revision_history_cache is not None:
439
history = self._revision_history_cache
441
history = self._gen_revision_history()
442
self._cache_revision_history(history)
314
"""Return sequence of revision hashes on to this branch."""
315
raise NotImplementedError(self.revision_history)
446
318
"""Return current revision number for this branch.
513
385
def revision_id_to_revno(self, revision_id):
514
386
"""Given a revision id, return its revno"""
515
if _mod_revision.is_null(revision_id):
387
if revision_id is None:
517
389
revision_id = osutils.safe_revision_id(revision_id)
518
390
history = self.revision_history()
520
392
return history.index(revision_id) + 1
521
393
except ValueError:
522
raise errors.NoSuchRevision(self, revision_id)
394
raise bzrlib.errors.NoSuchRevision(self, revision_id)
524
396
def get_rev_id(self, revno, history=None):
525
397
"""Find the revision id of the specified revno."""
673
543
raise InvalidRevisionNumber(revno)
676
def clone(self, to_bzrdir, revision_id=None):
546
def clone(self, *args, **kwargs):
677
547
"""Clone this branch into to_bzrdir preserving all semantic values.
679
549
revision_id: if not None, the revision history in the new branch will
680
550
be truncated to end with revision_id.
552
# for API compatibility, until 0.8 releases we provide the old api:
553
# def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
554
# after 0.8 releases, the *args and **kwargs should be changed:
555
# def clone(self, to_bzrdir, revision_id=None):
556
if (kwargs.get('to_location', None) or
557
kwargs.get('revision', None) or
558
kwargs.get('basis_branch', None) or
559
(len(args) and isinstance(args[0], basestring))):
560
# backwards compatibility api:
561
warn("Branch.clone() has been deprecated for BzrDir.clone() from"
562
" bzrlib 0.8.", DeprecationWarning, stacklevel=3)
565
basis_branch = args[2]
567
basis_branch = kwargs.get('basis_branch', None)
569
basis = basis_branch.bzrdir
574
revision_id = args[1]
576
revision_id = kwargs.get('revision', None)
581
# no default to raise if not provided.
582
url = kwargs.get('to_location')
583
return self.bzrdir.clone(url,
584
revision_id=revision_id,
585
basis=basis).open_branch()
587
# generate args by hand
589
revision_id = args[1]
591
revision_id = kwargs.get('revision_id', None)
595
# no default to raise if not provided.
596
to_bzrdir = kwargs.get('to_bzrdir')
682
597
result = self._format.initialize(to_bzrdir)
683
598
self.copy_content_into(result, revision_id=revision_id)
1038
936
# and an empty branch recieves new_revno of 0, new_revid of None.
1039
937
self['post_uncommit'] = []
939
def install_hook(self, hook_name, a_callable):
940
"""Install a_callable in to the hook hook_name.
942
:param hook_name: A hook name. See the __init__ method of BranchHooks
943
for the complete list of hooks.
944
:param a_callable: The callable to be invoked when the hook triggers.
945
The exact signature will depend on the hook - see the __init__
946
method of BranchHooks for details on each hook.
949
self[hook_name].append(a_callable)
951
raise errors.UnknownHook('branch', hook_name)
1042
954
# install the default hooks into the Branch class.
1043
955
Branch.hooks = BranchHooks()
1127
1039
format = BranchFormat.find_format(a_bzrdir)
1128
1040
assert format.__class__ == self.__class__
1130
transport = a_bzrdir.get_branch_transport(None)
1131
control_files = lockable_files.LockableFiles(transport, 'lock',
1133
return BzrBranch5(_format=self,
1134
_control_files=control_files,
1136
_repository=a_bzrdir.find_repository())
1138
raise NotBranchError(path=transport.base)
1041
transport = a_bzrdir.get_branch_transport(None)
1042
control_files = lockable_files.LockableFiles(transport, 'lock',
1044
return BzrBranch5(_format=self,
1045
_control_files=control_files,
1047
_repository=a_bzrdir.find_repository())
1141
1050
class BzrBranchFormat6(BzrBranchFormat5):
1280
1183
it's writable, and can be accessed via the normal filesystem API.
1283
def __init__(self, _format=None,
1186
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
1187
relax_version_check=DEPRECATED_PARAMETER, _format=None,
1284
1188
_control_files=None, a_bzrdir=None, _repository=None):
1285
"""Create new branch object at a particular location."""
1189
"""Create new branch object at a particular location.
1191
transport -- A Transport object, defining how to access files.
1193
init -- If True, create new control files in a previously
1194
unversioned directory. If False, the branch must already
1197
relax_version_check -- If true, the usual check for the branch
1198
version is not applied. This is intended only for
1199
upgrade/recovery type use; it's not guaranteed that
1200
all operations will work on old format branches.
1286
1202
Branch.__init__(self)
1287
1203
if a_bzrdir is None:
1288
raise ValueError('a_bzrdir must be supplied')
1204
self.bzrdir = bzrdir.BzrDir.open(transport.base)
1290
1206
self.bzrdir = a_bzrdir
1291
1207
# self._transport used to point to the directory containing the
1297
1213
raise ValueError('BzrBranch _control_files is None')
1298
1214
self.control_files = _control_files
1299
1215
self._transport = _control_files._transport
1216
if deprecated_passed(init):
1217
warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
1218
"deprecated as of bzr 0.8. Please use Branch.create().",
1222
# this is slower than before deprecation, oh well never mind.
1223
# -> its deprecated.
1224
self._initialize(transport.base)
1225
self._check_format(_format)
1226
if deprecated_passed(relax_version_check):
1227
warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
1228
"relax_version_check parameter is deprecated as of bzr 0.8. "
1229
"Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
1233
if (not relax_version_check
1234
and not self._format.is_supported()):
1235
raise errors.UnsupportedFormatError(format=fmt)
1236
if deprecated_passed(transport):
1237
warn("BzrBranch.__init__(transport=XXX...): The transport "
1238
"parameter is deprecated as of bzr 0.8. "
1239
"Please use Branch.open, or bzrdir.open_branch().",
1300
1242
self.repository = _repository
1302
1244
def __str__(self):
1311
1253
base = property(_get_base, doc="The URL for the root of this branch.")
1255
def _finish_transaction(self):
1256
"""Exit the current transaction."""
1257
return self.control_files._finish_transaction()
1259
def get_transaction(self):
1260
"""Return the current active transaction.
1262
If no transaction is active, this returns a passthrough object
1263
for which all data is immediately flushed and no caching happens.
1265
# this is an explicit function so that we can do tricky stuff
1266
# when the storage in rev_storage is elsewhere.
1267
# we probably need to hook the two 'lock a location' and
1268
# 'have a transaction' together more delicately, so that
1269
# we can have two locks (branch and storage) and one transaction
1270
# ... and finishing the transaction unlocks both, but unlocking
1271
# does not. - RBC 20051121
1272
return self.control_files.get_transaction()
1274
def _set_transaction(self, transaction):
1275
"""Set a new active transaction."""
1276
return self.control_files._set_transaction(transaction)
1313
1278
def abspath(self, name):
1314
1279
"""See Branch.abspath."""
1315
1280
return self.control_files._transport.abspath(name)
1318
@deprecated_method(zero_sixteen)
1282
def _check_format(self, format):
1283
"""Identify the branch format if needed.
1285
The format is stored as a reference to the format object in
1286
self._format for code that needs to check it later.
1288
The format parameter is either None or the branch format class
1289
used to open this branch.
1291
FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
1294
format = BranchFormat.find_format(self.bzrdir)
1295
self._format = format
1296
mutter("got branch format %s", self._format)
1319
1298
@needs_read_lock
1320
1299
def get_root_id(self):
1321
1300
"""See Branch.get_root_id."""
1389
1364
def set_revision_history(self, rev_history):
1390
1365
"""See Branch.set_revision_history."""
1391
1366
rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1392
self._clear_cached_state()
1393
1367
self._write_revision_history(rev_history)
1394
self._cache_revision_history(rev_history)
1368
transaction = self.get_transaction()
1369
history = transaction.map.find_revision_history()
1370
if history is not None:
1371
# update the revision history in the identity map.
1372
history[:] = list(rev_history)
1373
# this call is disabled because revision_history is
1374
# not really an object yet, and the transaction is for objects.
1375
# transaction.register_dirty(history)
1377
transaction.map.add_revision_history(rev_history)
1378
# this call is disabled because revision_history is
1379
# not really an object yet, and the transaction is for objects.
1380
# transaction.register_clean(history)
1395
1381
for hook in Branch.hooks['set_rh']:
1396
1382
hook(self, rev_history)
1399
def revision_history(self):
1400
"""See Branch.revision_history."""
1401
transaction = self.get_transaction()
1402
history = transaction.map.find_revision_history()
1403
if history is not None:
1404
# mutter("cache hit for revision-history in %s", self)
1405
return list(history)
1406
history = self._gen_revision_history()
1407
transaction.map.add_revision_history(history)
1408
# this call is disabled because revision_history is
1409
# not really an object yet, and the transaction is for objects.
1410
# transaction.register_clean(history, precious=True)
1411
return list(history)
1412
1413
def _lefthand_history(self, revision_id, last_rev=None,
1413
1414
other_branch=None):
1414
1415
# stop_revision must be a descendant of last_revision
1415
1416
stop_graph = self.repository.get_revision_graph(revision_id)
1416
if (last_rev is not None and last_rev != _mod_revision.NULL_REVISION
1417
and last_rev not in stop_graph):
1417
if last_rev is not None and last_rev not in stop_graph:
1418
1418
# our previous tip is not merged into stop_revision
1419
1419
raise errors.DivergedBranches(self, other_branch)
1420
1420
# make a new revision history from the graph
1490
1489
@needs_write_lock
1491
1490
def pull(self, source, overwrite=False, stop_revision=None,
1492
_hook_master=None, run_hooks=True):
1491
_hook_master=None, _run_hooks=True):
1493
1492
"""See Branch.pull.
1495
1494
:param _hook_master: Private parameter - set the branch to
1496
1495
be supplied as the master to push hooks.
1497
:param run_hooks: Private parameter - if false, this branch
1498
is being called because it's the master of the primary branch,
1499
so it should not run its hooks.
1496
:param _run_hooks: Private parameter - allow disabling of
1497
hooks, used when pushing to a master branch.
1501
1499
result = PullResult()
1502
1500
result.source_branch = source
1540
1538
@needs_read_lock
1541
1539
def push(self, target, overwrite=False, stop_revision=None,
1542
_override_hook_source_branch=None):
1540
_hook_master=None, _run_hooks=True):
1543
1541
"""See Branch.push.
1545
This is the basic concrete implementation of push()
1547
:param _override_hook_source_branch: If specified, run
1548
the hooks passing this Branch as the source, rather than self.
1549
This is for use of RemoteBranch, where push is delegated to the
1550
underlying vfs-based Branch.
1552
# TODO: Public option to disable running hooks - should be trivial but
1556
result = self._push_with_bound_branches(target, overwrite,
1558
_override_hook_source_branch=_override_hook_source_branch)
1563
def _push_with_bound_branches(self, target, overwrite,
1565
_override_hook_source_branch=None):
1566
"""Push from self into target, and into target's master if any.
1568
This is on the base BzrBranch class even though it doesn't support
1569
bound branches because the *target* might be bound.
1572
if _override_hook_source_branch:
1573
result.source_branch = _override_hook_source_branch
1574
for hook in Branch.hooks['post_push']:
1577
bound_location = target.get_bound_location()
1578
if bound_location and target.base != bound_location:
1579
# there is a master branch.
1581
# XXX: Why the second check? Is it even supported for a branch to
1582
# be bound to itself? -- mbp 20070507
1583
master_branch = target.get_master_branch()
1584
master_branch.lock_write()
1586
# push into the master from this branch.
1587
self._basic_push(master_branch, overwrite, stop_revision)
1588
# and push into the target branch from this. Note that we push from
1589
# this branch again, because its considered the highest bandwidth
1591
result = self._basic_push(target, overwrite, stop_revision)
1592
result.master_branch = master_branch
1593
result.local_branch = target
1597
master_branch.unlock()
1600
result = self._basic_push(target, overwrite, stop_revision)
1601
# TODO: Why set master_branch and local_branch if there's no
1602
# binding? Maybe cleaner to just leave them unset? -- mbp
1604
result.master_branch = target
1605
result.local_branch = None
1609
def _basic_push(self, target, overwrite, stop_revision):
1610
"""Basic implementation of push without bound branches or hooks.
1612
Must be called with self read locked and target write locked.
1543
:param _hook_master: Private parameter - set the branch to
1544
be supplied as the master to push hooks.
1545
:param _run_hooks: Private parameter - allow disabling of
1546
hooks, used when pushing to a master branch.
1614
1548
result = PushResult()
1615
1549
result.source_branch = self
1616
1550
result.target_branch = target
1617
result.old_revno, result.old_revid = target.last_revision_info()
1619
target.update_revisions(self, stop_revision)
1620
except DivergedBranches:
1624
target.set_revision_history(self.revision_history())
1625
result.tag_conflicts = self.tags.merge_to(target.tags)
1626
result.new_revno, result.new_revid = target.last_revision_info()
1553
result.old_revno, result.old_revid = target.last_revision_info()
1555
target.update_revisions(self, stop_revision)
1556
except DivergedBranches:
1560
target.set_revision_history(self.revision_history())
1561
result.tag_conflicts = self.tags.merge_to(target.tags)
1562
result.new_revno, result.new_revid = target.last_revision_info()
1564
result.master_branch = _hook_master
1565
result.local_branch = target
1567
result.master_branch = target
1568
result.local_branch = None
1570
for hook in Branch.hooks['post_push']:
1629
1576
def get_parent(self):
1700
1652
@needs_write_lock
1701
1653
def pull(self, source, overwrite=False, stop_revision=None,
1703
"""Pull from source into self, updating my master if any.
1655
"""Extends branch.pull to be bound branch aware.
1705
:param run_hooks: Private parameter - if false, this branch
1706
is being called because it's the master of the primary branch,
1707
so it should not run its hooks.
1657
:param _run_hooks: Private parameter used to force hook running
1658
off during bound branch double-pushing.
1709
1660
bound_location = self.get_bound_location()
1710
1661
master_branch = None
1716
1667
if master_branch:
1717
1668
# pull from source into master.
1718
1669
master_branch.pull(source, overwrite, stop_revision,
1720
1671
return super(BzrBranch5, self).pull(source, overwrite,
1721
1672
stop_revision, _hook_master=master_branch,
1722
run_hooks=run_hooks)
1673
_run_hooks=_run_hooks)
1676
master_branch.unlock()
1679
def push(self, target, overwrite=False, stop_revision=None):
1680
"""Updates branch.push to be bound branch aware."""
1681
bound_location = target.get_bound_location()
1682
master_branch = None
1683
if bound_location and target.base != bound_location:
1684
# not pushing to master, so we need to update master.
1685
master_branch = target.get_master_branch()
1686
master_branch.lock_write()
1689
# push into the master from this branch.
1690
super(BzrBranch5, self).push(master_branch, overwrite,
1691
stop_revision, _run_hooks=False)
1692
# and push into the target branch from this. Note that we push from
1693
# this branch again, because its considered the highest bandwidth
1695
return super(BzrBranch5, self).push(target, overwrite,
1696
stop_revision, _hook_master=master_branch)
1724
1698
if master_branch:
1725
1699
master_branch.unlock()
1969
1928
if self._get_append_revisions_only():
1970
1929
self._check_history_violation(revision_id)
1971
1930
self._write_last_revision_info(revno, revision_id)
1972
self._clear_cached_state()
1931
transaction = self.get_transaction()
1932
cached_history = transaction.map.find_revision_history()
1933
if cached_history is not None:
1934
transaction.map.remove_object(cached_history)
1974
1936
def _check_history_violation(self, revision_id):
1975
last_revision = _mod_revision.ensure_null(self.last_revision())
1976
if _mod_revision.is_null(last_revision):
1937
last_revision = self.last_revision()
1938
if last_revision is None:
1978
1940
if last_revision not in self._lefthand_history(revision_id):
1979
1941
raise errors.AppendRevisionsOnlyViolation(self.base)
2096
2057
if revision_id is None:
2097
2058
revno, revision_id = self.last_revision_info()
2099
# To figure out the revno for a random revision, we need to build
2100
# the revision history, and count its length.
2101
# We don't care about the order, just how long it is.
2102
# Alternatively, we could start at the current location, and count
2103
# backwards. But there is no guarantee that we will find it since
2104
# it may be a merged revision.
2105
revno = len(list(self.repository.iter_reverse_revision_history(
2060
revno = self.revision_id_to_revno(revision_id)
2107
2061
destination.set_last_revision_info(revno, revision_id)
2109
2063
def _make_tags(self):
2110
2064
return BasicTags(self)
2067
class BranchTestProviderAdapter(object):
2068
"""A tool to generate a suite testing multiple branch formats at once.
2070
This is done by copying the test once for each transport and injecting
2071
the transport_server, transport_readonly_server, and branch_format
2072
classes into each copy. Each copy is also given a new id() to make it
2076
def __init__(self, transport_server, transport_readonly_server, formats):
2077
self._transport_server = transport_server
2078
self._transport_readonly_server = transport_readonly_server
2079
self._formats = formats
2081
def adapt(self, test):
2082
result = TestSuite()
2083
for branch_format, bzrdir_format in self._formats:
2084
new_test = deepcopy(test)
2085
new_test.transport_server = self._transport_server
2086
new_test.transport_readonly_server = self._transport_readonly_server
2087
new_test.bzrdir_format = bzrdir_format
2088
new_test.branch_format = branch_format
2089
def make_new_test_id():
2090
# the format can be either a class or an instance
2091
name = getattr(branch_format, '__name__',
2092
branch_format.__class__.__name__)
2093
new_id = "%s(%s)" % (new_test.id(), name)
2094
return lambda: new_id
2095
new_test.id = make_new_test_id()
2096
result.addTest(new_test)
2113
2100
######################################################################
2114
2101
# results of operations