54
55
NotBranchError, UninitializableFormat,
55
56
UnlistableStore, UnlistableBranch,
58
from bzrlib.hooks import Hooks
57
59
from bzrlib.symbol_versioning import (deprecated_function,
59
61
DEPRECATED_PARAMETER,
61
zero_eight, zero_nine,
63
zero_eight, zero_nine, zero_sixteen,
63
65
from bzrlib.trace import mutter, note
99
101
def __init__(self, *ignored, **ignored_too):
100
102
self.tags = self._make_tags()
103
self._revision_history_cache = None
104
self._revision_id_to_revno_cache = None
102
106
def break_lock(self):
103
107
"""Break a lock if one is present from another instance.
193
197
def get_physical_lock_status(self):
194
198
raise NotImplementedError(self.get_physical_lock_status)
201
def get_revision_id_to_revno_map(self):
202
"""Return the revision_id => dotted revno map.
204
This will be regenerated on demand, but will be cached.
206
:return: A dictionary mapping revision_id => dotted revno.
207
This dictionary should not be modified by the caller.
209
if self._revision_id_to_revno_cache is not None:
210
mapping = self._revision_id_to_revno_cache
212
mapping = self._gen_revno_map()
213
self._cache_revision_id_to_revno(mapping)
214
# TODO: jam 20070417 Since this is being cached, should we be returning
216
# I would rather not, and instead just declare that users should not
217
# modify the return value.
220
def _gen_revno_map(self):
221
"""Create a new mapping from revision ids to dotted revnos.
223
Dotted revnos are generated based on the current tip in the revision
225
This is the worker function for get_revision_id_to_revno_map, which
226
just caches the return value.
228
:return: A dictionary mapping revision_id => dotted revno.
230
last_revision = self.last_revision()
231
revision_graph = self.repository.get_revision_graph(last_revision)
232
merge_sorted_revisions = tsort.merge_sort(
237
revision_id_to_revno = dict((rev_id, revno)
238
for seq_num, rev_id, depth, revno, end_of_merge
239
in merge_sorted_revisions)
240
return revision_id_to_revno
242
def leave_lock_in_place(self):
243
"""Tell this branch object not to release the physical lock when this
246
If lock_write doesn't return a token, then this method is not supported.
248
self.control_files.leave_in_place()
250
def dont_leave_lock_in_place(self):
251
"""Tell this branch object to release the physical lock when this
252
object is unlocked, even if it didn't originally acquire it.
254
If lock_write doesn't return a token, then this method is not supported.
256
self.control_files.dont_leave_in_place()
196
258
def abspath(self, name):
197
259
"""Return absolute filename for something in the branch
296
358
raise InvalidRevisionNumber(revno)
297
359
return self.repository.get_revision_delta(rh[revno-1])
361
@deprecated_method(zero_sixteen)
299
362
def get_root_id(self):
300
"""Return the id of this branches root"""
363
"""Return the id of this branches root
365
Deprecated: branches don't have root ids-- trees do.
366
Use basis_tree().get_root_id() instead.
301
368
raise NotImplementedError(self.get_root_id)
303
370
def print_file(self, file, revision_id):
310
377
def set_revision_history(self, rev_history):
311
378
raise NotImplementedError(self.set_revision_history)
380
def _cache_revision_history(self, rev_history):
381
"""Set the cached revision history to rev_history.
383
The revision_history method will use this cache to avoid regenerating
384
the revision history.
386
This API is semi-public; it only for use by subclasses, all other code
387
should consider it to be private.
389
self._revision_history_cache = rev_history
391
def _cache_revision_id_to_revno(self, revision_id_to_revno):
392
"""Set the cached revision_id => revno map to revision_id_to_revno.
394
This API is semi-public; it only for use by subclasses, all other code
395
should consider it to be private.
397
self._revision_id_to_revno_cache = revision_id_to_revno
399
def _clear_cached_state(self):
400
"""Clear any cached data on this branch, e.g. cached revision history.
402
This means the next call to revision_history will need to call
403
_gen_revision_history.
405
This API is semi-public; it only for use by subclasses, all other code
406
should consider it to be private.
408
self._revision_history_cache = None
409
self._revision_id_to_revno_cache = None
411
def _gen_revision_history(self):
412
"""Return sequence of revision hashes on to this branch.
414
Unlike revision_history, this method always regenerates or rereads the
415
revision history, i.e. it does not cache the result, so repeated calls
418
Concrete subclasses should override this instead of revision_history so
419
that subclasses do not need to deal with caching logic.
421
This API is semi-public; it only for use by subclasses, all other code
422
should consider it to be private.
424
raise NotImplementedError(self._gen_revision_history)
313
427
def revision_history(self):
314
"""Return sequence of revision hashes on to this branch."""
315
raise NotImplementedError(self.revision_history)
428
"""Return sequence of revision hashes on to this branch.
430
This method will cache the revision history for as long as it is safe to
433
if self._revision_history_cache is not None:
434
history = self._revision_history_cache
436
history = self._gen_revision_history()
437
self._cache_revision_history(history)
318
441
"""Return current revision number for this branch.
392
515
return history.index(revision_id) + 1
393
516
except ValueError:
394
raise bzrlib.errors.NoSuchRevision(self, revision_id)
517
raise errors.NoSuchRevision(self, revision_id)
396
519
def get_rev_id(self, revno, history=None):
397
520
"""Find the revision id of the specified revno."""
400
523
if history is None:
401
524
history = self.revision_history()
402
525
if revno <= 0 or revno > len(history):
403
raise bzrlib.errors.NoSuchRevision(self, revno)
526
raise errors.NoSuchRevision(self, revno)
404
527
return history[revno - 1]
406
529
def pull(self, source, overwrite=False, stop_revision=None):
510
633
def get_push_location(self):
511
634
"""Return the None or the location to push this branch to."""
512
raise NotImplementedError(self.get_push_location)
635
push_loc = self.get_config().get_user_option('push_location')
514
638
def set_push_location(self, location):
515
639
"""Set a new push location for this branch."""
543
667
raise InvalidRevisionNumber(revno)
546
def clone(self, *args, **kwargs):
670
def clone(self, to_bzrdir, revision_id=None):
547
671
"""Clone this branch into to_bzrdir preserving all semantic values.
549
673
revision_id: if not None, the revision history in the new branch will
550
674
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')
597
676
result = self._format.initialize(to_bzrdir)
598
677
self.copy_content_into(result, revision_id=revision_id)
683
762
def _get_checkout_format(self):
684
763
"""Return the most suitable metadir for a checkout of this branch.
685
Weaves are used if this branch's repostory uses weaves.
764
Weaves are used if this branch's repository uses weaves.
687
766
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
688
767
from bzrlib.repofmt import weaverepo
788
867
"""Return the current default format."""
789
868
return klass._default_format
870
def get_reference(self, a_bzrdir):
871
"""Get the target reference of the branch in a_bzrdir.
873
format probing must have been completed before calling
874
this method - it is assumed that the format of the branch
875
in a_bzrdir is correct.
877
:param a_bzrdir: The bzrdir to get the branch data from.
878
:return: None if the branch is not a reference branch.
791
882
def get_format_string(self):
792
883
"""Return the ASCII format string that identifies this format."""
793
884
raise NotImplementedError(self.get_format_string)
936
1027
# and an empty branch recieves new_revno of 0, new_revid of None.
937
1028
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)
954
1031
# install the default hooks into the Branch class.
955
1032
Branch.hooks = BranchHooks()
1039
1116
format = BranchFormat.find_format(a_bzrdir)
1040
1117
assert format.__class__ == self.__class__
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())
1119
transport = a_bzrdir.get_branch_transport(None)
1120
control_files = lockable_files.LockableFiles(transport, 'lock',
1122
return BzrBranch5(_format=self,
1123
_control_files=control_files,
1125
_repository=a_bzrdir.find_repository())
1127
raise NotBranchError(path=transport.base)
1050
1130
class BzrBranchFormat6(BzrBranchFormat5):
1114
1194
"""See BranchFormat.get_format_description()."""
1115
1195
return "Checkout reference format 1"
1197
def get_reference(self, a_bzrdir):
1198
"""See BranchFormat.get_reference()."""
1199
transport = a_bzrdir.get_branch_transport(None)
1200
return transport.get('location').read()
1117
1202
def initialize(self, a_bzrdir, target_branch=None):
1118
1203
"""Create a branch of this format in a_bzrdir."""
1119
1204
if target_branch is None:
1141
1226
# emit some sort of warning/error to the caller ?!
1144
def open(self, a_bzrdir, _found=False):
1229
def open(self, a_bzrdir, _found=False, location=None):
1145
1230
"""Return the branch that the branch reference in a_bzrdir points at.
1147
1232
_found is a private parameter, do not use it. It is used to indicate
1151
1236
format = BranchFormat.find_format(a_bzrdir)
1152
1237
assert format.__class__ == self.__class__
1153
transport = a_bzrdir.get_branch_transport(None)
1154
real_bzrdir = bzrdir.BzrDir.open(transport.get('location').read())
1238
if location is None:
1239
location = self.get_reference(a_bzrdir)
1240
real_bzrdir = bzrdir.BzrDir.open(location)
1155
1241
result = real_bzrdir.open_branch()
1156
1242
# this changes the behaviour of result.clone to create a new reference
1157
1243
# rather than a copy of the content of the branch.
1183
1269
it's writable, and can be accessed via the normal filesystem API.
1186
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
1187
relax_version_check=DEPRECATED_PARAMETER, _format=None,
1272
def __init__(self, _format=None,
1188
1273
_control_files=None, a_bzrdir=None, _repository=None):
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.
1274
"""Create new branch object at a particular location."""
1202
1275
Branch.__init__(self)
1203
1276
if a_bzrdir is None:
1204
self.bzrdir = bzrdir.BzrDir.open(transport.base)
1277
raise ValueError('a_bzrdir must be supplied')
1206
1279
self.bzrdir = a_bzrdir
1207
1280
# self._transport used to point to the directory containing the
1213
1286
raise ValueError('BzrBranch _control_files is None')
1214
1287
self.control_files = _control_files
1215
1288
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().",
1242
1289
self.repository = _repository
1244
1291
def __str__(self):
1253
1300
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)
1278
1302
def abspath(self, name):
1279
1303
"""See Branch.abspath."""
1280
1304
return self.control_files._transport.abspath(name)
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)
1307
@deprecated_method(zero_sixteen)
1298
1308
@needs_read_lock
1299
1309
def get_root_id(self):
1300
1310
"""See Branch.get_root_id."""
1304
1314
def is_locked(self):
1305
1315
return self.control_files.is_locked()
1307
def lock_write(self):
1308
self.repository.lock_write()
1317
def lock_write(self, token=None):
1318
repo_token = self.repository.lock_write()
1310
self.control_files.lock_write()
1320
token = self.control_files.lock_write(token=token)
1312
1322
self.repository.unlock()
1315
1326
def lock_read(self):
1316
1327
self.repository.lock_read()
1326
1337
self.control_files.unlock()
1328
1339
self.repository.unlock()
1340
if not self.control_files.is_locked():
1341
# we just released the lock
1342
self._clear_cached_state()
1330
1344
def peek_lock_mode(self):
1331
1345
if self.control_files._lock_count == 0:
1364
1378
def set_revision_history(self, rev_history):
1365
1379
"""See Branch.set_revision_history."""
1366
1380
rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1381
self._clear_cached_state()
1367
1382
self._write_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)
1383
self._cache_revision_history(rev_history)
1381
1384
for hook in Branch.hooks['set_rh']:
1382
1385
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)
1413
1401
def _lefthand_history(self, revision_id, last_rev=None,
1414
1402
other_branch=None):
1415
1403
# stop_revision must be a descendant of last_revision
1589
1577
except errors.InvalidURLJoin, e:
1590
1578
raise errors.InaccessibleParent(parent, self.base)
1592
def get_push_location(self):
1593
"""See Branch.get_push_location."""
1594
push_loc = self.get_config().get_user_option('push_location')
1597
1580
def set_push_location(self, location):
1598
1581
"""See Branch.set_push_location."""
1599
1582
self.get_config().set_user_option(
1614
1597
url = url.encode('ascii')
1615
1598
except UnicodeEncodeError:
1616
raise bzrlib.errors.InvalidURL(url,
1599
raise errors.InvalidURL(url,
1617
1600
"Urls must be 7-bit ascii, "
1618
1601
"use bzrlib.urlutils.escape")
1619
1602
url = urlutils.relative_url(self.base, url)
1833
1816
return "Experimental branch format"
1819
def get_reference(cls, a_bzrdir):
1820
"""Get the target reference of the branch in a_bzrdir.
1822
format probing must have been completed before calling
1823
this method - it is assumed that the format of the branch
1824
in a_bzrdir is correct.
1826
:param a_bzrdir: The bzrdir to get the branch data from.
1827
:return: None if the branch is not a reference branch.
1836
1832
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1838
1834
branch_transport = a_bzrdir.get_branch_transport(cls)
1928
1924
if self._get_append_revisions_only():
1929
1925
self._check_history_violation(revision_id)
1930
1926
self._write_last_revision_info(revno, revision_id)
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)
1927
self._clear_cached_state()
1936
1929
def _check_history_violation(self, revision_id):
1937
1930
last_revision = self.last_revision()
2073
2066
easy to identify.
2076
def __init__(self, transport_server, transport_readonly_server, formats):
2069
def __init__(self, transport_server, transport_readonly_server, formats,
2070
vfs_transport_factory=None):
2077
2071
self._transport_server = transport_server
2078
2072
self._transport_readonly_server = transport_readonly_server
2079
2073
self._formats = formats