76
92
return self._lookup_revno(self.new_revid)
79
class LocalGitTagDict(tag.BasicTags):
80
"""Dictionary with tags in a local repository."""
95
class GitTags(tag.BasicTags):
96
"""Ref-based tag dictionary."""
82
98
def __init__(self, branch):
83
99
self.branch = branch
84
100
self.repository = branch.repository
103
raise NotImplementedError(self.get_refs)
105
def _iter_tag_refs(self, refs):
106
raise NotImplementedError(self._iter_tag_refs)
108
def _merge_to_remote_git(self, target_repo, new_refs, overwrite=False):
111
def get_changed_refs(old_refs):
113
for k, v in new_refs.iteritems():
116
name = ref_to_tag_name(k)
117
if old_refs.get(k) == v:
119
elif overwrite or not k in old_refs:
121
updates[name] = target_repo.lookup_foreign_revision_id(v)
123
conflicts.append((name, v, old_refs[k]))
125
target_repo.bzrdir.send_pack(get_changed_refs, lambda have, want: [])
126
return updates, conflicts
128
def _merge_to_local_git(self, target_repo, refs, overwrite=False):
131
for k, (peeled, unpeeled) in gather_peeled(refs).iteritems():
134
name = ref_to_tag_name(k)
135
if target_repo._git.refs.get(k) in (peeled, unpeeled):
137
elif overwrite or not k in target_repo._git.refs:
138
target_repo._git.refs[k] = unpeeled or peeled
139
updates[name] = target_repo.lookup_foreign_revision_id(peeled)
141
conflicts.append((name, peeled, target_repo.refs[k]))
142
return updates, conflicts
144
def _merge_to_git(self, to_tags, refs, overwrite=False):
145
target_repo = to_tags.repository
146
if self.repository.has_same_location(target_repo):
148
if getattr(target_repo, "_git", None):
149
return self._merge_to_local_git(target_repo, refs, overwrite)
151
return self._merge_to_remote_git(target_repo, refs, overwrite)
153
def _merge_to_non_git(self, to_tags, refs, overwrite=False):
154
unpeeled_map = defaultdict(set)
157
result = dict(to_tags.get_tag_dict())
158
for n, peeled, unpeeled, bzr_revid in self._iter_tag_refs(refs):
159
if unpeeled is not None:
160
unpeeled_map[peeled].add(unpeeled)
161
if result.get(n) == bzr_revid:
163
elif n not in result or overwrite:
164
result[n] = bzr_revid
165
updates[n] = bzr_revid
167
conflicts.append((n, result[n], bzr_revid))
168
to_tags._set_tag_dict(result)
169
if len(unpeeled_map) > 0:
170
map_file = UnpeelMap.from_repository(to_tags.branch.repository)
171
map_file.update(unpeeled_map)
172
map_file.save_in_repository(to_tags.branch.repository)
173
return updates, conflicts
175
def merge_to(self, to_tags, overwrite=False, ignore_master=False,
177
"""See Tags.merge_to."""
178
if source_refs is None:
179
source_refs = self.get_refs()
182
if isinstance(to_tags, GitTags):
183
return self._merge_to_git(to_tags, source_refs,
189
master = to_tags.branch.get_master_branch()
190
updates, conflicts = self._merge_to_non_git(to_tags, source_refs,
192
if master is not None:
193
extra_updates, extra_conflicts = self.merge_to(
194
master.tags, overwrite=overwrite,
195
source_refs=source_refs,
196
ignore_master=ignore_master)
197
updates.update(extra_updates)
198
conflicts += extra_conflicts
199
return updates, conflicts
86
201
def get_tag_dict(self):
88
for k,v in extract_tags(self.repository._git.get_refs()).iteritems():
203
refs = self.get_refs()
204
for (name, peeled, unpeeled, bzr_revid) in self._iter_tag_refs(refs):
205
ret[name] = bzr_revid
209
class LocalGitTagDict(GitTags):
210
"""Dictionary with tags in a local repository."""
212
def __init__(self, branch):
213
super(LocalGitTagDict, self).__init__(branch)
214
self.refs = self.repository.bzrdir._git.refs
217
return self.refs.as_dict()
219
def _iter_tag_refs(self, refs):
220
"""Iterate over the tag refs.
222
:param refs: Refs dictionary (name -> git sha1)
223
:return: iterator over (name, peeled_sha1, unpeeled_sha1, bzr_revid)
225
for k, (peeled, unpeeled) in extract_tags(refs).iteritems():
90
obj = self.repository._git[v]
227
obj = self.repository._git[peeled]
92
mutter("Tag %s points at unknown object %s, ignoring", v, obj)
229
mutter("Tag %s points at unknown object %s, ignoring", peeled,
232
# FIXME: this shouldn't really be necessary, the repository
233
# already should have these unpeeled.
94
234
while isinstance(obj, Tag):
96
obj = self.repository._git[v]
235
peeled = obj.object[1]
236
obj = self.repository._git[peeled]
97
237
if not isinstance(obj, Commit):
98
238
mutter("Tag %s points at object %r that is not a commit, "
99
239
"ignoring", k, obj)
101
ret[k] = self.branch.lookup_foreign_revision_id(v)
241
yield (k, peeled, unpeeled,
242
self.branch.lookup_foreign_revision_id(peeled))
104
244
def _set_tag_dict(self, to_dict):
105
extra = set(self.repository._git.get_refs().keys())
245
extra = set(self.get_refs().keys())
106
246
for k, revid in to_dict.iteritems():
107
247
name = tag_name_to_ref(k)
108
248
if name in extra:
109
249
extra.remove(name)
110
250
self.set_tag(k, revid)
111
251
for name in extra:
112
if name.startswith("refs/tags/"):
113
253
del self.repository._git[name]
115
255
def set_tag(self, name, revid):
116
self.repository._git.refs[tag_name_to_ref(name)], _ = \
117
self.branch.lookup_bzr_revision_id(revid)
120
class DictTagDict(LocalGitTagDict):
257
git_sha, mapping = self.branch.lookup_bzr_revision_id(revid)
258
except errors.NoSuchRevision:
259
raise errors.GhostTagsNotSupported(self)
260
self.refs[tag_name_to_ref(name)] = git_sha
263
class DictTagDict(tag.BasicTags):
122
265
def __init__(self, branch, tags):
123
266
super(DictTagDict, self).__init__(branch)
427
688
def _get_branch_formats_to_test():
690
default_format = branch.format_registry.get_default()
691
except AttributeError:
692
default_format = branch.BranchFormat._default_format
694
(GitBranchFormat(), GitBranchFormat()),
695
(GitBranchFormat(), default_format)]
431
698
def _get_interrepo(self, source, target):
432
return repository.InterRepository.get(source.repository,
699
return _mod_repository.InterRepository.get(source.repository, target.repository)
436
702
def is_compatible(cls, source, target):
437
return (isinstance(source, GitBranch) and
438
not isinstance(target, GitBranch) and
439
(getattr(cls._get_interrepo(source, target), "fetch_objects", None) is not None))
441
def _update_revisions(self, stop_revision=None, overwrite=False,
442
graph=None, limit=None):
443
"""Like InterBranch.update_revisions(), but with additions.
445
Compared to the `update_revisions()` below, this function takes a
446
`limit` argument that limits how many git commits will be converted
447
and returns the new git head.
703
if not isinstance(source, GitBranch):
705
if isinstance(target, GitBranch):
706
# InterLocalGitRemoteGitBranch or InterToGitBranch should be used
708
if getattr(cls._get_interrepo(source, target), "fetch_objects", None) is None:
709
# fetch_objects is necessary for this to work
713
def fetch(self, stop_revision=None, fetch_tags=None, limit=None):
714
self.fetch_objects(stop_revision, fetch_tags=fetch_tags, limit=limit)
716
def fetch_objects(self, stop_revision, fetch_tags, limit=None):
449
717
interrepo = self._get_interrepo(self.source, self.target)
718
if fetch_tags is None:
719
c = self.source.get_config()
720
fetch_tags = c.get_user_option_as_bool('branch.fetch_tags')
450
721
def determine_wants(heads):
451
722
if self.source.ref is not None and not self.source.ref in heads:
452
raise NoSuchRef(self.source.ref, heads.keys())
453
if stop_revision is not None:
454
self._last_revid = stop_revision
455
head, mapping = self.source.repository.lookup_bzr_revision_id(
723
raise NoSuchRef(self.source.ref, self.source.user_url, heads.keys())
725
if stop_revision is None:
458
726
if self.source.ref is not None:
459
727
head = heads[self.source.ref]
461
729
head = heads["HEAD"]
462
730
self._last_revid = self.source.lookup_foreign_revision_id(head)
463
if self.target.repository.has_revision(self._last_revid):
732
self._last_revid = stop_revision
733
real = interrepo.get_determine_wants_revids(
734
[self._last_revid], include_tags=fetch_tags)
466
736
pack_hint, head, refs = interrepo.fetch_objects(
467
737
determine_wants, self.source.mapping, limit=limit)
468
738
if (pack_hint is not None and
469
739
self.target.repository._format.pack_compresses):
470
740
self.target.repository.pack(hint=pack_hint)
472
self._last_revid = self.source.lookup_foreign_revision_id(head)
743
def _update_revisions(self, stop_revision=None, overwrite=False):
744
head, refs = self.fetch_objects(stop_revision, fetch_tags=None)
474
746
prev_last_revid = None
476
748
prev_last_revid = self.target.last_revision()
477
749
self.target.generate_revision_history(self._last_revid,
750
prev_last_revid, self.source)
481
def update_revisions(self, stop_revision=None, overwrite=False,
483
"""See InterBranch.update_revisions()."""
484
self._update_revisions(stop_revision, overwrite, graph)
753
def _basic_pull(self, stop_revision, overwrite, run_hooks,
754
_override_hook_target, _hook_master):
755
result = GitBranchPullResult()
756
result.source_branch = self.source
757
if _override_hook_target is None:
758
result.target_branch = self.target
760
result.target_branch = _override_hook_target
761
self.source.lock_read()
763
self.target.lock_write()
765
# We assume that during 'pull' the target repository is closer than
767
(result.old_revno, result.old_revid) = \
768
self.target.last_revision_info()
769
result.new_git_head, remote_refs = self._update_revisions(
770
stop_revision, overwrite=overwrite)
771
tags_ret = self.source.tags.merge_to(
772
self.target.tags, overwrite)
773
if isinstance(tags_ret, tuple):
774
result.tag_updates, result.tag_conflicts = tags_ret
776
result.tag_conflicts = tags_ret
777
(result.new_revno, result.new_revid) = \
778
self.target.last_revision_info()
780
result.master_branch = _hook_master
781
result.local_branch = result.target_branch
783
result.master_branch = result.target_branch
784
result.local_branch = None
786
for hook in branch.Branch.hooks['post_pull']:
486
794
def pull(self, overwrite=False, stop_revision=None,
487
795
possible_transports=None, _hook_master=None, run_hooks=True,
488
_override_hook_target=None, local=False, limit=None):
796
_override_hook_target=None, local=False):
489
797
"""See Branch.pull.
491
799
:param _hook_master: Private parameter - set the branch to
495
803
so it should not run its hooks.
496
804
:param _override_hook_target: Private parameter - set the branch to be
497
805
supplied as the target_branch to pull hooks.
498
:param limit: Only import this many revisons. `None`, the default,
499
means import all revisions.
501
807
# This type of branch can't be bound.
808
bound_location = self.target.get_bound_location()
809
if local and not bound_location:
503
810
raise errors.LocalRequiresBoundBranch()
504
result = GitBranchPullResult()
505
result.source_branch = self.source
506
if _override_hook_target is None:
507
result.target_branch = self.target
509
result.target_branch = _override_hook_target
812
source_is_master = False
510
813
self.source.lock_read()
815
# bound_location comes from a config file, some care has to be
816
# taken to relate it to source.user_url
817
normalized = urlutils.normalize_url(bound_location)
819
relpath = self.source.user_transport.relpath(normalized)
820
source_is_master = (relpath == '')
821
except (errors.PathNotChild, errors.InvalidURL):
822
source_is_master = False
823
if not local and bound_location and not source_is_master:
824
# not pulling from master, so we need to update master.
825
master_branch = self.target.get_master_branch(possible_transports)
826
master_branch.lock_write()
512
# We assume that during 'pull' the target repository is closer than
514
graph = self.target.repository.get_graph(self.source.repository)
515
(result.old_revno, result.old_revid) = \
516
self.target.last_revision_info()
517
result.new_git_head = self._update_revisions(
518
stop_revision, overwrite=overwrite, graph=graph, limit=limit)
519
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
521
(result.new_revno, result.new_revid) = \
522
self.target.last_revision_info()
524
result.master_branch = _hook_master
525
result.local_branch = result.target_branch
527
result.master_branch = result.target_branch
528
result.local_branch = None
530
for hook in branch.Branch.hooks['post_pull']:
830
# pull from source into master.
831
master_branch.pull(self.source, overwrite, stop_revision,
833
result = self._basic_pull(stop_revision, overwrite, run_hooks,
834
_override_hook_target, _hook_master=master_branch)
839
master_branch.unlock()
536
842
def _basic_push(self, overwrite=False, stop_revision=None):
537
843
result = branch.BranchPushResult()
538
844
result.source_branch = self.source
539
845
result.target_branch = self.target
540
graph = self.target.repository.get_graph(self.source.repository)
541
846
result.old_revno, result.old_revid = self.target.last_revision_info()
542
result.new_git_head = self._update_revisions(
543
stop_revision, overwrite=overwrite, graph=graph)
544
result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
847
result.new_git_head, remote_refs = self._update_revisions(
848
stop_revision, overwrite=overwrite)
849
tags_ret = self.source.tags.merge_to(self.target.tags,
851
if isinstance(tags_ret, tuple):
852
(result.tag_updates, result.tag_conflicts) = tags_ret
854
result.tag_conflicts = tags_ret
546
855
result.new_revno, result.new_revid = self.target.last_revision_info()
587
class InterGitRemoteLocalBranch(InterGitBranch):
897
class InterGitLocalGitBranch(InterGitBranch):
588
898
"""InterBranch that copies from a remote to a local git branch."""
591
901
def _get_branch_formats_to_test():
595
906
def is_compatible(self, source, target):
596
from bzrlib.plugins.git.remote import RemoteGitBranch
597
return (isinstance(source, RemoteGitBranch) and
907
return (isinstance(source, GitBranch) and
598
908
isinstance(target, LocalGitBranch))
600
910
def _basic_push(self, overwrite=False, stop_revision=None):
601
result = branch.BranchPushResult()
911
result = GitBranchPushResult()
602
912
result.source_branch = self.source
603
913
result.target_branch = self.target
604
914
result.old_revid = self.target.last_revision()
605
915
refs, stop_revision = self.update_refs(stop_revision)
606
916
self.target.generate_revision_history(stop_revision, result.old_revid)
607
self.update_tags(refs)
917
tags_ret = self.source.tags.merge_to(self.target.tags,
918
source_refs=refs, overwrite=overwrite)
919
if isinstance(tags_ret, tuple):
920
(result.tag_updates, result.tag_conflicts) = tags_ret
922
result.tag_conflicts = tags_ret
608
923
result.new_revid = self.target.last_revision()
611
def update_tags(self, refs):
612
for name, v in extract_tags(refs).iteritems():
613
revid = self.target.lookup_foreign_revision_id(v)
614
self.target.tags.set_tag(name, revid)
616
926
def update_refs(self, stop_revision=None):
617
interrepo = repository.InterRepository.get(self.source.repository,
927
interrepo = _mod_repository.InterRepository.get(self.source.repository,
618
928
self.target.repository)
619
929
if stop_revision is None:
620
930
refs = interrepo.fetch(branches=["HEAD"])
631
941
result = GitPullResult()
632
942
result.source_branch = self.source
633
943
result.target_branch = self.target
634
result.old_revid = self.target.last_revision()
635
refs, stop_revision = self.update_refs(stop_revision)
636
self.target.generate_revision_history(stop_revision, result.old_revid)
637
self.update_tags(refs)
638
result.new_revid = self.target.last_revision()
944
self.source.lock_read()
946
self.target.lock_write()
948
result.old_revid = self.target.last_revision()
949
refs, stop_revision = self.update_refs(stop_revision)
950
self.target.generate_revision_history(stop_revision, result.old_revid)
951
tags_ret = self.source.tags.merge_to(self.target.tags,
952
overwrite=overwrite, source_refs=refs)
953
if isinstance(tags_ret, tuple):
954
(result.tag_updates, result.tag_conflicts) = tags_ret
956
result.tag_conflicts = tags_ret
957
result.new_revid = self.target.last_revision()
958
result.local_branch = None
959
result.master_branch = result.target_branch
961
for hook in branch.Branch.hooks['post_pull']:
642
970
class InterToGitBranch(branch.GenericInterBranch):
643
"""InterBranch implementation that pulls from Git into bzr."""
971
"""InterBranch implementation that pulls into a Git branch."""
645
973
def __init__(self, source, target):
646
974
super(InterToGitBranch, self).__init__(source, target)
647
self.interrepo = repository.InterRepository.get(source.repository,
975
self.interrepo = _mod_repository.InterRepository.get(source.repository,
648
976
target.repository)
651
979
def _get_branch_formats_to_test():
981
default_format = branch.format_registry.get_default()
982
except AttributeError:
983
default_format = branch.BranchFormat._default_format
984
return [(default_format, GitBranchFormat())]
655
987
def is_compatible(self, source, target):
656
988
return (not isinstance(source, GitBranch) and
657
989
isinstance(target, GitBranch))
659
def update_revisions(self, *args, **kwargs):
660
raise NoPushSupport()
662
def _get_new_refs(self, stop_revision=None):
991
def _get_new_refs(self, stop_revision=None, fetch_tags=None):
992
assert self.source.is_locked()
663
993
if stop_revision is None:
664
stop_revision = self.source.last_revision()
994
(stop_revno, stop_revision) = self.source.last_revision_info()
996
stop_revno = self.source.revision_id_to_revno(stop_revision)
665
997
assert type(stop_revision) is str
666
998
main_ref = self.target.ref or "refs/heads/master"
667
999
refs = { main_ref: (None, stop_revision) }
1000
if fetch_tags is None:
1001
c = self.source.get_config()
1002
fetch_tags = c.get_user_option_as_bool('branch.fetch_tags')
668
1003
for name, revid in self.source.tags.get_tag_dict().iteritems():
669
1004
if self.source.repository.has_revision(revid):
670
refs[tag_name_to_ref(name)] = (None, revid)
671
return refs, main_ref
1005
ref = tag_name_to_ref(name)
1006
if not check_ref_format(ref):
1007
warning("skipping tag with invalid characters %s (%s)",
1011
# FIXME: Skip tags that are not in the ancestry
1012
refs[ref] = (None, revid)
1013
return refs, main_ref, (stop_revno, stop_revision)
1015
def _update_refs(self, result, old_refs, new_refs, overwrite):
1016
mutter("updating refs. old refs: %r, new refs: %r",
1018
result.tag_updates = {}
1019
result.tag_conflicts = []
1020
ret = dict(old_refs)
1021
def ref_equals(refs, ref, git_sha, revid):
1026
if (value[0] is not None and
1027
git_sha is not None and
1028
value[0] != git_sha):
1030
if (value[1] is not None and
1031
revid is not None and
1034
# FIXME: If one side only has the git sha available and the other only
1035
# has the bzr revid, then this will cause us to show a tag as updated
1036
# that hasn't actually been updated.
1038
for ref, (git_sha, revid) in new_refs.iteritems():
1039
if ref not in ret or overwrite:
1040
if not ref_equals(ret, ref, git_sha, revid):
1042
tag_name = ref_to_tag_name(ref)
1046
result.tag_updates[tag_name] = revid
1047
ret[ref] = (git_sha, revid)
1048
elif ref_equals(ret, ref, git_sha, revid):
1052
name = ref_to_tag_name(ref)
1056
result.tag_conflicts.append((name, revid, ret[name][1]))
1057
# FIXME: Check for diverged branches
1058
ret.update(new_refs)
673
1061
def pull(self, overwrite=False, stop_revision=None, local=False,
674
possible_transports=None):
675
from dulwich.protocol import ZERO_SHA
1062
possible_transports=None, run_hooks=True):
676
1063
result = GitBranchPullResult()
677
1064
result.source_branch = self.source
678
1065
result.target_branch = self.target
679
new_refs, main_ref = self._get_new_refs(stop_revision)
680
def update_refs(old_refs):
681
refs = dict(old_refs)
682
# FIXME: Check for diverged branches
683
refs.update(new_refs)
685
old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
686
result.old_revid = self.target.lookup_foreign_revision_id(
687
old_refs.get(main_ref, ZERO_SHA))
688
result.new_revid = new_refs[main_ref]
1066
self.source.lock_read()
1068
self.target.lock_write()
1070
new_refs, main_ref, stop_revinfo = self._get_new_refs(
1072
def update_refs(old_refs):
1073
return self._update_refs(result, old_refs, new_refs, overwrite)
1075
result.revidmap, old_refs, new_refs = self.interrepo.fetch_refs(
1076
update_refs, lossy=False)
1077
except NoPushSupport:
1078
raise errors.NoRoundtrippingSupport(self.source, self.target)
1079
(result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
1080
if result.old_revid is None:
1081
result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
1082
result.new_revid = new_refs[main_ref][1]
1083
result.local_branch = None
1084
result.master_branch = self.target
1086
for hook in branch.Branch.hooks['post_pull']:
1089
self.target.unlock()
1091
self.source.unlock()
691
def push(self, overwrite=False, stop_revision=None,
1094
def push(self, overwrite=False, stop_revision=None, lossy=False,
692
1095
_override_hook_source_branch=None):
693
from dulwich.protocol import ZERO_SHA
694
1096
result = GitBranchPushResult()
695
1097
result.source_branch = self.source
696
1098
result.target_branch = self.target
697
new_refs, main_ref = self._get_new_refs(stop_revision)
698
def update_refs(old_refs):
699
refs = dict(old_refs)
700
# FIXME: Check for diverged branches
701
refs.update(new_refs)
703
old_refs, new_refs = self.interrepo.fetch_refs(update_refs)
704
(result.old_revid, old_sha1) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
705
if result.old_revid is None:
706
result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
707
result.new_revid = new_refs[main_ref]
1099
result.local_branch = None
1100
result.master_branch = result.target_branch
1101
self.source.lock_read()
1103
new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
1104
def update_refs(old_refs):
1105
return self._update_refs(result, old_refs, new_refs, overwrite)
1107
result.revidmap, old_refs, new_refs = self.interrepo.fetch_refs(
1108
update_refs, lossy=lossy)
1109
except NoPushSupport:
1110
raise errors.NoRoundtrippingSupport(self.source, self.target)
1111
(old_sha1, result.old_revid) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
1112
if result.old_revid is None:
1113
result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
1114
result.new_revid = new_refs[main_ref][1]
1115
(result.new_original_revno, result.new_original_revid) = stop_revinfo
1116
for hook in branch.Branch.hooks['post_push']:
1119
self.source.unlock()
710
1122
def lossy_push(self, stop_revision=None):
711
result = GitBranchPushResult()
712
result.source_branch = self.source
713
result.target_branch = self.target
714
new_refs, main_ref = self._get_new_refs(stop_revision)
715
def update_refs(old_refs):
716
refs = dict(old_refs)
717
# FIXME: Check for diverged branches
718
refs.update(new_refs)
720
result.revidmap, old_refs, new_refs = self.interrepo.dfetch_refs(
722
result.old_revid = old_refs.get(self.target.ref, (None, NULL_REVISION))[1]
723
result.new_revid = new_refs[main_ref][1]
727
branch.InterBranch.register_optimiser(InterGitRemoteLocalBranch)
1123
# For compatibility with bzr < 2.4
1124
return self.push(lossy=True, stop_revision=stop_revision)
1127
branch.InterBranch.register_optimiser(InterGitLocalGitBranch)
728
1128
branch.InterBranch.register_optimiser(InterFromGitBranch)
729
1129
branch.InterBranch.register_optimiser(InterToGitBranch)
730
branch.InterBranch.register_optimiser(InterGitLocalRemoteBranch)
1130
branch.InterBranch.register_optimiser(InterLocalGitRemoteGitBranch)