485
441
* end_of_merge: When True the next node (earlier in history) is
486
442
part of a different merge.
488
with self.lock_read():
489
# Note: depth and revno values are in the context of the branch so
490
# we need the full graph to get stable numbers, regardless of the
492
if self._merge_sorted_revisions_cache is None:
493
last_revision = self.last_revision()
494
known_graph = self.repository.get_known_graph_ancestry(
496
self._merge_sorted_revisions_cache = known_graph.merge_sort(
498
filtered = self._filter_merge_sorted_revisions(
499
self._merge_sorted_revisions_cache, start_revision_id,
500
stop_revision_id, stop_rule)
501
# Make sure we don't return revisions that are not part of the
502
# start_revision_id ancestry.
503
filtered = self._filter_start_non_ancestors(filtered)
504
if direction == 'reverse':
506
if direction == 'forward':
507
return reversed(list(filtered))
509
raise ValueError('invalid direction %r' % direction)
444
# Note: depth and revno values are in the context of the branch so
445
# we need the full graph to get stable numbers, regardless of the
447
if self._merge_sorted_revisions_cache is None:
448
last_revision = self.last_revision()
449
last_key = (last_revision,)
450
known_graph = self.repository.revisions.get_known_graph_ancestry(
452
self._merge_sorted_revisions_cache = known_graph.merge_sort(
454
filtered = self._filter_merge_sorted_revisions(
455
self._merge_sorted_revisions_cache, start_revision_id,
456
stop_revision_id, stop_rule)
457
if direction == 'reverse':
459
if direction == 'forward':
460
return reversed(list(filtered))
462
raise ValueError('invalid direction %r' % direction)
511
464
def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
512
start_revision_id, stop_revision_id,
465
start_revision_id, stop_revision_id, stop_rule):
514
466
"""Iterate over an inclusive range of sorted revisions."""
515
467
rev_iter = iter(merge_sorted_revisions)
516
468
if start_revision_id is not None:
517
469
for node in rev_iter:
470
rev_id = node.key[-1]
519
471
if rev_id != start_revision_id:
522
474
# The decision to include the start or not
523
475
# depends on the stop_rule if a stop is provided
524
476
# so pop this node back into the iterator
525
rev_iter = itertools.chain(iter([node]), rev_iter)
477
rev_iter = chain(iter([node]), rev_iter)
527
479
if stop_revision_id is None:
528
480
# Yield everything
529
481
for node in rev_iter:
482
rev_id = node.key[-1]
531
483
yield (rev_id, node.merge_depth, node.revno,
532
484
node.end_of_merge)
533
485
elif stop_rule == 'exclude':
534
486
for node in rev_iter:
487
rev_id = node.key[-1]
536
488
if rev_id == stop_revision_id:
538
490
yield (rev_id, node.merge_depth, node.revno,
539
491
node.end_of_merge)
540
492
elif stop_rule == 'include':
541
493
for node in rev_iter:
494
rev_id = node.key[-1]
543
495
yield (rev_id, node.merge_depth, node.revno,
544
496
node.end_of_merge)
545
497
if rev_id == stop_revision_id:
547
elif stop_rule == 'with-merges-without-common-ancestry':
548
# We want to exclude all revisions that are already part of the
549
# stop_revision_id ancestry.
550
graph = self.repository.get_graph()
551
ancestors = graph.find_unique_ancestors(start_revision_id,
553
for node in rev_iter:
555
if rev_id not in ancestors:
557
yield (rev_id, node.merge_depth, node.revno,
559
499
elif stop_rule == 'with-merges':
560
500
stop_rev = self.repository.get_revision(stop_revision_id)
561
501
if stop_rev.parent_ids:
562
502
left_parent = stop_rev.parent_ids[0]
564
504
left_parent = _mod_revision.NULL_REVISION
565
# left_parent is the actual revision we want to stop logging at,
566
# since we want to show the merged revisions after the stop_rev too
567
reached_stop_revision_id = False
568
revision_id_whitelist = []
569
505
for node in rev_iter:
506
rev_id = node.key[-1]
571
507
if rev_id == left_parent:
572
# reached the left parent after the stop_revision
574
if (not reached_stop_revision_id
575
or rev_id in revision_id_whitelist):
576
yield (rev_id, node.merge_depth, node.revno,
578
if reached_stop_revision_id or rev_id == stop_revision_id:
579
# only do the merged revs of rev_id from now on
580
rev = self.repository.get_revision(rev_id)
582
reached_stop_revision_id = True
583
revision_id_whitelist.extend(rev.parent_ids)
509
yield (rev_id, node.merge_depth, node.revno,
585
512
raise ValueError('invalid stop_rule %r' % stop_rule)
587
def _filter_start_non_ancestors(self, rev_iter):
588
# If we started from a dotted revno, we want to consider it as a tip
589
# and don't want to yield revisions that are not part of its
590
# ancestry. Given the order guaranteed by the merge sort, we will see
591
# uninteresting descendants of the first parent of our tip before the
594
first = next(rev_iter)
595
except StopIteration:
597
(rev_id, merge_depth, revno, end_of_merge) = first
600
# We start at a mainline revision so by definition, all others
601
# revisions in rev_iter are ancestors
602
for node in rev_iter:
607
pmap = self.repository.get_parent_map([rev_id])
608
parents = pmap.get(rev_id, [])
610
whitelist.update(parents)
612
# If there is no parents, there is nothing of interest left
614
# FIXME: It's hard to test this scenario here as this code is never
615
# called in that case. -- vila 20100322
618
for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
620
if rev_id in whitelist:
621
pmap = self.repository.get_parent_map([rev_id])
622
parents = pmap.get(rev_id, [])
623
whitelist.remove(rev_id)
624
whitelist.update(parents)
626
# We've reached the mainline, there is nothing left to
630
# A revision that is not part of the ancestry of our
633
yield (rev_id, merge_depth, revno, end_of_merge)
635
514
def leave_lock_in_place(self):
636
515
"""Tell this branch object not to release the physical lock when this
637
516
object is unlocked.
639
If lock_write doesn't return a token, then this method is not
518
If lock_write doesn't return a token, then this method is not supported.
642
520
self.control_files.leave_in_place()
795
677
if not self._format.supports_stacking():
796
raise UnstackableBranchFormat(self._format, self.user_url)
797
with self.lock_write():
798
# XXX: Changing from one fallback repository to another does not
799
# check that all the data you need is present in the new fallback.
800
# Possibly it should.
801
self._check_stackable_repo()
804
self.get_stacked_on_url()
805
except (errors.NotStacked, UnstackableBranchFormat,
806
errors.UnstackableRepositoryFormat):
810
self._activate_fallback_location(
811
url, possible_transports=[self.controldir.root_transport])
812
# write this out after the repository is stacked to avoid setting a
813
# stacked config that doesn't work.
814
self._set_config_location('stacked_on_location', url)
678
raise errors.UnstackableBranchFormat(self._format, self.base)
679
# XXX: Changing from one fallback repository to another does not check
680
# that all the data you need is present in the new fallback.
681
# Possibly it should.
682
self._check_stackable_repo()
685
old_url = self.get_stacked_on_url()
686
except (errors.NotStacked, errors.UnstackableBranchFormat,
687
errors.UnstackableRepositoryFormat):
691
self._activate_fallback_location(url)
692
# write this out after the repository is stacked to avoid setting a
693
# stacked config that doesn't work.
694
self._set_config_location('stacked_on_location', url)
816
696
def _unstack(self):
817
697
"""Change a branch to be unstacked, copying data as needed.
819
699
Don't call this directly, use set_stacked_on_url(None).
821
with ui.ui_factory.nested_progress_bar() as pb:
822
pb.update(gettext("Unstacking"))
701
pb = ui.ui_factory.nested_progress_bar()
703
pb.update("Unstacking")
823
704
# The basic approach here is to fetch the tip of the branch,
824
705
# including all available ghosts, from the existing stacked
825
# repository into a new repository object without the fallbacks.
706
# repository into a new repository object without the fallbacks.
827
708
# XXX: See <https://launchpad.net/bugs/397286> - this may not be
828
709
# correct for CHKMap repostiories
829
710
old_repository = self.repository
830
711
if len(old_repository._fallback_repositories) != 1:
831
raise AssertionError(
832
"can't cope with fallback repositories "
833
"of %r (fallbacks: %r)" % (
834
old_repository, old_repository._fallback_repositories))
835
# Open the new repository object.
836
# Repositories don't offer an interface to remove fallback
837
# repositories today; take the conceptually simpler option and just
838
# reopen it. We reopen it starting from the URL so that we
839
# get a separate connection for RemoteRepositories and can
840
# stream from one of them to the other. This does mean doing
841
# separate SSH connection setup, but unstacking is not a
842
# common operation so it's tolerable.
843
new_bzrdir = controldir.ControlDir.open(
844
self.controldir.root_transport.base)
845
new_repository = new_bzrdir.find_repository()
846
if new_repository._fallback_repositories:
847
raise AssertionError(
848
"didn't expect %r to have fallback_repositories"
849
% (self.repository,))
850
# Replace self.repository with the new repository.
851
# Do our best to transfer the lock state (i.e. lock-tokens and
852
# lock count) of self.repository to the new repository.
853
lock_token = old_repository.lock_write().repository_token
854
self.repository = new_repository
855
if isinstance(self, remote.RemoteBranch):
856
# Remote branches can have a second reference to the old
857
# repository that need to be replaced.
858
if self._real_branch is not None:
859
self._real_branch.repository = new_repository
860
self.repository.lock_write(token=lock_token)
861
if lock_token is not None:
862
old_repository.leave_lock_in_place()
712
raise AssertionError("can't cope with fallback repositories "
713
"of %r" % (self.repository,))
714
# unlock it, including unlocking the fallback
863
715
old_repository.unlock()
864
if lock_token is not None:
865
# XXX: self.repository.leave_lock_in_place() before this
866
# function will not be preserved. Fortunately that doesn't
867
# affect the current default format (2a), and would be a
868
# corner-case anyway.
869
# - Andrew Bennetts, 2010/06/30
870
self.repository.dont_leave_lock_in_place()
874
old_repository.unlock()
875
except errors.LockNotHeld:
878
if old_lock_count == 0:
879
raise AssertionError(
880
'old_repository should have been locked at least once.')
881
for i in range(old_lock_count - 1):
716
old_repository.lock_read()
718
# Repositories don't offer an interface to remove fallback
719
# repositories today; take the conceptually simpler option and just
720
# reopen it. We reopen it starting from the URL so that we
721
# get a separate connection for RemoteRepositories and can
722
# stream from one of them to the other. This does mean doing
723
# separate SSH connection setup, but unstacking is not a
724
# common operation so it's tolerable.
725
new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
726
new_repository = new_bzrdir.find_repository()
727
self.repository = new_repository
728
if self.repository._fallback_repositories:
729
raise AssertionError("didn't expect %r to have "
730
"fallback_repositories"
731
% (self.repository,))
732
# this is not paired with an unlock because it's just restoring
733
# the previous state; the lock's released when set_stacked_on_url
882
735
self.repository.lock_write()
883
# Fetch from the old repository into the new.
884
with old_repository.lock_read():
885
736
# XXX: If you unstack a branch while it has a working tree
886
737
# with a pending merge, the pending-merged revisions will no
887
738
# longer be present. You can (probably) revert and remerge.
889
tags_to_fetch = set(self.tags.get_reverse_tag_dict())
890
except errors.TagsNotSupported:
891
tags_to_fetch = set()
892
fetch_spec = vf_search.NotInOtherForRevs(
893
self.repository, old_repository,
894
required_ids=[self.last_revision()],
895
if_present_ids=tags_to_fetch, find_ghosts=True).execute()
896
self.repository.fetch(old_repository, fetch_spec=fetch_spec)
740
# XXX: This only fetches up to the tip of the repository; it
741
# doesn't bring across any tags. That's fairly consistent
742
# with how branch works, but perhaps not ideal.
743
self.repository.fetch(old_repository,
744
revision_id=self.last_revision(),
747
old_repository.unlock()
751
def _set_tags_bytes(self, bytes):
752
"""Mirror method for _get_tags_bytes.
754
:seealso: Branch._get_tags_bytes.
756
return _run_with_write_locked_target(self, self._transport.put_bytes,
898
759
def _cache_revision_history(self, rev_history):
899
760
"""Set the cached revision history to rev_history.
967
833
def unbind(self):
968
834
"""Older format branches cannot bind or unbind."""
969
raise errors.UpgradeRequired(self.user_url)
835
raise errors.UpgradeRequired(self.base)
971
837
def last_revision(self):
972
838
"""Return last revision id, or NULL_REVISION."""
973
839
return self.last_revision_info()[1]
975
842
def last_revision_info(self):
976
843
"""Return information about the last revision.
978
845
:return: A tuple (revno, revision_id).
980
with self.lock_read():
981
if self._last_revision_info_cache is None:
982
self._last_revision_info_cache = (
983
self._read_last_revision_info())
984
return self._last_revision_info_cache
986
def _read_last_revision_info(self):
987
raise NotImplementedError(self._read_last_revision_info)
989
def import_last_revision_info_and_tags(self, source, revno, revid,
847
if self._last_revision_info_cache is None:
848
self._last_revision_info_cache = self._last_revision_info()
849
return self._last_revision_info_cache
851
def _last_revision_info(self):
852
rh = self.revision_history()
855
return (revno, rh[-1])
857
return (0, _mod_revision.NULL_REVISION)
859
@deprecated_method(deprecated_in((1, 6, 0)))
860
def missing_revisions(self, other, stop_revision=None):
861
"""Return a list of new revisions that would perfectly fit.
863
If self and other have not diverged, return a list of the revisions
864
present in other, but missing from self.
866
self_history = self.revision_history()
867
self_len = len(self_history)
868
other_history = other.revision_history()
869
other_len = len(other_history)
870
common_index = min(self_len, other_len) -1
871
if common_index >= 0 and \
872
self_history[common_index] != other_history[common_index]:
873
raise errors.DivergedBranches(self, other)
875
if stop_revision is None:
876
stop_revision = other_len
878
if stop_revision > other_len:
879
raise errors.NoSuchRevision(self, stop_revision)
880
return other_history[self_len:stop_revision]
883
def update_revisions(self, other, stop_revision=None, overwrite=False,
885
"""Pull in new perfect-fit revisions.
887
:param other: Another Branch to pull from
888
:param stop_revision: Updated until the given revision
889
:param overwrite: Always set the branch pointer, rather than checking
890
to see if it is a proper descendant.
891
:param graph: A Graph object that can be used to query history
892
information. This can be None.
895
return InterBranch.get(other, self).update_revisions(stop_revision,
898
def import_last_revision_info(self, source_repo, revno, revid):
991
899
"""Set the last revision info, importing from another repo if necessary.
993
901
This is used by the bound branch code to upload a revision to
994
902
the master branch first before updating the tip of the local branch.
995
Revisions referenced by source's tags are also transferred.
997
:param source: Source branch to optionally fetch from
904
:param source_repo: Source repository to optionally fetch from
998
905
:param revno: Revision number of the new tip
999
906
:param revid: Revision id of the new tip
1000
:param lossy: Whether to discard metadata that can not be
1001
natively represented
1002
:return: Tuple with the new revision number and revision id
1003
(should only be different from the arguments when lossy=True)
1005
if not self.repository.has_same_location(source.repository):
1006
self.fetch(source, revid)
908
if not self.repository.has_same_location(source_repo):
909
self.repository.fetch(source_repo, revision_id=revid)
1007
910
self.set_last_revision_info(revno, revid)
1008
return (revno, revid)
1010
912
def revision_id_to_revno(self, revision_id):
1011
913
"""Given a revision id, return its revno"""
1012
914
if _mod_revision.is_null(revision_id):
1014
history = self._revision_history()
916
history = self.revision_history()
1016
918
return history.index(revision_id) + 1
1017
919
except ValueError:
1018
920
raise errors.NoSuchRevision(self, revision_id)
1020
923
def get_rev_id(self, revno, history=None):
1021
924
"""Find the revision id of the specified revno."""
1022
with self.lock_read():
1024
return _mod_revision.NULL_REVISION
1025
last_revno, last_revid = self.last_revision_info()
1026
if revno == last_revno:
1028
if revno <= 0 or revno > last_revno:
1029
raise errors.NoSuchRevision(self, revno)
1030
distance_from_last = last_revno - revno
1031
if len(self._partial_revision_history_cache) <= distance_from_last:
1032
self._extend_partial_history(distance_from_last)
1033
return self._partial_revision_history_cache[distance_from_last]
926
return _mod_revision.NULL_REVISION
927
last_revno, last_revid = self.last_revision_info()
928
if revno == last_revno:
930
if revno <= 0 or revno > last_revno:
931
raise errors.NoSuchRevision(self, revno)
932
distance_from_last = last_revno - revno
933
if len(self._partial_revision_history_cache) <= distance_from_last:
934
self._extend_partial_history(distance_from_last)
935
return self._partial_revision_history_cache[distance_from_last]
1035
938
def pull(self, source, overwrite=False, stop_revision=None,
1036
939
possible_transports=None, *args, **kwargs):
1037
940
"""Mirror source into this branch.
1327
1283
# XXX: Fix the bzrdir API to allow getting the branch back from the
1328
1284
# clone call. Or something. 20090224 RBC/spiv.
1329
# XXX: Should this perhaps clone colocated branches as well,
1330
# rather than just the default branch? 20100319 JRV
1331
1285
if revision_id is None:
1332
1286
revision_id = self.last_revision()
1333
dir_to = self.controldir.clone_on_transport(
1334
to_transport, revision_id=revision_id, stacked_on=stacked_on,
1335
create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1336
no_tree=no_tree, tag_selector=tag_selector)
1288
dir_to = self.bzrdir.clone_on_transport(to_transport,
1289
revision_id=revision_id, stacked_on=stacked_on,
1290
create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1291
except errors.FileExists:
1292
if not use_existing_dir:
1294
except errors.NoSuchFile:
1295
if not create_prefix:
1337
1297
return dir_to.open_branch()
1339
1299
def create_checkout(self, to_location, revision_id=None,
1340
1300
lightweight=False, accelerator_tree=None,
1341
hardlink=False, recurse_nested=True):
1342
1302
"""Create a checkout of a branch.
1344
1304
:param to_location: The url to produce the checkout at
1345
1305
:param revision_id: The revision to check out
1346
1306
:param lightweight: If True, produce a lightweight checkout, otherwise,
1347
produce a bound branch (heavyweight checkout)
1307
produce a bound branch (heavyweight checkout)
1348
1308
:param accelerator_tree: A tree which can be used for retrieving file
1349
1309
contents more quickly than the revision tree, i.e. a workingtree.
1350
1310
The revision tree will be used for cases where accelerator_tree's
1351
1311
content is different.
1352
1312
:param hardlink: If true, hard-link files from accelerator_tree,
1353
1313
where possible.
1354
:param recurse_nested: Whether to recurse into nested trees
1355
1314
:return: The tree of the created checkout
1357
1316
t = transport.get_transport(to_location)
1358
1317
t.ensure_base()
1359
format = self._get_checkout_format(lightweight=lightweight)
1319
format = self._get_checkout_format()
1361
1320
checkout = format.initialize_on_transport(t)
1362
except errors.AlreadyControlDirError:
1363
# It's fine if the control directory already exists,
1364
# as long as there is no existing branch and working tree.
1365
checkout = controldir.ControlDir.open_from_transport(t)
1367
checkout.open_branch()
1368
except errors.NotBranchError:
1371
raise errors.AlreadyControlDirError(t.base)
1372
if (checkout.control_transport.base
1373
== self.controldir.control_transport.base):
1374
# When checking out to the same control directory,
1375
# always create a lightweight checkout
1379
from_branch = checkout.set_branch_reference(target_branch=self)
1321
from_branch = BranchReferenceFormat().initialize(checkout, self)
1381
policy = checkout.determine_repository_policy()
1382
policy.acquire_repository()
1383
checkout_branch = checkout.create_branch()
1323
format = self._get_checkout_format()
1324
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1325
to_location, force_new_tree=False, format=format)
1326
checkout = checkout_branch.bzrdir
1384
1327
checkout_branch.bind(self)
1385
1328
# pull up to the specified revision_id to set the initial
1386
1329
# branch tip correctly, and seed it with history.
1387
1330
checkout_branch.pull(self, stop_revision=revision_id)
1389
1332
tree = checkout.create_workingtree(revision_id,
1390
1333
from_branch=from_branch,
1391
1334
accelerator_tree=accelerator_tree,
1392
1335
hardlink=hardlink)
1393
1336
basis_tree = tree.basis_tree()
1394
with basis_tree.lock_read():
1395
for path in basis_tree.iter_references():
1396
reference_parent = tree.reference_parent(path)
1397
if reference_parent is None:
1398
warning('Branch location for %s unknown.', path)
1400
reference_parent.create_checkout(
1402
basis_tree.get_reference_revision(path), lightweight)
1337
basis_tree.lock_read()
1339
for path, file_id in basis_tree.iter_references():
1340
reference_parent = self.reference_parent(file_id, path)
1341
reference_parent.create_checkout(tree.abspath(path),
1342
basis_tree.get_reference_revision(file_id, path),
1405
1349
def reconcile(self, thorough=True):
1406
"""Make sure the data stored in this branch is consistent.
1350
"""Make sure the data stored in this branch is consistent."""
1351
from bzrlib.reconcile import BranchReconciler
1352
reconciler = BranchReconciler(self, thorough=thorough)
1353
reconciler.reconcile()
1408
:return: A `ReconcileResult` object.
1356
def reference_parent(self, file_id, path, possible_transports=None):
1357
"""Return the parent branch for a tree-reference file_id
1358
:param file_id: The file_id of the tree reference
1359
:param path: The path of the file_id in the tree
1360
:return: A branch associated with the file_id
1410
raise NotImplementedError(self.reconcile)
1362
# FIXME should provide multiple branches, based on config
1363
return Branch.open(self.bzrdir.root_transport.clone(path).base,
1364
possible_transports=possible_transports)
1412
1366
def supports_tags(self):
1413
1367
return self._format.supports_tags()
1415
def automatic_tag_name(self, revision_id):
1416
"""Try to automatically find the tag name for a revision.
1418
:param revision_id: Revision id of the revision.
1419
:return: A tag name or None if no tag name could be determined.
1421
for hook in Branch.hooks['automatic_tag_name']:
1422
ret = hook(self, revision_id)
1427
1369
def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1429
1371
"""Ensure that revision_b is a descendant of revision_a.
1782
1724
self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1785
class BranchInitHookParams(object):
1786
"""Object holding parameters passed to `*_branch_init` hooks.
1788
There are 4 fields that hooks may wish to access:
1790
:ivar format: the branch format
1791
:ivar bzrdir: the ControlDir where the branch will be/has been initialized
1792
:ivar name: name of colocated branch, if any (or None)
1793
:ivar branch: the branch created
1795
Note that for lightweight checkouts, the bzrdir and format fields refer to
1796
the checkout, hence they are different from the corresponding fields in
1797
branch, which refer to the original branch.
1800
def __init__(self, format, controldir, name, branch):
1801
"""Create a group of BranchInitHook parameters.
1803
:param format: the branch format
1804
:param controldir: the ControlDir where the branch will be/has been
1806
:param name: name of colocated branch, if any (or None)
1807
:param branch: the branch created
1809
Note that for lightweight checkouts, the bzrdir and format fields refer
1810
to the checkout, hence they are different from the corresponding fields
1811
in branch, which refer to the original branch.
1813
self.format = format
1814
self.controldir = controldir
1816
self.branch = branch
1818
def __eq__(self, other):
1819
return self.__dict__ == other.__dict__
1822
return "<%s of %s>" % (self.__class__.__name__, self.branch)
1825
class SwitchHookParams(object):
1826
"""Object holding parameters passed to `*_switch` hooks.
1828
There are 4 fields that hooks may wish to access:
1830
:ivar control_dir: ControlDir of the checkout to change
1831
:ivar to_branch: branch that the checkout is to reference
1832
:ivar force: skip the check for local commits in a heavy checkout
1833
:ivar revision_id: revision ID to switch to (or None)
1836
def __init__(self, control_dir, to_branch, force, revision_id):
1837
"""Create a group of SwitchHook parameters.
1839
:param control_dir: ControlDir of the checkout to change
1840
:param to_branch: branch that the checkout is to reference
1841
:param force: skip the check for local commits in a heavy checkout
1842
:param revision_id: revision ID to switch to (or None)
1844
self.control_dir = control_dir
1845
self.to_branch = to_branch
1847
self.revision_id = revision_id
1849
def __eq__(self, other):
1850
return self.__dict__ == other.__dict__
1853
return "<%s for %s to (%s, %s)>" % (
1854
self.__class__.__name__, self.control_dir, self.to_branch,
1858
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
1859
"""Branch format registry."""
1861
def __init__(self, other_registry=None):
1862
super(BranchFormatRegistry, self).__init__(other_registry)
1863
self._default_format = None
1864
self._default_format_key = None
1866
def get_default(self):
1867
"""Return the current default format."""
1868
if (self._default_format_key is not None
1869
and self._default_format is None):
1870
self._default_format = self.get(self._default_format_key)
1871
return self._default_format
1873
def set_default(self, format):
1874
"""Set the default format."""
1875
self._default_format = format
1876
self._default_format_key = None
1878
def set_default_key(self, format_string):
1879
"""Set the default format by its format string."""
1880
self._default_format_key = format_string
1881
self._default_format = None
1727
class BzrBranchFormat4(BranchFormat):
1728
"""Bzr branch format 4.
1731
- a revision-history file.
1732
- a branch-lock lock file [ to be shared with the bzrdir ]
1735
def get_format_description(self):
1736
"""See BranchFormat.get_format_description()."""
1737
return "Branch format 4"
1739
def initialize(self, a_bzrdir):
1740
"""Create a branch of this format in a_bzrdir."""
1741
utf8_files = [('revision-history', ''),
1742
('branch-name', ''),
1744
return self._initialize_helper(a_bzrdir, utf8_files,
1745
lock_type='branch4', set_format=False)
1748
super(BzrBranchFormat4, self).__init__()
1749
self._matchingbzrdir = bzrdir.BzrDirFormat6()
1751
def network_name(self):
1752
"""The network name for this format is the control dirs disk label."""
1753
return self._matchingbzrdir.get_format_string()
1755
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1756
"""See BranchFormat.open()."""
1758
# we are being called directly and must probe.
1759
raise NotImplementedError
1760
return BzrBranch(_format=self,
1761
_control_files=a_bzrdir._control_files,
1763
_repository=a_bzrdir.open_repository())
1766
return "Bazaar-NG branch format 4"
1769
class BranchFormatMetadir(BranchFormat):
1770
"""Common logic for meta-dir based branch formats."""
1772
def _branch_class(self):
1773
"""What class to instantiate on open calls."""
1774
raise NotImplementedError(self._branch_class)
1776
def network_name(self):
1777
"""A simple byte string uniquely identifying this format for RPC calls.
1779
Metadir branch formats use their format string.
1781
return self.get_format_string()
1783
def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1784
"""See BranchFormat.open()."""
1786
format = BranchFormat.find_format(a_bzrdir)
1787
if format.__class__ != self.__class__:
1788
raise AssertionError("wrong format %r found for %r" %
1791
transport = a_bzrdir.get_branch_transport(None)
1792
control_files = lockable_files.LockableFiles(transport, 'lock',
1794
return self._branch_class()(_format=self,
1795
_control_files=control_files,
1797
_repository=a_bzrdir.find_repository(),
1798
ignore_fallbacks=ignore_fallbacks)
1799
except errors.NoSuchFile:
1800
raise errors.NotBranchError(path=transport.base)
1803
super(BranchFormatMetadir, self).__init__()
1804
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1805
self._matchingbzrdir.set_branch_format(self)
1807
def supports_tags(self):
1811
class BzrBranchFormat5(BranchFormatMetadir):
1812
"""Bzr branch format 5.
1815
- a revision-history file.
1817
- a lock dir guarding the branch itself
1818
- all of this stored in a branch/ subdirectory
1819
- works with shared repositories.
1821
This format is new in bzr 0.8.
1824
def _branch_class(self):
1827
def get_format_string(self):
1828
"""See BranchFormat.get_format_string()."""
1829
return "Bazaar-NG branch format 5\n"
1831
def get_format_description(self):
1832
"""See BranchFormat.get_format_description()."""
1833
return "Branch format 5"
1835
def initialize(self, a_bzrdir):
1836
"""Create a branch of this format in a_bzrdir."""
1837
utf8_files = [('revision-history', ''),
1838
('branch-name', ''),
1840
return self._initialize_helper(a_bzrdir, utf8_files)
1842
def supports_tags(self):
1846
class BzrBranchFormat6(BranchFormatMetadir):
1847
"""Branch format with last-revision and tags.
1849
Unlike previous formats, this has no explicit revision history. Instead,
1850
this just stores the last-revision, and the left-hand history leading
1851
up to there is the history.
1853
This format was introduced in bzr 0.15
1854
and became the default in 0.91.
1857
def _branch_class(self):
1860
def get_format_string(self):
1861
"""See BranchFormat.get_format_string()."""
1862
return "Bazaar Branch Format 6 (bzr 0.15)\n"
1864
def get_format_description(self):
1865
"""See BranchFormat.get_format_description()."""
1866
return "Branch format 6"
1868
def initialize(self, a_bzrdir):
1869
"""Create a branch of this format in a_bzrdir."""
1870
utf8_files = [('last-revision', '0 null:\n'),
1871
('branch.conf', ''),
1874
return self._initialize_helper(a_bzrdir, utf8_files)
1876
def make_tags(self, branch):
1877
"""See bzrlib.branch.BranchFormat.make_tags()."""
1878
return BasicTags(branch)
1880
def supports_set_append_revisions_only(self):
1884
class BzrBranchFormat8(BranchFormatMetadir):
1885
"""Metadir format supporting storing locations of subtree branches."""
1887
def _branch_class(self):
1890
def get_format_string(self):
1891
"""See BranchFormat.get_format_string()."""
1892
return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
1894
def get_format_description(self):
1895
"""See BranchFormat.get_format_description()."""
1896
return "Branch format 8"
1898
def initialize(self, a_bzrdir):
1899
"""Create a branch of this format in a_bzrdir."""
1900
utf8_files = [('last-revision', '0 null:\n'),
1901
('branch.conf', ''),
1905
return self._initialize_helper(a_bzrdir, utf8_files)
1908
super(BzrBranchFormat8, self).__init__()
1909
self._matchingbzrdir.repository_format = \
1910
RepositoryFormatKnitPack5RichRoot()
1912
def make_tags(self, branch):
1913
"""See bzrlib.branch.BranchFormat.make_tags()."""
1914
return BasicTags(branch)
1916
def supports_set_append_revisions_only(self):
1919
def supports_stacking(self):
1922
supports_reference_locations = True
1925
class BzrBranchFormat7(BzrBranchFormat8):
1926
"""Branch format with last-revision, tags, and a stacked location pointer.
1928
The stacked location pointer is passed down to the repository and requires
1929
a repository format with supports_external_lookups = True.
1931
This format was introduced in bzr 1.6.
1934
def initialize(self, a_bzrdir):
1935
"""Create a branch of this format in a_bzrdir."""
1936
utf8_files = [('last-revision', '0 null:\n'),
1937
('branch.conf', ''),
1940
return self._initialize_helper(a_bzrdir, utf8_files)
1942
def _branch_class(self):
1945
def get_format_string(self):
1946
"""See BranchFormat.get_format_string()."""
1947
return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
1949
def get_format_description(self):
1950
"""See BranchFormat.get_format_description()."""
1951
return "Branch format 7"
1953
def supports_set_append_revisions_only(self):
1956
supports_reference_locations = False
1959
class BranchReferenceFormat(BranchFormat):
1960
"""Bzr branch reference format.
1962
Branch references are used in implementing checkouts, they
1963
act as an alias to the real branch which is at some other url.
1970
def get_format_string(self):
1971
"""See BranchFormat.get_format_string()."""
1972
return "Bazaar-NG Branch Reference Format 1\n"
1974
def get_format_description(self):
1975
"""See BranchFormat.get_format_description()."""
1976
return "Checkout reference format 1"
1978
def get_reference(self, a_bzrdir):
1979
"""See BranchFormat.get_reference()."""
1980
transport = a_bzrdir.get_branch_transport(None)
1981
return transport.get('location').read()
1983
def set_reference(self, a_bzrdir, to_branch):
1984
"""See BranchFormat.set_reference()."""
1985
transport = a_bzrdir.get_branch_transport(None)
1986
location = transport.put_bytes('location', to_branch.base)
1988
def initialize(self, a_bzrdir, target_branch=None):
1989
"""Create a branch of this format in a_bzrdir."""
1990
if target_branch is None:
1991
# this format does not implement branch itself, thus the implicit
1992
# creation contract must see it as uninitializable
1993
raise errors.UninitializableFormat(self)
1994
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1995
branch_transport = a_bzrdir.get_branch_transport(self)
1996
branch_transport.put_bytes('location',
1997
target_branch.bzrdir.root_transport.base)
1998
branch_transport.put_bytes('format', self.get_format_string())
2000
a_bzrdir, _found=True,
2001
possible_transports=[target_branch.bzrdir.root_transport])
2004
super(BranchReferenceFormat, self).__init__()
2005
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2006
self._matchingbzrdir.set_branch_format(self)
2008
def _make_reference_clone_function(format, a_branch):
2009
"""Create a clone() routine for a branch dynamically."""
2010
def clone(to_bzrdir, revision_id=None,
2011
repository_policy=None):
2012
"""See Branch.clone()."""
2013
return format.initialize(to_bzrdir, a_branch)
2014
# cannot obey revision_id limits when cloning a reference ...
2015
# FIXME RBC 20060210 either nuke revision_id for clone, or
2016
# emit some sort of warning/error to the caller ?!
2019
def open(self, a_bzrdir, _found=False, location=None,
2020
possible_transports=None, ignore_fallbacks=False):
2021
"""Return the branch that the branch reference in a_bzrdir points at.
2023
:param a_bzrdir: A BzrDir that contains a branch.
2024
:param _found: a private parameter, do not use it. It is used to
2025
indicate if format probing has already be done.
2026
:param ignore_fallbacks: when set, no fallback branches will be opened
2027
(if there are any). Default is to open fallbacks.
2028
:param location: The location of the referenced branch. If
2029
unspecified, this will be determined from the branch reference in
2031
:param possible_transports: An optional reusable transports list.
2034
format = BranchFormat.find_format(a_bzrdir)
2035
if format.__class__ != self.__class__:
2036
raise AssertionError("wrong format %r found for %r" %
2038
if location is None:
2039
location = self.get_reference(a_bzrdir)
2040
real_bzrdir = bzrdir.BzrDir.open(
2041
location, possible_transports=possible_transports)
2042
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2043
# this changes the behaviour of result.clone to create a new reference
2044
# rather than a copy of the content of the branch.
2045
# I did not use a proxy object because that needs much more extensive
2046
# testing, and we are only changing one behaviour at the moment.
2047
# If we decide to alter more behaviours - i.e. the implicit nickname
2048
# then this should be refactored to introduce a tested proxy branch
2049
# and a subclass of that for use in overriding clone() and ....
2051
result.clone = self._make_reference_clone_function(result)
1884
2055
network_format_registry = registry.FormatRegistry()
1889
2060
BranchFormat.network_name() for more detail.
1892
format_registry = BranchFormatRegistry(network_format_registry)
1895
2064
# formats which have no format string are not discoverable
1896
2065
# and not independently creatable, so are not registered.
1897
format_registry.register_lazy(
1898
b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
1900
format_registry.register_lazy(
1901
b"Bazaar Branch Format 6 (bzr 0.15)\n",
1902
"breezy.bzr.branch", "BzrBranchFormat6")
1903
format_registry.register_lazy(
1904
b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
1905
"breezy.bzr.branch", "BzrBranchFormat7")
1906
format_registry.register_lazy(
1907
b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
1908
"breezy.bzr.branch", "BzrBranchFormat8")
1909
format_registry.register_lazy(
1910
b"Bazaar-NG Branch Reference Format 1\n",
1911
"breezy.bzr.branch", "BranchReferenceFormat")
1913
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
1916
class BranchWriteLockResult(LogicalLockResult):
1917
"""The result of write locking a branch.
1919
:ivar token: The token obtained from the underlying branch lock, or
1921
:ivar unlock: A callable which will unlock the lock.
1925
return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token)
2066
__format5 = BzrBranchFormat5()
2067
__format6 = BzrBranchFormat6()
2068
__format7 = BzrBranchFormat7()
2069
__format8 = BzrBranchFormat8()
2070
BranchFormat.register_format(__format5)
2071
BranchFormat.register_format(BranchReferenceFormat())
2072
BranchFormat.register_format(__format6)
2073
BranchFormat.register_format(__format7)
2074
BranchFormat.register_format(__format8)
2075
BranchFormat.set_default_format(__format7)
2076
_legacy_formats = [BzrBranchFormat4(),
2078
network_format_registry.register(
2079
_legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2082
class BzrBranch(Branch):
2083
"""A branch stored in the actual filesystem.
2085
Note that it's "local" in the context of the filesystem; it doesn't
2086
really matter if it's on an nfs/smb/afs/coda/... share, as long as
2087
it's writable, and can be accessed via the normal filesystem API.
2089
:ivar _transport: Transport for file operations on this branch's
2090
control files, typically pointing to the .bzr/branch directory.
2091
:ivar repository: Repository for this branch.
2092
:ivar base: The url of the base directory for this branch; the one
2093
containing the .bzr directory.
2096
def __init__(self, _format=None,
2097
_control_files=None, a_bzrdir=None, _repository=None,
2098
ignore_fallbacks=False):
2099
"""Create new branch object at a particular location."""
2100
if a_bzrdir is None:
2101
raise ValueError('a_bzrdir must be supplied')
2103
self.bzrdir = a_bzrdir
2104
self._base = self.bzrdir.transport.clone('..').base
2105
# XXX: We should be able to just do
2106
# self.base = self.bzrdir.root_transport.base
2107
# but this does not quite work yet -- mbp 20080522
2108
self._format = _format
2109
if _control_files is None:
2110
raise ValueError('BzrBranch _control_files is None')
2111
self.control_files = _control_files
2112
self._transport = _control_files._transport
2113
self.repository = _repository
2114
Branch.__init__(self)
2117
return '%s(%r)' % (self.__class__.__name__, self.base)
2121
def _get_base(self):
2122
"""Returns the directory containing the control directory."""
2125
base = property(_get_base, doc="The URL for the root of this branch.")
2127
def _get_config(self):
2128
return TransportConfig(self._transport, 'branch.conf')
2130
def is_locked(self):
2131
return self.control_files.is_locked()
2133
def lock_write(self, token=None):
2134
# All-in-one needs to always unlock/lock.
2135
repo_control = getattr(self.repository, 'control_files', None)
2136
if self.control_files == repo_control or not self.is_locked():
2137
self.repository.lock_write()
2142
return self.control_files.lock_write(token=token)
2145
self.repository.unlock()
2148
def lock_read(self):
2149
# All-in-one needs to always unlock/lock.
2150
repo_control = getattr(self.repository, 'control_files', None)
2151
if self.control_files == repo_control or not self.is_locked():
2152
self.repository.lock_read()
2157
self.control_files.lock_read()
2160
self.repository.unlock()
2165
self.control_files.unlock()
2167
# All-in-one needs to always unlock/lock.
2168
repo_control = getattr(self.repository, 'control_files', None)
2169
if (self.control_files == repo_control or
2170
not self.control_files.is_locked()):
2171
self.repository.unlock()
2172
if not self.control_files.is_locked():
2173
# we just released the lock
2174
self._clear_cached_state()
2176
def peek_lock_mode(self):
2177
if self.control_files._lock_count == 0:
2180
return self.control_files._lock_mode
2182
def get_physical_lock_status(self):
2183
return self.control_files.get_physical_lock_status()
2186
def print_file(self, file, revision_id):
2187
"""See Branch.print_file."""
2188
return self.repository.print_file(file, revision_id)
2190
def _write_revision_history(self, history):
2191
"""Factored out of set_revision_history.
2193
This performs the actual writing to disk.
2194
It is intended to be called by BzrBranch5.set_revision_history."""
2195
self._transport.put_bytes(
2196
'revision-history', '\n'.join(history),
2197
mode=self.bzrdir._get_file_mode())
2200
def set_revision_history(self, rev_history):
2201
"""See Branch.set_revision_history."""
2202
if 'evil' in debug.debug_flags:
2203
mutter_callsite(3, "set_revision_history scales with history.")
2204
check_not_reserved_id = _mod_revision.check_not_reserved_id
2205
for rev_id in rev_history:
2206
check_not_reserved_id(rev_id)
2207
if Branch.hooks['post_change_branch_tip']:
2208
# Don't calculate the last_revision_info() if there are no hooks
2210
old_revno, old_revid = self.last_revision_info()
2211
if len(rev_history) == 0:
2212
revid = _mod_revision.NULL_REVISION
2214
revid = rev_history[-1]
2215
self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2216
self._write_revision_history(rev_history)
2217
self._clear_cached_state()
2218
self._cache_revision_history(rev_history)
2219
for hook in Branch.hooks['set_rh']:
2220
hook(self, rev_history)
2221
if Branch.hooks['post_change_branch_tip']:
2222
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2224
def _synchronize_history(self, destination, revision_id):
2225
"""Synchronize last revision and revision history between branches.
2227
This version is most efficient when the destination is also a
2228
BzrBranch5, but works for BzrBranch6 as long as the revision
2229
history is the true lefthand parent history, and all of the revisions
2230
are in the destination's repository. If not, set_revision_history
2233
:param destination: The branch to copy the history into
2234
:param revision_id: The revision-id to truncate history at. May
2235
be None to copy complete history.
2237
if not isinstance(destination._format, BzrBranchFormat5):
2238
super(BzrBranch, self)._synchronize_history(
2239
destination, revision_id)
2241
if revision_id == _mod_revision.NULL_REVISION:
2244
new_history = self.revision_history()
2245
if revision_id is not None and new_history != []:
2247
new_history = new_history[:new_history.index(revision_id) + 1]
2249
rev = self.repository.get_revision(revision_id)
2250
new_history = rev.get_history(self.repository)[1:]
2251
destination.set_revision_history(new_history)
2254
def set_last_revision_info(self, revno, revision_id):
2255
"""Set the last revision of this branch.
2257
The caller is responsible for checking that the revno is correct
2258
for this revision id.
2260
It may be possible to set the branch last revision to an id not
2261
present in the repository. However, branches can also be
2262
configured to check constraints on history, in which case this may not
2265
revision_id = _mod_revision.ensure_null(revision_id)
2266
# this old format stores the full history, but this api doesn't
2267
# provide it, so we must generate, and might as well check it's
2269
history = self._lefthand_history(revision_id)
2270
if len(history) != revno:
2271
raise AssertionError('%d != %d' % (len(history), revno))
2272
self.set_revision_history(history)
2274
def _gen_revision_history(self):
2275
history = self._transport.get_bytes('revision-history').split('\n')
2276
if history[-1:] == ['']:
2277
# There shouldn't be a trailing newline, but just in case.
2282
def generate_revision_history(self, revision_id, last_rev=None,
2284
"""Create a new revision history that will finish with revision_id.
2286
:param revision_id: the new tip to use.
2287
:param last_rev: The previous last_revision. If not None, then this
2288
must be a ancestory of revision_id, or DivergedBranches is raised.
2289
:param other_branch: The other branch that DivergedBranches should
2290
raise with respect to.
2292
self.set_revision_history(self._lefthand_history(revision_id,
2293
last_rev, other_branch))
2295
def basis_tree(self):
2296
"""See Branch.basis_tree."""
2297
return self.repository.revision_tree(self.last_revision())
2299
def _get_parent_location(self):
2300
_locs = ['parent', 'pull', 'x-pull']
2303
return self._transport.get_bytes(l).strip('\n')
2304
except errors.NoSuchFile:
2308
def _basic_push(self, target, overwrite, stop_revision):
2309
"""Basic implementation of push without bound branches or hooks.
2311
Must be called with source read locked and target write locked.
2313
result = BranchPushResult()
2314
result.source_branch = self
2315
result.target_branch = target
2316
result.old_revno, result.old_revid = target.last_revision_info()
2317
self.update_references(target)
2318
if result.old_revid != self.last_revision():
2319
# We assume that during 'push' this repository is closer than
2321
graph = self.repository.get_graph(target.repository)
2322
target.update_revisions(self, stop_revision,
2323
overwrite=overwrite, graph=graph)
2324
if self._push_should_merge_tags():
2325
result.tag_conflicts = self.tags.merge_to(target.tags,
2327
result.new_revno, result.new_revid = target.last_revision_info()
2330
def get_stacked_on_url(self):
2331
raise errors.UnstackableBranchFormat(self._format, self.base)
2333
def set_push_location(self, location):
2334
"""See Branch.set_push_location."""
2335
self.get_config().set_user_option(
2336
'push_location', location,
2337
store=_mod_config.STORE_LOCATION_NORECURSE)
2339
def _set_parent_location(self, url):
2341
self._transport.delete('parent')
2343
self._transport.put_bytes('parent', url + '\n',
2344
mode=self.bzrdir._get_file_mode())
2347
class BzrBranch5(BzrBranch):
2348
"""A format 5 branch. This supports new features over plain branches.
2350
It has support for a master_branch which is the data for bound branches.
2353
def get_bound_location(self):
2355
return self._transport.get_bytes('bound')[:-1]
2356
except errors.NoSuchFile:
2360
def get_master_branch(self, possible_transports=None):
2361
"""Return the branch we are bound to.
2363
:return: Either a Branch, or None
2365
This could memoise the branch, but if thats done
2366
it must be revalidated on each new lock.
2367
So for now we just don't memoise it.
2368
# RBC 20060304 review this decision.
2370
bound_loc = self.get_bound_location()
2374
return Branch.open(bound_loc,
2375
possible_transports=possible_transports)
2376
except (errors.NotBranchError, errors.ConnectionError), e:
2377
raise errors.BoundBranchConnectionFailure(
2381
def set_bound_location(self, location):
2382
"""Set the target where this branch is bound to.
2384
:param location: URL to the target branch
2387
self._transport.put_bytes('bound', location+'\n',
2388
mode=self.bzrdir._get_file_mode())
2391
self._transport.delete('bound')
2392
except errors.NoSuchFile:
2397
def bind(self, other):
2398
"""Bind this branch to the branch other.
2400
This does not push or pull data between the branches, though it does
2401
check for divergence to raise an error when the branches are not
2402
either the same, or one a prefix of the other. That behaviour may not
2403
be useful, so that check may be removed in future.
2405
:param other: The branch to bind to
2408
# TODO: jam 20051230 Consider checking if the target is bound
2409
# It is debatable whether you should be able to bind to
2410
# a branch which is itself bound.
2411
# Committing is obviously forbidden,
2412
# but binding itself may not be.
2413
# Since we *have* to check at commit time, we don't
2414
# *need* to check here
2416
# we want to raise diverged if:
2417
# last_rev is not in the other_last_rev history, AND
2418
# other_last_rev is not in our history, and do it without pulling
2420
self.set_bound_location(other.base)
2424
"""If bound, unbind"""
2425
return self.set_bound_location(None)
2428
def update(self, possible_transports=None):
2429
"""Synchronise this branch with the master branch if any.
2431
:return: None or the last_revision that was pivoted out during the
2434
master = self.get_master_branch(possible_transports)
2435
if master is not None:
2436
old_tip = _mod_revision.ensure_null(self.last_revision())
2437
self.pull(master, overwrite=True)
2438
if self.repository.get_graph().is_ancestor(old_tip,
2439
_mod_revision.ensure_null(self.last_revision())):
2445
class BzrBranch8(BzrBranch5):
2446
"""A branch that stores tree-reference locations."""
2448
def _open_hook(self):
2449
if self._ignore_fallbacks:
2452
url = self.get_stacked_on_url()
2453
except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2454
errors.UnstackableBranchFormat):
2457
for hook in Branch.hooks['transform_fallback_location']:
2458
url = hook(self, url)
2460
hook_name = Branch.hooks.get_hook_name(hook)
2461
raise AssertionError(
2462
"'transform_fallback_location' hook %s returned "
2463
"None, not a URL." % hook_name)
2464
self._activate_fallback_location(url)
2466
def __init__(self, *args, **kwargs):
2467
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2468
super(BzrBranch8, self).__init__(*args, **kwargs)
2469
self._last_revision_info_cache = None
2470
self._reference_info = None
2472
def _clear_cached_state(self):
2473
super(BzrBranch8, self)._clear_cached_state()
2474
self._last_revision_info_cache = None
2475
self._reference_info = None
2477
def _last_revision_info(self):
2478
revision_string = self._transport.get_bytes('last-revision')
2479
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2480
revision_id = cache_utf8.get_cached_utf8(revision_id)
2482
return revno, revision_id
2484
def _write_last_revision_info(self, revno, revision_id):
2485
"""Simply write out the revision id, with no checks.
2487
Use set_last_revision_info to perform this safely.
2489
Does not update the revision_history cache.
2490
Intended to be called by set_last_revision_info and
2491
_write_revision_history.
2493
revision_id = _mod_revision.ensure_null(revision_id)
2494
out_string = '%d %s\n' % (revno, revision_id)
2495
self._transport.put_bytes('last-revision', out_string,
2496
mode=self.bzrdir._get_file_mode())
2499
def set_last_revision_info(self, revno, revision_id):
2500
revision_id = _mod_revision.ensure_null(revision_id)
2501
old_revno, old_revid = self.last_revision_info()
2502
if self._get_append_revisions_only():
2503
self._check_history_violation(revision_id)
2504
self._run_pre_change_branch_tip_hooks(revno, revision_id)
2505
self._write_last_revision_info(revno, revision_id)
2506
self._clear_cached_state()
2507
self._last_revision_info_cache = revno, revision_id
2508
self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2510
def _synchronize_history(self, destination, revision_id):
2511
"""Synchronize last revision and revision history between branches.
2513
:see: Branch._synchronize_history
2515
# XXX: The base Branch has a fast implementation of this method based
2516
# on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2517
# that uses set_revision_history. This class inherits from BzrBranch5,
2518
# but wants the fast implementation, so it calls
2519
# Branch._synchronize_history directly.
2520
Branch._synchronize_history(self, destination, revision_id)
2522
def _check_history_violation(self, revision_id):
2523
last_revision = _mod_revision.ensure_null(self.last_revision())
2524
if _mod_revision.is_null(last_revision):
2526
if last_revision not in self._lefthand_history(revision_id):
2527
raise errors.AppendRevisionsOnlyViolation(self.base)
2529
def _gen_revision_history(self):
2530
"""Generate the revision history from last revision
2532
last_revno, last_revision = self.last_revision_info()
2533
self._extend_partial_history(stop_index=last_revno-1)
2534
return list(reversed(self._partial_revision_history_cache))
2536
def _write_revision_history(self, history):
2537
"""Factored out of set_revision_history.
2539
This performs the actual writing to disk, with format-specific checks.
2540
It is intended to be called by BzrBranch5.set_revision_history.
2542
if len(history) == 0:
2543
last_revision = 'null:'
2545
if history != self._lefthand_history(history[-1]):
2546
raise errors.NotLefthandHistory(history)
2547
last_revision = history[-1]
2548
if self._get_append_revisions_only():
2549
self._check_history_violation(last_revision)
2550
self._write_last_revision_info(len(history), last_revision)
2553
def _set_parent_location(self, url):
2554
"""Set the parent branch"""
2555
self._set_config_location('parent_location', url, make_relative=True)
2558
def _get_parent_location(self):
2559
"""Set the parent branch"""
2560
return self._get_config_location('parent_location')
2563
def _set_all_reference_info(self, info_dict):
2564
"""Replace all reference info stored in a branch.
2566
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
2569
writer = rio.RioWriter(s)
2570
for key, (tree_path, branch_location) in info_dict.iteritems():
2571
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2572
branch_location=branch_location)
2573
writer.write_stanza(stanza)
2574
self._transport.put_bytes('references', s.getvalue())
2575
self._reference_info = info_dict
2578
def _get_all_reference_info(self):
2579
"""Return all the reference info stored in a branch.
2581
:return: A dict of {file_id: (tree_path, branch_location)}
2583
if self._reference_info is not None:
2584
return self._reference_info
2585
rio_file = self._transport.get('references')
2587
stanzas = rio.read_stanzas(rio_file)
2588
info_dict = dict((s['file_id'], (s['tree_path'],
2589
s['branch_location'])) for s in stanzas)
2592
self._reference_info = info_dict
2595
def set_reference_info(self, file_id, tree_path, branch_location):
2596
"""Set the branch location to use for a tree reference.
2598
:param file_id: The file-id of the tree reference.
2599
:param tree_path: The path of the tree reference in the tree.
2600
:param branch_location: The location of the branch to retrieve tree
2603
info_dict = self._get_all_reference_info()
2604
info_dict[file_id] = (tree_path, branch_location)
2605
if None in (tree_path, branch_location):
2606
if tree_path is not None:
2607
raise ValueError('tree_path must be None when branch_location'
2609
if branch_location is not None:
2610
raise ValueError('branch_location must be None when tree_path'
2612
del info_dict[file_id]
2613
self._set_all_reference_info(info_dict)
2615
def get_reference_info(self, file_id):
2616
"""Get the tree_path and branch_location for a tree reference.
2618
:return: a tuple of (tree_path, branch_location)
2620
return self._get_all_reference_info().get(file_id, (None, None))
2622
def reference_parent(self, file_id, path, possible_transports=None):
2623
"""Return the parent branch for a tree-reference file_id.
2625
:param file_id: The file_id of the tree reference
2626
:param path: The path of the file_id in the tree
2627
:return: A branch associated with the file_id
2629
branch_location = self.get_reference_info(file_id)[1]
2630
if branch_location is None:
2631
return Branch.reference_parent(self, file_id, path,
2632
possible_transports)
2633
branch_location = urlutils.join(self.base, branch_location)
2634
return Branch.open(branch_location,
2635
possible_transports=possible_transports)
2637
def set_push_location(self, location):
2638
"""See Branch.set_push_location."""
2639
self._set_config_location('push_location', location)
2641
def set_bound_location(self, location):
2642
"""See Branch.set_push_location."""
2644
config = self.get_config()
2645
if location is None:
2646
if config.get_user_option('bound') != 'True':
2649
config.set_user_option('bound', 'False', warn_masked=True)
2652
self._set_config_location('bound_location', location,
2654
config.set_user_option('bound', 'True', warn_masked=True)
2657
def _get_bound_location(self, bound):
2658
"""Return the bound location in the config file.
2660
Return None if the bound parameter does not match"""
2661
config = self.get_config()
2662
config_bound = (config.get_user_option('bound') == 'True')
2663
if config_bound != bound:
2665
return self._get_config_location('bound_location', config=config)
2667
def get_bound_location(self):
2668
"""See Branch.set_push_location."""
2669
return self._get_bound_location(True)
2671
def get_old_bound_location(self):
2672
"""See Branch.get_old_bound_location"""
2673
return self._get_bound_location(False)
2675
def get_stacked_on_url(self):
2676
# you can always ask for the URL; but you might not be able to use it
2677
# if the repo can't support stacking.
2678
## self._check_stackable_repo()
2679
stacked_url = self._get_config_location('stacked_on_location')
2680
if stacked_url is None:
2681
raise errors.NotStacked(self)
2684
def _get_append_revisions_only(self):
2685
value = self.get_config().get_user_option('append_revisions_only')
2686
return value == 'True'
2689
def generate_revision_history(self, revision_id, last_rev=None,
2691
"""See BzrBranch5.generate_revision_history"""
2692
history = self._lefthand_history(revision_id, last_rev, other_branch)
2693
revno = len(history)
2694
self.set_last_revision_info(revno, revision_id)
2697
def get_rev_id(self, revno, history=None):
2698
"""Find the revision id of the specified revno."""
2700
return _mod_revision.NULL_REVISION
2702
last_revno, last_revision_id = self.last_revision_info()
2703
if revno <= 0 or revno > last_revno:
2704
raise errors.NoSuchRevision(self, revno)
2706
if history is not None:
2707
return history[revno - 1]
2709
index = last_revno - revno
2710
if len(self._partial_revision_history_cache) <= index:
2711
self._extend_partial_history(stop_index=index)
2712
if len(self._partial_revision_history_cache) > index:
2713
return self._partial_revision_history_cache[index]
2715
raise errors.NoSuchRevision(self, revno)
2718
def revision_id_to_revno(self, revision_id):
2719
"""Given a revision id, return its revno"""
2720
if _mod_revision.is_null(revision_id):
2723
index = self._partial_revision_history_cache.index(revision_id)
2725
self._extend_partial_history(stop_revision=revision_id)
2726
index = len(self._partial_revision_history_cache) - 1
2727
if self._partial_revision_history_cache[index] != revision_id:
2728
raise errors.NoSuchRevision(self, revision_id)
2729
return self.revno() - index
2732
class BzrBranch7(BzrBranch8):
2733
"""A branch with support for a fallback repository."""
2735
def set_reference_info(self, file_id, tree_path, branch_location):
2736
Branch.set_reference_info(self, file_id, tree_path, branch_location)
2738
def get_reference_info(self, file_id):
2739
Branch.get_reference_info(self, file_id)
2741
def reference_parent(self, file_id, path, possible_transports=None):
2742
return Branch.reference_parent(self, file_id, path,
2743
possible_transports)
2746
class BzrBranch6(BzrBranch7):
2747
"""See BzrBranchFormat6 for the capabilities of this branch.
2749
This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2753
def get_stacked_on_url(self):
2754
raise errors.UnstackableBranchFormat(self._format, self.base)
1928
2757
######################################################################
2201
3025
if graph is None:
2202
3026
graph = self.target.repository.get_graph()
2203
3027
this_revno, this_last_revision = \
2204
self.target.last_revision_info()
2205
stop_revno = graph.find_distance_to_null(
2206
stop_revision, [(other_last_revision, other_revno),
2207
(this_last_revision, this_revno)])
3028
self.target.last_revision_info()
3029
stop_revno = graph.find_distance_to_null(stop_revision,
3030
[(other_last_revision, other_revno),
3031
(this_last_revision, this_revno)])
2208
3032
self.target.set_last_revision_info(stop_revno, stop_revision)
3034
self.source.unlock()
2210
3036
def pull(self, overwrite=False, stop_revision=None,
2211
possible_transports=None, run_hooks=True,
2212
_override_hook_target=None, local=False,
2214
"""Pull from source into self, updating my master if any.
2216
:param run_hooks: Private parameter - if false, this branch
2217
is being called because it's the master of the primary branch,
2218
so it should not run its hooks.
2220
with contextlib.ExitStack() as exit_stack:
2221
exit_stack.enter_context(self.target.lock_write())
2222
bound_location = self.target.get_bound_location()
2223
if local and not bound_location:
2224
raise errors.LocalRequiresBoundBranch()
2225
master_branch = None
2226
source_is_master = False
2228
# bound_location comes from a config file, some care has to be
2229
# taken to relate it to source.user_url
2230
normalized = urlutils.normalize_url(bound_location)
2232
relpath = self.source.user_transport.relpath(normalized)
2233
source_is_master = (relpath == '')
2234
except (errors.PathNotChild, urlutils.InvalidURL):
2235
source_is_master = False
2236
if not local and bound_location and not source_is_master:
2237
# not pulling from master, so we need to update master.
2238
master_branch = self.target.get_master_branch(
2239
possible_transports)
2240
exit_stack.enter_context(master_branch.lock_write())
2242
# pull from source into master.
2244
self.source, overwrite, stop_revision, run_hooks=False,
2245
tag_selector=tag_selector)
2247
overwrite, stop_revision, _hook_master=master_branch,
2248
run_hooks=run_hooks,
2249
_override_hook_target=_override_hook_target,
2250
merge_tags_to_master=not source_is_master,
2251
tag_selector=tag_selector)
2253
def push(self, overwrite=False, stop_revision=None, lossy=False,
2254
_override_hook_source_branch=None, tag_selector=None):
2255
"""See InterBranch.push.
2257
This is the basic concrete implementation of push()
2259
:param _override_hook_source_branch: If specified, run the hooks
2260
passing this Branch as the source, rather than self. This is for
2261
use of RemoteBranch, where push is delegated to the underlying
2265
raise errors.LossyPushToSameVCS(self.source, self.target)
2266
# TODO: Public option to disable running hooks - should be trivial but
2270
if _override_hook_source_branch:
2271
result.source_branch = _override_hook_source_branch
2272
for hook in Branch.hooks['post_push']:
2275
with self.source.lock_read(), self.target.lock_write():
2276
bound_location = self.target.get_bound_location()
2277
if bound_location and self.target.base != bound_location:
2278
# there is a master branch.
2280
# XXX: Why the second check? Is it even supported for a branch
2281
# to be bound to itself? -- mbp 20070507
2282
master_branch = self.target.get_master_branch()
2283
with master_branch.lock_write():
2284
# push into the master from the source branch.
2285
master_inter = InterBranch.get(self.source, master_branch)
2286
master_inter._basic_push(
2287
overwrite, stop_revision, tag_selector=tag_selector)
2288
# and push into the target branch from the source. Note
2289
# that we push from the source branch again, because it's
2290
# considered the highest bandwidth repository.
2291
result = self._basic_push(
2292
overwrite, stop_revision, tag_selector=tag_selector)
2293
result.master_branch = master_branch
2294
result.local_branch = self.target
2297
master_branch = None
2299
result = self._basic_push(
2300
overwrite, stop_revision, tag_selector=tag_selector)
2301
# TODO: Why set master_branch and local_branch if there's no
2302
# binding? Maybe cleaner to just leave them unset? -- mbp
2304
result.master_branch = self.target
2305
result.local_branch = None
2309
def _basic_push(self, overwrite, stop_revision, tag_selector=None):
2310
"""Basic implementation of push without bound branches or hooks.
2312
Must be called with source read locked and target write locked.
2314
result = BranchPushResult()
2315
result.source_branch = self.source
2316
result.target_branch = self.target
2317
result.old_revno, result.old_revid = self.target.last_revision_info()
2318
overwrite = _fix_overwrite_type(overwrite)
2319
if result.old_revid != stop_revision:
2320
# We assume that during 'push' this repository is closer than
2322
graph = self.source.repository.get_graph(self.target.repository)
2323
self._update_revisions(
2324
stop_revision, overwrite=("history" in overwrite), graph=graph)
2325
if self.source._push_should_merge_tags():
2326
result.tag_updates, result.tag_conflicts = (
2327
self.source.tags.merge_to(
2328
self.target.tags, "tags" in overwrite, selector=tag_selector))
2329
self.update_references()
2330
result.new_revno, result.new_revid = self.target.last_revision_info()
2333
def _pull(self, overwrite=False, stop_revision=None,
2334
possible_transports=None, _hook_master=None, run_hooks=True,
2335
_override_hook_target=None, local=False,
2336
merge_tags_to_master=True, tag_selector=None):
3037
possible_transports=None, _hook_master=None, run_hooks=True,
3038
_override_hook_target=None, local=False):
2337
3039
"""See Branch.pull.
2339
This function is the core worker, used by GenericInterBranch.pull to
2340
avoid duplication when pulling source->master and source->local.
2342
3041
:param _hook_master: Private parameter - set the branch to
2343
3042
be supplied as the master to pull hooks.
2344
3043
:param run_hooks: Private parameter - if false, this branch
2345
3044
is being called because it's the master of the primary branch,
2346
3045
so it should not run its hooks.
2347
is being called because it's the master of the primary branch,
2348
so it should not run its hooks.
2349
3046
:param _override_hook_target: Private parameter - set the branch to be
2350
3047
supplied as the target_branch to pull hooks.
2351
3048
:param local: Only update the local branch, and not the bound branch.