245
243
def set_last_revision_info(self, revno, revision_id):
246
244
if not revision_id or not isinstance(revision_id, bytes):
247
245
raise errors.InvalidRevisionId(
248
revision_id=revision_id, branch=self)
246
revision_id=revision_id, branch=self)
249
247
revision_id = _mod_revision.ensure_null(revision_id)
250
248
with self.lock_write():
251
249
old_revno, old_revid = self.last_revision_info()
402
396
revision_id = _mod_revision.ensure_null(revision_id)
403
397
out_string = b'%d %s\n' % (revno, revision_id)
404
398
self._transport.put_bytes('last-revision', out_string,
405
mode=self.controldir._get_file_mode())
399
mode=self.controldir._get_file_mode())
407
401
def update_feature_flags(self, updated_flags):
408
402
"""Update the feature flags for this branch.
445
438
super(BzrBranch, self)._clear_cached_state()
446
439
self._tags_bytes = None
448
def reconcile(self, thorough=True):
449
"""Make sure the data stored in this branch is consistent."""
450
from .reconcile import BranchReconciler
451
with self.lock_write():
452
reconciler = BranchReconciler(self, thorough=thorough)
453
return reconciler.reconcile()
455
def set_reference_info(self, file_id, branch_location, path=None):
456
"""Set the branch location to use for a tree reference."""
457
raise errors.UnsupportedOperation(self.set_reference_info, self)
459
def get_reference_info(self, file_id, path=None):
460
"""Get the tree_path and branch_location for a tree reference."""
461
raise errors.UnsupportedOperation(self.get_reference_info, self)
463
def reference_parent(self, file_id, path, possible_transports=None):
464
"""Return the parent branch for a tree-reference.
466
:param path: The path of the nested tree in the tree
467
:return: A branch associated with the nested tree
470
branch_location = self.get_reference_info(file_id)[0]
471
except errors.UnsupportedOperation:
472
branch_location = None
473
if branch_location is None:
475
return Branch.open_from_transport(
476
self.controldir.root_transport.clone(path),
477
possible_transports=possible_transports)
478
except errors.NotBranchError:
482
urlutils.strip_segment_parameters(self.user_url), branch_location),
483
possible_transports=possible_transports)
486
442
class BzrBranch8(BzrBranch):
487
443
"""A branch that stores tree-reference locations."""
504
460
raise AssertionError(
505
461
"'transform_fallback_location' hook %s returned "
506
462
"None, not a URL." % hook_name)
507
self._activate_fallback_location(
508
url, possible_transports=possible_transports)
463
self._activate_fallback_location(url,
464
possible_transports=possible_transports)
510
466
def __init__(self, *args, **kwargs):
511
467
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
533
489
"""Generate the revision history from last revision
535
491
last_revno, last_revision = self.last_revision_info()
536
self._extend_partial_history(stop_index=last_revno - 1)
492
self._extend_partial_history(stop_index=last_revno-1)
537
493
return list(reversed(self._partial_revision_history_cache))
539
495
def _set_parent_location(self, url):
540
496
"""Set the parent branch"""
541
497
with self.lock_write():
542
self._set_config_location(
543
'parent_location', url, make_relative=True)
498
self._set_config_location('parent_location', url, make_relative=True)
545
500
def _get_parent_location(self):
546
501
"""Set the parent branch"""
550
505
def _set_all_reference_info(self, info_dict):
551
506
"""Replace all reference info stored in a branch.
553
:param info_dict: A dict of {file_id: (branch_location, tree_path)}
508
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
556
511
writer = rio.RioWriter(s)
557
for file_id, (branch_location, tree_path) in viewitems(info_dict):
558
stanza = rio.Stanza(file_id=file_id,
512
for tree_path, ( branch_location, file_id) in viewitems(info_dict):
513
stanza = rio.Stanza(tree_path=tree_path,
559
514
branch_location=branch_location)
560
if tree_path is not None:
561
stanza.add('tree_path', tree_path)
515
if file_id is not None:
516
stanza.add('file_id', file_id)
562
517
writer.write_stanza(stanza)
563
518
with self.lock_write():
564
519
self._transport.put_bytes('references', s.getvalue())
572
527
with self.lock_read():
573
528
if self._reference_info is not None:
574
529
return self._reference_info
576
with self._transport.get('references') as rio_file:
577
stanzas = rio.read_stanzas(rio_file)
579
s['file_id'].encode('utf-8'): (
580
s['branch_location'],
581
s['tree_path'] if 'tree_path' in s else None)
583
except errors.NoSuchFile:
530
with self._transport.get('references') as rio_file:
531
stanzas = rio.read_stanzas(rio_file)
534
s['branch_location'],
535
s['file_id'].encode('ascii') if 'file_id' in s else None)
585
537
self._reference_info = info_dict
588
def set_reference_info(self, file_id, branch_location, tree_path=None):
540
def set_reference_info(self, tree_path, branch_location, file_id=None):
589
541
"""Set the branch location to use for a tree reference.
543
:param tree_path: The path of the tree reference in the tree.
591
544
:param branch_location: The location of the branch to retrieve tree
593
546
:param file_id: The file-id of the tree reference.
594
:param tree_path: The path of the tree reference in the tree.
596
548
info_dict = self._get_all_reference_info()
597
info_dict[file_id] = (branch_location, tree_path)
549
info_dict[tree_path] = (branch_location, file_id)
598
550
if branch_location is None:
599
del info_dict[file_id]
551
del info_dict[tree_path]
600
552
self._set_all_reference_info(info_dict)
602
def get_reference_info(self, file_id):
554
def get_reference_info(self, path):
603
555
"""Get the tree_path and branch_location for a tree reference.
605
:return: a tuple of (branch_location, tree_path)
607
return self._get_all_reference_info().get(file_id, (None, None))
557
:return: a tuple of (branch_location, file_id)
559
return self._get_all_reference_info().get(path, (None, None))
561
def reference_parent(self, path, file_id=None, possible_transports=None):
562
"""Return the parent branch for a tree-reference file_id.
564
:param file_id: The file_id of the tree reference
565
:param path: The path of the file_id in the tree
566
:return: A branch associated with the file_id
568
branch_location = self.get_reference_info(path)[0]
569
if branch_location is None:
570
return Branch.reference_parent(self, path, file_id,
572
branch_location = urlutils.join(self.user_url, branch_location)
573
return Branch.open(branch_location,
574
possible_transports=possible_transports)
609
576
def set_push_location(self, location):
610
577
"""See Branch.set_push_location."""
646
614
def get_stacked_on_url(self):
647
615
# you can always ask for the URL; but you might not be able to use it
648
616
# if the repo can't support stacking.
649
# self._check_stackable_repo()
617
## self._check_stackable_repo()
650
618
# stacked_on_location is only ever defined in branch.conf, so don't
651
619
# waste effort reading the whole stack of config files.
652
620
conf = _mod_config.BranchOnlyStack(self)
693
661
self._extend_partial_history(stop_revision=revision_id)
694
662
except errors.RevisionNotPresent as e:
695
663
raise errors.GhostRevisionsHaveNoRevno(
696
revision_id, e.revision_id)
664
revision_id, e.revision_id)
697
665
index = len(self._partial_revision_history_cache) - 1
699
667
raise errors.NoSuchRevision(self, revision_id)
705
673
class BzrBranch7(BzrBranch8):
706
674
"""A branch with support for a fallback repository."""
708
def set_reference_info(self, file_id, branch_location, tree_path=None):
709
super(BzrBranch7, self).set_reference_info(
710
file_id, branch_location, tree_path)
711
format_string = BzrBranchFormat8.get_format_string()
712
mutter('Upgrading branch to format %r', format_string)
713
self._transport.put_bytes('format', format_string)
676
def set_reference_info(self, tree_path, branch_location, file_id=None):
677
Branch.set_reference_info(self, file_id, tree_path, branch_location)
679
def get_reference_info(self, path):
680
Branch.get_reference_info(self, path)
682
def reference_parent(self, path, file_id=None, possible_transports=None):
683
return Branch.reference_parent(self, path, file_id, possible_transports)
716
686
class BzrBranch6(BzrBranch7):
786
756
control_files.unlock()
787
757
branch = self.open(a_controldir, name, _found=True,
788
found_repository=repository)
758
found_repository=repository)
789
759
self._run_post_branch_init_hooks(a_controldir, name, branch)
792
def open(self, a_controldir, name=None, _found=False,
793
ignore_fallbacks=False, found_repository=None,
794
possible_transports=None):
762
def open(self, a_controldir, name=None, _found=False, ignore_fallbacks=False,
763
found_repository=None, possible_transports=None):
795
764
"""See BranchFormat.open()."""
797
766
name = a_controldir._get_selected_branch()
799
768
format = BranchFormatMetadir.find_format(a_controldir, name=name)
800
769
if format.__class__ != self.__class__:
801
770
raise AssertionError("wrong format %r found for %r" %
803
772
transport = a_controldir.get_branch_transport(None, name=name)
805
774
control_files = lockable_files.LockableFiles(transport, 'lock',
807
776
if found_repository is None:
808
777
found_repository = a_controldir.find_repository()
809
return self._branch_class()(
810
_format=self, _control_files=control_files, name=name,
811
a_controldir=a_controldir, _repository=found_repository,
812
ignore_fallbacks=ignore_fallbacks,
813
possible_transports=possible_transports)
778
return self._branch_class()(_format=self,
779
_control_files=control_files,
781
a_controldir=a_controldir,
782
_repository=found_repository,
783
ignore_fallbacks=ignore_fallbacks,
784
possible_transports=possible_transports)
814
785
except errors.NoSuchFile:
815
raise errors.NotBranchError(
816
path=transport.base, controldir=a_controldir)
786
raise errors.NotBranchError(path=transport.base, controldir=a_controldir)
819
789
def _matchingcontroldir(self):
830
800
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
832
BranchFormat.check_support_status(
833
self, allow_unsupported=allow_unsupported,
834
recommend_upgrade=recommend_upgrade, basedir=basedir)
835
bzrdir.BzrFormat.check_support_status(
836
self, allow_unsupported=allow_unsupported,
802
BranchFormat.check_support_status(self,
803
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
805
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
837
806
recommend_upgrade=recommend_upgrade, basedir=basedir)
863
832
def initialize(self, a_controldir, name=None, repository=None,
864
833
append_revisions_only=None):
865
834
"""Create a branch of this format in a_controldir."""
867
('last-revision', b'0 null:\n'),
868
('branch.conf', self._get_initial_config(append_revisions_only)),
871
return self._initialize_helper(
872
a_controldir, utf8_files, name, repository)
835
utf8_files = [('last-revision', b'0 null:\n'),
837
self._get_initial_config(append_revisions_only)),
840
return self._initialize_helper(a_controldir, utf8_files, name, repository)
874
842
def make_tags(self, branch):
875
843
"""See breezy.branch.BranchFormat.make_tags()."""
906
872
('references', b'')
908
return self._initialize_helper(
909
a_controldir, utf8_files, name, repository)
874
return self._initialize_helper(a_controldir, utf8_files, name, repository)
911
876
def make_tags(self, branch):
912
877
"""See breezy.branch.BranchFormat.make_tags()."""
991
953
def get_reference(self, a_controldir, name=None):
992
954
"""See BranchFormat.get_reference()."""
993
955
transport = a_controldir.get_branch_transport(None, name=name)
994
url = urlutils.strip_segment_parameters(a_controldir.user_url)
995
return urlutils.join(
996
url, transport.get_bytes('location').decode('utf-8'))
956
url = urlutils.split_segment_parameters(a_controldir.user_url)[0]
957
return urlutils.join(url, transport.get_bytes('location').decode('utf-8'))
998
959
def _write_reference(self, a_controldir, transport, to_branch):
999
960
to_url = to_branch.user_url
1000
# Ideally, we'd write a relative path here for the benefit of colocated
1001
# branches - so that moving a control directory doesn't break
1002
# any references to colocated branches. Unfortunately, bzr
1003
# does not support relative URLs. See pad.lv/1803845 -- jelmer
1004
# to_url = urlutils.relative_url(
1005
# a_controldir.user_url, to_branch.user_url)
961
if a_controldir.control_url == to_branch.controldir.control_url:
962
# Write relative paths for colocated branches, but absolute
963
# paths for everything else. This is for the benefit
964
# of older bzr versions that don't support relative paths.
965
to_url = urlutils.relative_url(a_controldir.user_url, to_branch.user_url)
1006
966
transport.put_bytes('location', to_url.encode('utf-8'))
1008
968
def set_reference(self, a_controldir, name, to_branch):
1011
971
self._write_reference(a_controldir, transport, to_branch)
1013
973
def initialize(self, a_controldir, name=None, target_branch=None,
1014
repository=None, append_revisions_only=None):
974
repository=None, append_revisions_only=None):
1015
975
"""Create a branch of this format in a_controldir."""
1016
976
if target_branch is None:
1017
977
# this format does not implement branch itself, thus the implicit
1026
986
self._write_reference(a_controldir, branch_transport, target_branch)
1027
987
branch_transport.put_bytes('format', self.as_string())
1028
988
branch = self.open(a_controldir, name, _found=True,
1029
possible_transports=[target_branch.controldir.root_transport])
989
possible_transports=[target_branch.controldir.root_transport])
1030
990
self._run_post_branch_init_hooks(a_controldir, name, branch)
1033
993
def _make_reference_clone_function(format, a_branch):
1034
994
"""Create a clone() routine for a branch dynamically."""
1035
def clone(to_bzrdir, revision_id=None, repository_policy=None, name=None,
995
def clone(to_bzrdir, revision_id=None,
996
repository_policy=None):
1037
997
"""See Branch.clone()."""
1038
return format.initialize(to_bzrdir, target_branch=a_branch, name=name)
998
return format.initialize(to_bzrdir, target_branch=a_branch)
1039
999
# cannot obey revision_id limits when cloning a reference ...
1040
1000
# FIXME RBC 20060210 either nuke revision_id for clone, or
1041
1001
# emit some sort of warning/error to the caller ?!
1063
1023
format = BranchFormatMetadir.find_format(a_controldir, name=name)
1064
1024
if format.__class__ != self.__class__:
1065
1025
raise AssertionError("wrong format %r found for %r" %
1067
1027
if location is None:
1068
1028
location = self.get_reference(a_controldir, name)
1069
1029
real_bzrdir = controldir.ControlDir.open(
1070
1030
location, possible_transports=possible_transports)
1071
result = real_bzrdir.open_branch(
1072
ignore_fallbacks=ignore_fallbacks,
1031
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
1073
1032
possible_transports=possible_transports)
1074
1033
# this changes the behaviour of result.clone to create a new reference
1075
1034
# rather than a copy of the content of the branch.