401
407
with self.lock_write():
402
408
self._format._update_feature_flags(updated_flags)
403
self.control_transport.put_bytes('format', self._format.as_string())
409
self.control_transport.put_bytes(
410
'format', self._format.as_string())
412
def _get_tags_bytes(self):
413
"""Get the bytes of a serialised tags dict.
415
Note that not all branches support tags, nor do all use the same tags
416
logic: this method is specific to BasicTags. Other tag implementations
417
may use the same method name and behave differently, safely, because
418
of the double-dispatch via
419
format.make_tags->tags_instance->get_tags_dict.
421
:return: The bytes of the tags file.
422
:seealso: Branch._set_tags_bytes.
424
with self.lock_read():
425
if self._tags_bytes is None:
426
self._tags_bytes = self._transport.get_bytes('tags')
427
return self._tags_bytes
429
def _set_tags_bytes(self, bytes):
430
"""Mirror method for _get_tags_bytes.
432
:seealso: Branch._get_tags_bytes.
434
with self.lock_write():
435
self._tags_bytes = bytes
436
return self._transport.put_bytes('tags', bytes)
438
def _clear_cached_state(self):
439
super(BzrBranch, self)._clear_cached_state()
440
self._tags_bytes = None
442
def reconcile(self, thorough=True):
443
"""Make sure the data stored in this branch is consistent."""
444
from .reconcile import BranchReconciler
445
with self.lock_write():
446
reconciler = BranchReconciler(self, thorough=thorough)
447
return reconciler.reconcile()
449
def set_reference_info(self, file_id, branch_location, path=None):
450
"""Set the branch location to use for a tree reference."""
451
raise errors.UnsupportedOperation(self.set_reference_info, self)
453
def get_reference_info(self, file_id, path=None):
454
"""Get the tree_path and branch_location for a tree reference."""
455
raise errors.UnsupportedOperation(self.get_reference_info, self)
457
def reference_parent(self, file_id, path, possible_transports=None):
458
"""Return the parent branch for a tree-reference.
460
:param path: The path of the nested tree in the tree
461
:return: A branch associated with the nested tree
464
branch_location = self.get_reference_info(file_id)[0]
465
except errors.UnsupportedOperation:
466
branch_location = None
467
if branch_location is None:
469
return Branch.open_from_transport(
470
self.controldir.root_transport.clone(path),
471
possible_transports=possible_transports)
472
except errors.NotBranchError:
476
urlutils.strip_segment_parameters(self.user_url), branch_location),
477
possible_transports=possible_transports)
406
480
class BzrBranch8(BzrBranch):
424
498
raise AssertionError(
425
499
"'transform_fallback_location' hook %s returned "
426
500
"None, not a URL." % hook_name)
427
self._activate_fallback_location(url,
428
possible_transports=possible_transports)
501
self._activate_fallback_location(
502
url, possible_transports=possible_transports)
430
504
def __init__(self, *args, **kwargs):
431
505
self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
453
527
"""Generate the revision history from last revision
455
529
last_revno, last_revision = self.last_revision_info()
456
self._extend_partial_history(stop_index=last_revno-1)
530
self._extend_partial_history(stop_index=last_revno - 1)
457
531
return list(reversed(self._partial_revision_history_cache))
459
533
def _set_parent_location(self, url):
460
534
"""Set the parent branch"""
461
535
with self.lock_write():
462
self._set_config_location('parent_location', url, make_relative=True)
536
self._set_config_location(
537
'parent_location', url, make_relative=True)
464
539
def _get_parent_location(self):
465
540
"""Set the parent branch"""
469
544
def _set_all_reference_info(self, info_dict):
470
545
"""Replace all reference info stored in a branch.
472
:param info_dict: A dict of {file_id: (tree_path, branch_location)}
547
:param info_dict: A dict of {file_id: (branch_location, tree_path)}
475
550
writer = rio.RioWriter(s)
476
for key, (tree_path, branch_location) in viewitems(info_dict):
477
stanza = rio.Stanza(file_id=key, tree_path=tree_path,
551
for file_id, (branch_location, tree_path) in info_dict.items():
552
stanza = rio.Stanza(file_id=file_id,
478
553
branch_location=branch_location)
554
if tree_path is not None:
555
stanza.add('tree_path', tree_path)
479
556
writer.write_stanza(stanza)
480
557
with self.lock_write():
481
558
self._transport.put_bytes('references', s.getvalue())
484
561
def _get_all_reference_info(self):
485
562
"""Return all the reference info stored in a branch.
487
:return: A dict of {file_id: (tree_path, branch_location)}
564
:return: A dict of {tree_path: (branch_location, file_id)}
489
566
with self.lock_read():
490
567
if self._reference_info is not None:
491
568
return self._reference_info
492
rio_file = self._transport.get('references')
494
stanzas = rio.read_stanzas(rio_file)
495
info_dict = dict((s['file_id'], (s['tree_path'],
496
s['branch_location'])) for s in stanzas)
570
with self._transport.get('references') as rio_file:
571
stanzas = rio.read_stanzas(rio_file)
573
s['file_id'].encode('utf-8'): (
574
s['branch_location'],
575
s['tree_path'] if 'tree_path' in s else None)
577
except errors.NoSuchFile:
499
579
self._reference_info = info_dict
502
def set_reference_info(self, file_id, tree_path, branch_location):
582
def set_reference_info(self, file_id, branch_location, tree_path=None):
503
583
"""Set the branch location to use for a tree reference.
585
:param branch_location: The location of the branch to retrieve tree
505
587
:param file_id: The file-id of the tree reference.
506
588
:param tree_path: The path of the tree reference in the tree.
507
:param branch_location: The location of the branch to retrieve tree
510
590
info_dict = self._get_all_reference_info()
511
info_dict[file_id] = (tree_path, branch_location)
512
if None in (tree_path, branch_location):
513
if tree_path is not None:
514
raise ValueError('tree_path must be None when branch_location'
516
if branch_location is not None:
517
raise ValueError('branch_location must be None when tree_path'
591
info_dict[file_id] = (branch_location, tree_path)
592
if branch_location is None:
519
593
del info_dict[file_id]
520
594
self._set_all_reference_info(info_dict)
522
596
def get_reference_info(self, file_id):
523
597
"""Get the tree_path and branch_location for a tree reference.
525
:return: a tuple of (tree_path, branch_location)
599
:return: a tuple of (branch_location, tree_path)
527
601
return self._get_all_reference_info().get(file_id, (None, None))
529
def reference_parent(self, file_id, path, possible_transports=None):
530
"""Return the parent branch for a tree-reference file_id.
532
:param file_id: The file_id of the tree reference
533
:param path: The path of the file_id in the tree
534
:return: A branch associated with the file_id
536
branch_location = self.get_reference_info(file_id)[1]
537
if branch_location is None:
538
return Branch.reference_parent(self, file_id, path,
540
branch_location = urlutils.join(self.user_url, branch_location)
541
return Branch.open(branch_location,
542
possible_transports=possible_transports)
544
603
def set_push_location(self, location):
545
604
"""See Branch.set_push_location."""
546
605
self._set_config_location('push_location', location)
637
696
class BzrBranch7(BzrBranch8):
638
697
"""A branch with support for a fallback repository."""
640
def set_reference_info(self, file_id, tree_path, branch_location):
641
Branch.set_reference_info(self, file_id, tree_path, branch_location)
643
def get_reference_info(self, file_id):
644
Branch.get_reference_info(self, file_id)
646
def reference_parent(self, file_id, path, possible_transports=None):
647
return Branch.reference_parent(self, file_id, path,
699
def set_reference_info(self, file_id, branch_location, tree_path=None):
700
super(BzrBranch7, self).set_reference_info(
701
file_id, branch_location, tree_path)
702
format_string = BzrBranchFormat8.get_format_string()
703
mutter('Upgrading branch to format %r', format_string)
704
self._transport.put_bytes('format', format_string)
651
707
class BzrBranch6(BzrBranch7):
676
732
raise errors.NotBranchError(path=name, controldir=controldir)
678
734
format_string = transport.get_bytes("format")
679
# GZ 2017-06-09: Where should format strings get decoded...
680
format_text = format_string.decode("ascii")
681
735
except errors.NoSuchFile:
682
736
raise errors.NotBranchError(
683
737
path=transport.base, controldir=controldir)
684
return klass._find_format(format_registry, 'branch', format_text)
738
return klass._find_format(format_registry, 'branch', format_string)
686
740
def _branch_class(self):
687
741
"""What class to instantiate on open calls."""
723
777
control_files.unlock()
724
778
branch = self.open(a_controldir, name, _found=True,
725
found_repository=repository)
779
found_repository=repository)
726
780
self._run_post_branch_init_hooks(a_controldir, name, branch)
729
def open(self, a_controldir, name=None, _found=False, ignore_fallbacks=False,
730
found_repository=None, possible_transports=None):
783
def open(self, a_controldir, name=None, _found=False,
784
ignore_fallbacks=False, found_repository=None,
785
possible_transports=None):
731
786
"""See BranchFormat.open()."""
733
788
name = a_controldir._get_selected_branch()
735
790
format = BranchFormatMetadir.find_format(a_controldir, name=name)
736
791
if format.__class__ != self.__class__:
737
792
raise AssertionError("wrong format %r found for %r" %
739
794
transport = a_controldir.get_branch_transport(None, name=name)
741
796
control_files = lockable_files.LockableFiles(transport, 'lock',
743
798
if found_repository is None:
744
799
found_repository = a_controldir.find_repository()
745
return self._branch_class()(_format=self,
746
_control_files=control_files,
748
a_controldir=a_controldir,
749
_repository=found_repository,
750
ignore_fallbacks=ignore_fallbacks,
751
possible_transports=possible_transports)
800
return self._branch_class()(
801
_format=self, _control_files=control_files, name=name,
802
a_controldir=a_controldir, _repository=found_repository,
803
ignore_fallbacks=ignore_fallbacks,
804
possible_transports=possible_transports)
752
805
except errors.NoSuchFile:
753
raise errors.NotBranchError(path=transport.base, controldir=a_controldir)
806
raise errors.NotBranchError(
807
path=transport.base, controldir=a_controldir)
756
810
def _matchingcontroldir(self):
767
821
def check_support_status(self, allow_unsupported, recommend_upgrade=True,
769
BranchFormat.check_support_status(self,
770
allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
772
bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
823
BranchFormat.check_support_status(
824
self, allow_unsupported=allow_unsupported,
825
recommend_upgrade=recommend_upgrade, basedir=basedir)
826
bzrdir.BzrFormat.check_support_status(
827
self, allow_unsupported=allow_unsupported,
773
828
recommend_upgrade=recommend_upgrade, basedir=basedir)
799
854
def initialize(self, a_controldir, name=None, repository=None,
800
855
append_revisions_only=None):
801
856
"""Create a branch of this format in a_controldir."""
802
utf8_files = [('last-revision', '0 null:\n'),
804
self._get_initial_config(append_revisions_only)),
807
return self._initialize_helper(a_controldir, utf8_files, name, repository)
858
('last-revision', b'0 null:\n'),
859
('branch.conf', self._get_initial_config(append_revisions_only)),
862
return self._initialize_helper(
863
a_controldir, utf8_files, name, repository)
809
865
def make_tags(self, branch):
810
866
"""See breezy.branch.BranchFormat.make_tags()."""
832
890
def initialize(self, a_controldir, name=None, repository=None,
833
891
append_revisions_only=None):
834
892
"""Create a branch of this format in a_controldir."""
835
utf8_files = [('last-revision', '0 null:\n'),
893
utf8_files = [('last-revision', b'0 null:\n'),
837
895
self._get_initial_config(append_revisions_only)),
841
return self._initialize_helper(a_controldir, utf8_files, name, repository)
899
return self._initialize_helper(
900
a_controldir, utf8_files, name, repository)
843
902
def make_tags(self, branch):
844
903
"""See breezy.branch.BranchFormat.make_tags()."""
920
982
def get_reference(self, a_controldir, name=None):
921
983
"""See BranchFormat.get_reference()."""
922
984
transport = a_controldir.get_branch_transport(None, name=name)
923
return transport.get_bytes('location')
985
url = urlutils.strip_segment_parameters(a_controldir.user_url)
986
return urlutils.join(
987
url, transport.get_bytes('location').decode('utf-8'))
989
def _write_reference(self, a_controldir, transport, to_branch):
990
to_url = to_branch.user_url
991
# Ideally, we'd write a relative path here for the benefit of colocated
992
# branches - so that moving a control directory doesn't break
993
# any references to colocated branches. Unfortunately, bzr
994
# does not support relative URLs. See pad.lv/1803845 -- jelmer
995
# to_url = urlutils.relative_url(
996
# a_controldir.user_url, to_branch.user_url)
997
transport.put_bytes('location', to_url.encode('utf-8'))
925
999
def set_reference(self, a_controldir, name, to_branch):
926
1000
"""See BranchFormat.set_reference()."""
927
1001
transport = a_controldir.get_branch_transport(None, name=name)
928
location = transport.put_bytes('location', to_branch.base)
1002
self._write_reference(a_controldir, transport, to_branch)
930
1004
def initialize(self, a_controldir, name=None, target_branch=None,
931
repository=None, append_revisions_only=None):
1005
repository=None, append_revisions_only=None):
932
1006
"""Create a branch of this format in a_controldir."""
933
1007
if target_branch is None:
934
1008
# this format does not implement branch itself, thus the implicit
940
1014
if name is None:
941
1015
name = a_controldir._get_selected_branch()
942
1016
branch_transport = a_controldir.get_branch_transport(self, name=name)
943
branch_transport.put_bytes('location',
944
target_branch.user_url)
1017
self._write_reference(a_controldir, branch_transport, target_branch)
945
1018
branch_transport.put_bytes('format', self.as_string())
946
1019
branch = self.open(a_controldir, name, _found=True,
947
possible_transports=[target_branch.controldir.root_transport])
1020
possible_transports=[target_branch.controldir.root_transport])
948
1021
self._run_post_branch_init_hooks(a_controldir, name, branch)
951
1024
def _make_reference_clone_function(format, a_branch):
952
1025
"""Create a clone() routine for a branch dynamically."""
953
1026
def clone(to_bzrdir, revision_id=None,
954
repository_policy=None):
1027
repository_policy=None, tag_selector=None):
955
1028
"""See Branch.clone()."""
956
1029
return format.initialize(to_bzrdir, target_branch=a_branch)
957
1030
# cannot obey revision_id limits when cloning a reference ...
981
1054
format = BranchFormatMetadir.find_format(a_controldir, name=name)
982
1055
if format.__class__ != self.__class__:
983
1056
raise AssertionError("wrong format %r found for %r" %
985
1058
if location is None:
986
1059
location = self.get_reference(a_controldir, name)
987
1060
real_bzrdir = controldir.ControlDir.open(
988
1061
location, possible_transports=possible_transports)
989
result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
1062
result = real_bzrdir.open_branch(
1063
ignore_fallbacks=ignore_fallbacks,
990
1064
possible_transports=possible_transports)
991
1065
# this changes the behaviour of result.clone to create a new reference
992
1066
# rather than a copy of the content of the branch.
1011
1085
# Copy source data into target
1012
1086
new_branch._write_last_revision_info(*branch.last_revision_info())
1013
new_branch.lock_write()
1087
with new_branch.lock_write():
1015
1088
new_branch.set_parent(branch.get_parent())
1016
1089
new_branch.set_bound_location(branch.get_bound_location())
1017
1090
new_branch.set_push_location(branch.get_push_location())
1021
1092
# New branch has no tags by default
1022
1093
new_branch.tags._set_tag_dict({})
1024
1095
# Copying done; now update target format
1025
new_branch._transport.put_bytes('format',
1096
new_branch._transport.put_bytes(
1097
'format', format.as_string(),
1027
1098
mode=new_branch.controldir._get_file_mode())
1029
1100
# Clean up old files
1030
1101
new_branch._transport.delete('revision-history')
1102
with branch.lock_write():
1034
1104
branch.set_parent(None)
1035
1105
except errors.NoSuchFile:
1037
1107
branch.set_bound_location(None)
1042
1110
class Converter6to7(object):