50
55
NotBranchError, UninitializableFormat,
51
56
UnlistableStore, UnlistableBranch,
58
from bzrlib.hooks import Hooks
53
59
from bzrlib.symbol_versioning import (deprecated_function,
55
61
DEPRECATED_PARAMETER,
57
zero_eight, zero_nine,
63
zero_eight, zero_nine, zero_sixteen,
59
65
from bzrlib.trace import mutter, note
62
68
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
63
69
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
64
BZR_BRANCH_FORMAT_6 = "Bazaar-NG branch, format 6\n"
70
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
67
73
# TODO: Maybe include checks for common corruption of newlines, etc?
185
197
def get_physical_lock_status(self):
186
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()
188
258
def abspath(self, name):
189
259
"""Return absolute filename for something in the branch
302
377
def set_revision_history(self, rev_history):
303
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)
305
427
def revision_history(self):
306
"""Return sequence of revision hashes on to this branch."""
307
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)
310
441
"""Return current revision number for this branch.
391
523
if history is None:
392
524
history = self.revision_history()
393
525
if revno <= 0 or revno > len(history):
394
raise bzrlib.errors.NoSuchRevision(self, revno)
526
raise errors.NoSuchRevision(self, revno)
395
527
return history[revno - 1]
397
529
def pull(self, source, overwrite=False, stop_revision=None):
398
530
"""Mirror source into this branch.
400
532
This branch is considered to be 'local', having low latency.
534
:returns: PullResult instance
402
536
raise NotImplementedError(self.pull)
445
579
raise NotImplementedError(self.get_parent)
581
def _set_config_location(self, name, url, config=None,
582
make_relative=False):
584
config = self.get_config()
588
url = urlutils.relative_url(self.base, url)
589
config.set_user_option(name, url)
591
def _get_config_location(self, name, config=None):
593
config = self.get_config()
594
location = config.get_user_option(name)
447
599
def get_submit_branch(self):
448
600
"""Return the submit location of the branch.
463
615
self.get_config().set_user_option('submit_branch', location)
617
def get_public_branch(self):
618
"""Return the public location of the branch.
620
This is is used by merge directives.
622
return self._get_config_location('public_branch')
624
def set_public_branch(self, location):
625
"""Return the submit location of the branch.
627
This is the default location for bundle. The usual
628
pattern is that the user can override it by specifying a
631
self._set_config_location('public_branch', location)
465
633
def get_push_location(self):
466
634
"""Return the None or the location to push this branch to."""
467
raise NotImplementedError(self.get_push_location)
635
push_loc = self.get_config().get_user_option('push_location')
469
638
def set_push_location(self, location):
470
639
"""Set a new push location for this branch."""
498
667
raise InvalidRevisionNumber(revno)
501
def clone(self, *args, **kwargs):
670
def clone(self, to_bzrdir, revision_id=None):
502
671
"""Clone this branch into to_bzrdir preserving all semantic values.
504
673
revision_id: if not None, the revision history in the new branch will
505
674
be truncated to end with revision_id.
507
# for API compatibility, until 0.8 releases we provide the old api:
508
# def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
509
# after 0.8 releases, the *args and **kwargs should be changed:
510
# def clone(self, to_bzrdir, revision_id=None):
511
if (kwargs.get('to_location', None) or
512
kwargs.get('revision', None) or
513
kwargs.get('basis_branch', None) or
514
(len(args) and isinstance(args[0], basestring))):
515
# backwards compatibility api:
516
warn("Branch.clone() has been deprecated for BzrDir.clone() from"
517
" bzrlib 0.8.", DeprecationWarning, stacklevel=3)
520
basis_branch = args[2]
522
basis_branch = kwargs.get('basis_branch', None)
524
basis = basis_branch.bzrdir
529
revision_id = args[1]
531
revision_id = kwargs.get('revision', None)
536
# no default to raise if not provided.
537
url = kwargs.get('to_location')
538
return self.bzrdir.clone(url,
539
revision_id=revision_id,
540
basis=basis).open_branch()
542
# generate args by hand
544
revision_id = args[1]
546
revision_id = kwargs.get('revision_id', None)
550
# no default to raise if not provided.
551
to_bzrdir = kwargs.get('to_bzrdir')
552
676
result = self._format.initialize(to_bzrdir)
553
677
self.copy_content_into(result, revision_id=revision_id)
636
762
def _get_checkout_format(self):
637
763
"""Return the most suitable metadir for a checkout of this branch.
638
Weaves are used if this branch's repostory uses weaves.
764
Weaves are used if this branch's repository uses weaves.
640
766
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
641
767
from bzrlib.repofmt import weaverepo
642
768
format = bzrdir.BzrDirMetaFormat1()
643
769
format.repository_format = weaverepo.RepositoryFormat7()
645
format = self.repository.bzrdir.cloning_metadir()
646
format.branch_format = self._format
771
format = self.repository.bzrdir.checkout_metadir()
772
format.set_branch_format(self._format)
649
775
def create_checkout(self, to_location, revision_id=None,
673
797
# pull up to the specified revision_id to set the initial
674
798
# branch tip correctly, and seed it with history.
675
799
checkout_branch.pull(self, stop_revision=revision_id)
676
return checkout.create_workingtree(revision_id)
800
tree = checkout.create_workingtree(revision_id)
801
basis_tree = tree.basis_tree()
802
basis_tree.lock_read()
804
for path, file_id in basis_tree.iter_references():
805
reference_parent = self.reference_parent(file_id, path)
806
reference_parent.create_checkout(tree.abspath(path),
807
basis_tree.get_reference_revision(file_id, path),
813
def reference_parent(self, file_id, path):
814
"""Return the parent branch for a tree-reference file_id
815
:param file_id: The file_id of the tree reference
816
:param path: The path of the file_id in the tree
817
:return: A branch associated with the file_id
819
# FIXME should provide multiple branches, based on config
820
return Branch.open(self.bzrdir.root_transport.clone(path).base)
822
def supports_tags(self):
823
return self._format.supports_tags()
679
826
class BranchFormat(object):
794
959
def __str__(self):
795
960
return self.get_format_string().rstrip()
798
class BranchHooks(dict):
962
def supports_tags(self):
963
"""True if this format supports tags stored in the branch"""
964
return False # by default
966
# XXX: Probably doesn't really belong here -- mbp 20070212
967
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
969
branch_transport = a_bzrdir.get_branch_transport(self)
970
control_files = lockable_files.LockableFiles(branch_transport,
971
lock_filename, lock_class)
972
control_files.create_lock()
973
control_files.lock_write()
975
for filename, content in utf8_files:
976
control_files.put_utf8(filename, content)
978
control_files.unlock()
981
class BranchHooks(Hooks):
799
982
"""A dictionary mapping hook name to a list of callables for branch hooks.
801
984
e.g. ['set_rh'] Is the list of items to be called when the
817
1000
self['set_rh'] = []
818
1001
# invoked after a push operation completes.
819
1002
# the api signature is
1004
# containing the members
820
1005
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
821
# where local is the local branch or None, master is the target
1006
# where local is the local target branch or None, master is the target
822
1007
# master branch, and the rest should be self explanatory. The source
823
1008
# is read locked and the target branches write locked. Source will
824
1009
# be the local low-latency branch.
825
1010
self['post_push'] = []
826
1011
# invoked after a pull operation completes.
827
1012
# the api signature is
1014
# containing the members
828
1015
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
829
1016
# where local is the local branch or None, master is the target
830
1017
# master branch, and the rest should be self explanatory. The source
843
1030
# and an empty branch recieves new_revno of 0, new_revid of None.
844
1031
self['post_uncommit'] = []
846
def install_hook(self, hook_name, a_callable):
847
"""Install a_callable in to the hook hook_name.
849
:param hook_name: A hook name. See the __init__ method of BranchHooks
850
for the complete list of hooks.
851
:param a_callable: The callable to be invoked when the hook triggers.
852
The exact signature will depend on the hook - see the __init__
853
method of BranchHooks for details on each hook.
856
self[hook_name].append(a_callable)
858
raise errors.UnknownHook('branch', hook_name)
861
1034
# install the default hooks into the Branch class.
862
1035
Branch.hooks = BranchHooks()
946
1119
format = BranchFormat.find_format(a_bzrdir)
947
1120
assert format.__class__ == self.__class__
948
transport = a_bzrdir.get_branch_transport(None)
949
control_files = lockable_files.LockableFiles(transport, 'lock',
951
return BzrBranch5(_format=self,
952
_control_files=control_files,
954
_repository=a_bzrdir.find_repository())
957
return "Bazaar-NG Metadir branch format 5"
1122
transport = a_bzrdir.get_branch_transport(None)
1123
control_files = lockable_files.LockableFiles(transport, 'lock',
1125
return BzrBranch5(_format=self,
1126
_control_files=control_files,
1128
_repository=a_bzrdir.find_repository())
1130
raise NotBranchError(path=transport.base)
960
1133
class BzrBranchFormat6(BzrBranchFormat5):
1089
1272
it's writable, and can be accessed via the normal filesystem API.
1092
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
1093
relax_version_check=DEPRECATED_PARAMETER, _format=None,
1275
def __init__(self, _format=None,
1094
1276
_control_files=None, a_bzrdir=None, _repository=None):
1095
"""Create new branch object at a particular location.
1097
transport -- A Transport object, defining how to access files.
1099
init -- If True, create new control files in a previously
1100
unversioned directory. If False, the branch must already
1103
relax_version_check -- If true, the usual check for the branch
1104
version is not applied. This is intended only for
1105
upgrade/recovery type use; it's not guaranteed that
1106
all operations will work on old format branches.
1277
"""Create new branch object at a particular location."""
1278
Branch.__init__(self)
1108
1279
if a_bzrdir is None:
1109
self.bzrdir = bzrdir.BzrDir.open(transport.base)
1280
raise ValueError('a_bzrdir must be supplied')
1111
1282
self.bzrdir = a_bzrdir
1112
self._transport = self.bzrdir.transport.clone('..')
1113
self._base = self._transport.base
1283
# self._transport used to point to the directory containing the
1284
# control directory, but was not used - now it's just the transport
1285
# for the branch control files. mbp 20070212
1286
self._base = self.bzrdir.transport.clone('..').base
1114
1287
self._format = _format
1115
1288
if _control_files is None:
1116
1289
raise ValueError('BzrBranch _control_files is None')
1117
1290
self.control_files = _control_files
1118
if deprecated_passed(init):
1119
warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
1120
"deprecated as of bzr 0.8. Please use Branch.create().",
1124
# this is slower than before deprecation, oh well never mind.
1125
# -> its deprecated.
1126
self._initialize(transport.base)
1127
self._check_format(_format)
1128
if deprecated_passed(relax_version_check):
1129
warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
1130
"relax_version_check parameter is deprecated as of bzr 0.8. "
1131
"Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
1135
if (not relax_version_check
1136
and not self._format.is_supported()):
1137
raise errors.UnsupportedFormatError(format=fmt)
1138
if deprecated_passed(transport):
1139
warn("BzrBranch.__init__(transport=XXX...): The transport "
1140
"parameter is deprecated as of bzr 0.8. "
1141
"Please use Branch.open, or bzrdir.open_branch().",
1291
self._transport = _control_files._transport
1144
1292
self.repository = _repository
1146
1294
def __str__(self):
1149
1297
__repr__ = __str__
1151
1299
def _get_base(self):
1300
"""Returns the directory containing the control directory."""
1152
1301
return self._base
1154
1303
base = property(_get_base, doc="The URL for the root of this branch.")
1156
def _finish_transaction(self):
1157
"""Exit the current transaction."""
1158
return self.control_files._finish_transaction()
1160
def get_transaction(self):
1161
"""Return the current active transaction.
1163
If no transaction is active, this returns a passthrough object
1164
for which all data is immediately flushed and no caching happens.
1166
# this is an explicit function so that we can do tricky stuff
1167
# when the storage in rev_storage is elsewhere.
1168
# we probably need to hook the two 'lock a location' and
1169
# 'have a transaction' together more delicately, so that
1170
# we can have two locks (branch and storage) and one transaction
1171
# ... and finishing the transaction unlocks both, but unlocking
1172
# does not. - RBC 20051121
1173
return self.control_files.get_transaction()
1175
def _set_transaction(self, transaction):
1176
"""Set a new active transaction."""
1177
return self.control_files._set_transaction(transaction)
1179
1305
def abspath(self, name):
1180
1306
"""See Branch.abspath."""
1181
1307
return self.control_files._transport.abspath(name)
1183
def _check_format(self, format):
1184
"""Identify the branch format if needed.
1186
The format is stored as a reference to the format object in
1187
self._format for code that needs to check it later.
1189
The format parameter is either None or the branch format class
1190
used to open this branch.
1192
FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
1195
format = BranchFormat.find_format(self.bzrdir)
1196
self._format = format
1197
mutter("got branch format %s", self._format)
1310
@deprecated_method(zero_sixteen)
1199
1311
@needs_read_lock
1200
1312
def get_root_id(self):
1201
1313
"""See Branch.get_root_id."""
1258
1375
This performs the actual writing to disk.
1259
1376
It is intended to be called by BzrBranch5.set_revision_history."""
1260
self.control_files.put_utf8(
1377
self.control_files.put_bytes(
1261
1378
'revision-history', '\n'.join(history))
1263
1380
@needs_write_lock
1264
1381
def set_revision_history(self, rev_history):
1265
1382
"""See Branch.set_revision_history."""
1383
rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1384
self._clear_cached_state()
1266
1385
self._write_revision_history(rev_history)
1267
transaction = self.get_transaction()
1268
history = transaction.map.find_revision_history()
1269
if history is not None:
1270
# update the revision history in the identity map.
1271
history[:] = list(rev_history)
1272
# this call is disabled because revision_history is
1273
# not really an object yet, and the transaction is for objects.
1274
# transaction.register_dirty(history)
1276
transaction.map.add_revision_history(rev_history)
1277
# this call is disabled because revision_history is
1278
# not really an object yet, and the transaction is for objects.
1279
# transaction.register_clean(history)
1386
self._cache_revision_history(rev_history)
1280
1387
for hook in Branch.hooks['set_rh']:
1281
1388
hook(self, rev_history)
1283
1390
@needs_write_lock
1284
1391
def set_last_revision_info(self, revno, revision_id):
1392
revision_id = osutils.safe_revision_id(revision_id)
1285
1393
history = self._lefthand_history(revision_id)
1286
1394
assert len(history) == revno, '%d != %d' % (len(history), revno)
1287
1395
self.set_revision_history(history)
1289
1397
def _gen_revision_history(self):
1290
decode_utf8 = cache_utf8.decode
1291
history = [decode_utf8(l.rstrip('\r\n')) for l in
1292
self.control_files.get('revision-history').readlines()]
1398
history = self.control_files.get('revision-history').read().split('\n')
1399
if history[-1:] == ['']:
1400
# There shouldn't be a trailing newline, but just in case.
1296
def revision_history(self):
1297
"""See Branch.revision_history."""
1298
transaction = self.get_transaction()
1299
history = transaction.map.find_revision_history()
1300
if history is not None:
1301
# mutter("cache hit for revision-history in %s", self)
1302
return list(history)
1303
history = self._gen_revision_history()
1304
transaction.map.add_revision_history(history)
1305
# this call is disabled because revision_history is
1306
# not really an object yet, and the transaction is for objects.
1307
# transaction.register_clean(history, precious=True)
1308
return list(history)
1310
1404
def _lefthand_history(self, revision_id, last_rev=None,
1311
1405
other_branch=None):
1312
1406
# stop_revision must be a descendant of last_revision
1351
1446
if stop_revision is None:
1352
1447
# if there are no commits, we're done.
1450
stop_revision = osutils.safe_revision_id(stop_revision)
1354
1451
# whats the current last revision, before we fetch [and change it
1356
1453
last_rev = self.last_revision()
1357
1454
# we fetch here regardless of whether we need to so that we pickup
1358
1455
# filled in ghosts.
1359
1456
self.fetch(other, stop_revision)
1360
my_ancestry = self.repository.get_ancestry(last_rev)
1457
my_ancestry = self.repository.get_ancestry(last_rev,
1361
1459
if stop_revision in my_ancestry:
1362
1460
# last_revision is a descendant of stop_revision
1383
1481
@needs_write_lock
1384
1482
def pull(self, source, overwrite=False, stop_revision=None,
1385
_hook_master=None, _run_hooks=True):
1483
_hook_master=None, run_hooks=True):
1386
1484
"""See Branch.pull.
1388
1486
:param _hook_master: Private parameter - set the branch to
1389
1487
be supplied as the master to push hooks.
1390
:param _run_hooks: Private parameter - allow disabling of
1391
hooks, used when pushing to a master branch.
1488
:param run_hooks: Private parameter - if false, this branch
1489
is being called because it's the master of the primary branch,
1490
so it should not run its hooks.
1492
result = PullResult()
1493
result.source_branch = source
1494
result.target_branch = self
1393
1495
source.lock_read()
1395
old_count, old_tip = self.last_revision_info()
1497
result.old_revno, result.old_revid = self.last_revision_info()
1397
1499
self.update_revisions(source, stop_revision)
1398
1500
except DivergedBranches:
1399
1501
if not overwrite:
1402
self.set_revision_history(source.revision_history())
1403
new_count, new_tip = self.last_revision_info()
1504
if stop_revision is None:
1505
stop_revision = source.last_revision()
1506
self.generate_revision_history(stop_revision)
1507
result.tag_conflicts = source.tags.merge_to(self.tags)
1508
result.new_revno, result.new_revid = self.last_revision_info()
1510
result.master_branch = _hook_master
1511
result.local_branch = self
1513
result.master_branch = self
1514
result.local_branch = None
1410
1516
for hook in Branch.hooks['post_pull']:
1411
hook(source, _hook_local, _hook_master, old_count, old_tip,
1413
return new_count - old_count
1415
1519
source.unlock()
1417
1522
def _get_parent_location(self):
1418
1523
_locs = ['parent', 'pull', 'x-pull']
1426
1531
@needs_read_lock
1427
1532
def push(self, target, overwrite=False, stop_revision=None,
1428
_hook_master=None, _run_hooks=True):
1533
_override_hook_source_branch=None):
1429
1534
"""See Branch.push.
1431
:param _hook_master: Private parameter - set the branch to
1432
be supplied as the master to push hooks.
1433
:param _run_hooks: Private parameter - allow disabling of
1434
hooks, used when pushing to a master branch.
1536
This is the basic concrete implementation of push()
1538
:param _override_hook_source_branch: If specified, run
1539
the hooks passing this Branch as the source, rather than self.
1540
This is for use of RemoteBranch, where push is delegated to the
1541
underlying vfs-based Branch.
1543
# TODO: Public option to disable running hooks - should be trivial but
1436
1545
target.lock_write()
1438
old_count, old_tip = target.last_revision_info()
1440
target.update_revisions(self, stop_revision)
1441
except DivergedBranches:
1445
target.set_revision_history(self.revision_history())
1446
new_count, new_tip = target.last_revision_info()
1449
_hook_local = target
1451
_hook_master = target
1453
for hook in Branch.hooks['post_push']:
1454
hook(self, _hook_local, _hook_master, old_count, old_tip,
1456
return new_count - old_count
1547
result = self._push_with_bound_branches(target, overwrite,
1549
_override_hook_source_branch=_override_hook_source_branch)
1458
1552
target.unlock()
1554
def _push_with_bound_branches(self, target, overwrite,
1556
_override_hook_source_branch=None):
1557
"""Push from self into target, and into target's master if any.
1559
This is on the base BzrBranch class even though it doesn't support
1560
bound branches because the *target* might be bound.
1563
if _override_hook_source_branch:
1564
result.source_branch = _override_hook_source_branch
1565
for hook in Branch.hooks['post_push']:
1568
bound_location = target.get_bound_location()
1569
if bound_location and target.base != bound_location:
1570
# there is a master branch.
1572
# XXX: Why the second check? Is it even supported for a branch to
1573
# be bound to itself? -- mbp 20070507
1574
master_branch = target.get_master_branch()
1575
master_branch.lock_write()
1577
# push into the master from this branch.
1578
self._basic_push(master_branch, overwrite, stop_revision)
1579
# and push into the target branch from this. Note that we push from
1580
# this branch again, because its considered the highest bandwidth
1582
result = self._basic_push(target, overwrite, stop_revision)
1583
result.master_branch = master_branch
1584
result.local_branch = target
1588
master_branch.unlock()
1591
result = self._basic_push(target, overwrite, stop_revision)
1592
# TODO: Why set master_branch and local_branch if there's no
1593
# binding? Maybe cleaner to just leave them unset? -- mbp
1595
result.master_branch = target
1596
result.local_branch = None
1600
def _basic_push(self, target, overwrite, stop_revision):
1601
"""Basic implementation of push without bound branches or hooks.
1603
Must be called with self read locked and target write locked.
1605
result = PushResult()
1606
result.source_branch = self
1607
result.target_branch = target
1608
result.old_revno, result.old_revid = target.last_revision_info()
1610
target.update_revisions(self, stop_revision)
1611
except DivergedBranches:
1615
target.set_revision_history(self.revision_history())
1616
result.tag_conflicts = self.tags.merge_to(target.tags)
1617
result.new_revno, result.new_revid = target.last_revision_info()
1460
1620
def get_parent(self):
1461
1621
"""See Branch.get_parent."""
1537
1691
@needs_write_lock
1538
1692
def pull(self, source, overwrite=False, stop_revision=None,
1540
"""Extends branch.pull to be bound branch aware.
1694
"""Pull from source into self, updating my master if any.
1542
:param _run_hooks: Private parameter used to force hook running
1543
off during bound branch double-pushing.
1696
:param run_hooks: Private parameter - if false, this branch
1697
is being called because it's the master of the primary branch,
1698
so it should not run its hooks.
1545
1700
bound_location = self.get_bound_location()
1546
1701
master_branch = None
1552
1707
if master_branch:
1553
1708
# pull from source into master.
1554
1709
master_branch.pull(source, overwrite, stop_revision,
1556
1711
return super(BzrBranch5, self).pull(source, overwrite,
1557
1712
stop_revision, _hook_master=master_branch,
1558
_run_hooks=_run_hooks)
1561
master_branch.unlock()
1564
def push(self, target, overwrite=False, stop_revision=None):
1565
"""Updates branch.push to be bound branch aware."""
1566
bound_location = target.get_bound_location()
1567
master_branch = None
1568
if bound_location and target.base != bound_location:
1569
# not pushing to master, so we need to update master.
1570
master_branch = target.get_master_branch()
1571
master_branch.lock_write()
1574
# push into the master from this branch.
1575
super(BzrBranch5, self).push(master_branch, overwrite,
1576
stop_revision, _run_hooks=False)
1577
# and push into the target branch from this. Note that we push from
1578
# this branch again, because its considered the highest bandwidth
1580
return super(BzrBranch5, self).push(target, overwrite,
1581
stop_revision, _hook_master=master_branch)
1713
run_hooks=run_hooks)
1583
1715
if master_branch:
1584
1716
master_branch.unlock()
1682
1814
if master is not None:
1683
1815
old_tip = self.last_revision()
1684
1816
self.pull(master, overwrite=True)
1685
if old_tip in self.repository.get_ancestry(self.last_revision()):
1817
if old_tip in self.repository.get_ancestry(self.last_revision(),
1824
class BzrBranchExperimental(BzrBranch5):
1825
"""Bzr experimental branch format
1828
- a revision-history file.
1830
- a lock dir guarding the branch itself
1831
- all of this stored in a branch/ subdirectory
1832
- works with shared repositories.
1833
- a tag dictionary in the branch
1835
This format is new in bzr 0.15, but shouldn't be used for real data,
1838
This class acts as it's own BranchFormat.
1841
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1844
def get_format_string(cls):
1845
"""See BranchFormat.get_format_string()."""
1846
return "Bazaar-NG branch format experimental\n"
1849
def get_format_description(cls):
1850
"""See BranchFormat.get_format_description()."""
1851
return "Experimental branch format"
1854
def get_reference(cls, a_bzrdir):
1855
"""Get the target reference of the branch in a_bzrdir.
1857
format probing must have been completed before calling
1858
this method - it is assumed that the format of the branch
1859
in a_bzrdir is correct.
1861
:param a_bzrdir: The bzrdir to get the branch data from.
1862
:return: None if the branch is not a reference branch.
1867
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1869
branch_transport = a_bzrdir.get_branch_transport(cls)
1870
control_files = lockable_files.LockableFiles(branch_transport,
1871
lock_filename, lock_class)
1872
control_files.create_lock()
1873
control_files.lock_write()
1875
for filename, content in utf8_files:
1876
control_files.put_utf8(filename, content)
1878
control_files.unlock()
1881
def initialize(cls, a_bzrdir):
1882
"""Create a branch of this format in a_bzrdir."""
1883
utf8_files = [('format', cls.get_format_string()),
1884
('revision-history', ''),
1885
('branch-name', ''),
1888
cls._initialize_control_files(a_bzrdir, utf8_files,
1889
'lock', lockdir.LockDir)
1890
return cls.open(a_bzrdir, _found=True)
1893
def open(cls, a_bzrdir, _found=False):
1894
"""Return the branch object for a_bzrdir
1896
_found is a private parameter, do not use it. It is used to indicate
1897
if format probing has already be done.
1900
format = BranchFormat.find_format(a_bzrdir)
1901
assert format.__class__ == cls
1902
transport = a_bzrdir.get_branch_transport(None)
1903
control_files = lockable_files.LockableFiles(transport, 'lock',
1905
return cls(_format=cls,
1906
_control_files=control_files,
1908
_repository=a_bzrdir.find_repository())
1911
def is_supported(cls):
1914
def _make_tags(self):
1915
return BasicTags(self)
1918
def supports_tags(cls):
1922
BranchFormat.register_format(BzrBranchExperimental)
1691
1925
class BzrBranch6(BzrBranch5):
1693
1927
@needs_read_lock
1694
1928
def last_revision_info(self):
1695
revision_string = self.control_files.get_utf8('last-revision').read()
1929
revision_string = self.control_files.get('last-revision').read()
1696
1930
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1931
revision_id = cache_utf8.get_cached_utf8(revision_id)
1697
1932
revno = int(revno)
1698
1933
return revno, revision_id
1716
1951
if revision_id is None:
1717
1952
revision_id = 'null:'
1718
1953
out_string = '%d %s\n' % (revno, revision_id)
1719
self.control_files.put_utf8('last-revision', out_string)
1954
self.control_files.put_bytes('last-revision', out_string)
1721
1956
@needs_write_lock
1722
1957
def set_last_revision_info(self, revno, revision_id):
1958
revision_id = osutils.safe_revision_id(revision_id)
1723
1959
if self._get_append_revisions_only():
1724
1960
self._check_history_violation(revision_id)
1725
1961
self._write_last_revision_info(revno, revision_id)
1726
transaction = self.get_transaction()
1727
cached_history = transaction.map.find_revision_history()
1728
if cached_history is not None:
1729
transaction.map.remove_object(cached_history)
1962
self._clear_cached_state()
1731
1964
def _check_history_violation(self, revision_id):
1732
1965
last_revision = self.last_revision()
1777
2011
self.set_last_revision_info(prev_revno + len(revision_ids),
1778
2012
revision_ids[-1])
1780
def _set_config_location(self, name, url, config=None,
1781
make_relative=False):
1783
config = self.get_config()
1787
url = urlutils.relative_url(self.base, url)
1788
config.set_user_option(name, url)
1791
def _get_config_location(self, name, config=None):
1793
config = self.get_config()
1794
location = config.get_user_option(name)
1799
2014
@needs_write_lock
1800
2015
def _set_parent_location(self, url):
1801
2016
"""Set the parent branch"""
1870
2085
if revision_id is None:
1871
2086
revno, revision_id = self.last_revision_info()
1873
revno = self.revision_id_to_revno(revision_id)
2088
# To figure out the revno for a random revision, we need to build
2089
# the revision history, and count its length.
2090
# We don't care about the order, just how long it is.
2091
# Alternatively, we could start at the current location, and count
2092
# backwards. But there is no guarantee that we will find it since
2093
# it may be a merged revision.
2094
revno = len(list(self.repository.iter_reverse_revision_history(
1874
2096
destination.set_last_revision_info(revno, revision_id)
2098
def _make_tags(self):
2099
return BasicTags(self)
1877
2102
class BranchTestProviderAdapter(object):
1878
2103
"""A tool to generate a suite testing multiple branch formats at once.
1897
2123
new_test.bzrdir_format = bzrdir_format
1898
2124
new_test.branch_format = branch_format
1899
2125
def make_new_test_id():
1900
new_id = "%s(%s)" % (new_test.id(), branch_format.__class__.__name__)
2126
# the format can be either a class or an instance
2127
name = getattr(branch_format, '__name__',
2128
branch_format.__class__.__name__)
2129
new_id = "%s(%s)" % (new_test.id(), name)
1901
2130
return lambda: new_id
1902
2131
new_test.id = make_new_test_id()
1903
2132
result.addTest(new_test)
2136
######################################################################
2137
# results of operations
2140
class _Result(object):
2142
def _show_tag_conficts(self, to_file):
2143
if not getattr(self, 'tag_conflicts', None):
2145
to_file.write('Conflicting tags:\n')
2146
for name, value1, value2 in self.tag_conflicts:
2147
to_file.write(' %s\n' % (name, ))
2150
class PullResult(_Result):
2151
"""Result of a Branch.pull operation.
2153
:ivar old_revno: Revision number before pull.
2154
:ivar new_revno: Revision number after pull.
2155
:ivar old_revid: Tip revision id before pull.
2156
:ivar new_revid: Tip revision id after pull.
2157
:ivar source_branch: Source (local) branch object.
2158
:ivar master_branch: Master branch of the target, or None.
2159
:ivar target_branch: Target/destination branch object.
2163
# DEPRECATED: pull used to return the change in revno
2164
return self.new_revno - self.old_revno
2166
def report(self, to_file):
2167
if self.old_revid == self.new_revid:
2168
to_file.write('No revisions to pull.\n')
2170
to_file.write('Now on revision %d.\n' % self.new_revno)
2171
self._show_tag_conficts(to_file)
2174
class PushResult(_Result):
2175
"""Result of a Branch.push operation.
2177
:ivar old_revno: Revision number before push.
2178
:ivar new_revno: Revision number after push.
2179
:ivar old_revid: Tip revision id before push.
2180
:ivar new_revid: Tip revision id after push.
2181
:ivar source_branch: Source branch object.
2182
:ivar master_branch: Master branch of the target, or None.
2183
:ivar target_branch: Target/destination branch object.
2187
# DEPRECATED: push used to return the change in revno
2188
return self.new_revno - self.old_revno
2190
def report(self, to_file):
2191
"""Write a human-readable description of the result."""
2192
if self.old_revid == self.new_revid:
2193
to_file.write('No new revisions to push.\n')
2195
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2196
self._show_tag_conficts(to_file)
1907
2199
class BranchCheckResult(object):
1908
2200
"""Results of checking branch consistency.