119
97
return self._lookup_revno(self.new_revid)
122
class InterTagsFromGitToRemoteGit(InterTags):
125
def is_compatible(klass, source, target):
126
if not isinstance(source, GitTags):
128
if not isinstance(target, GitTags):
130
if getattr(target.branch.repository, "_git", None) is not None:
134
def merge(self, overwrite=False, ignore_master=False, selector=None):
135
if self.source.branch.repository.has_same_location(self.target.branch.repository):
100
class GitTags(tag.BasicTags):
101
"""Ref-based tag dictionary."""
103
def __init__(self, branch):
105
self.repository = branch.repository
107
def _merge_to_remote_git(self, target_repo, source_tag_refs, overwrite=False):
139
source_tag_refs = self.source.branch.get_tag_refs()
141
110
def get_changed_refs(old_refs):
142
111
ret = dict(old_refs)
143
for ref_name, tag_name, peeled, unpeeled in (
144
source_tag_refs.iteritems()):
145
if selector and not selector(tag_name):
112
for ref_name, tag_name, peeled, unpeeled in source_tag_refs.iteritems():
147
113
if old_refs.get(ref_name) == unpeeled:
149
elif overwrite or ref_name not in old_refs:
115
elif overwrite or not ref_name in old_refs:
150
116
ret[ref_name] = unpeeled
151
updates[tag_name] = self.target.branch.repository.lookup_foreign_revision_id(
153
self.target.branch._tag_refs = None
117
updates[tag_name] = target_repo.lookup_foreign_revision_id(peeled)
155
119
conflicts.append(
157
self.repository.lookup_foreign_revision_id(peeled),
158
self.target.branch.repository.lookup_foreign_revision_id(
159
old_refs[ref_name])))
121
self.repository.lookup_foreign_revision_id(peeled),
122
target_repo.lookup_foreign_revision_id(old_refs[ref_name])))
161
self.target.branch.repository.controldir.send_pack(
162
get_changed_refs, lambda have, want: [])
163
return updates, set(conflicts)
166
class InterTagsFromGitToLocalGit(InterTags):
169
def is_compatible(klass, source, target):
170
if not isinstance(source, GitTags):
172
if not isinstance(target, GitTags):
174
if getattr(target.branch.repository, "_git", None) is None:
178
def merge(self, overwrite=False, ignore_master=False, selector=None):
179
if self.source.branch.repository.has_same_location(self.target.branch.repository):
124
target_repo.controldir.send_pack(get_changed_refs, lambda have, want: [])
125
return updates, conflicts
127
def _merge_to_local_git(self, target_repo, source_tag_refs, overwrite=False):
184
source_tag_refs = self.source.branch.get_tag_refs()
186
target_repo = self.target.branch.repository
188
130
for ref_name, tag_name, peeled, unpeeled in source_tag_refs:
189
if selector and not selector(tag_name):
191
131
if target_repo._git.refs.get(ref_name) == unpeeled:
193
elif overwrite or ref_name not in target_repo._git.refs:
195
updates[tag_name] = (
196
target_repo.lookup_foreign_revision_id(peeled))
198
trace.warning('%s does not point to a valid object',
201
except NotCommitError:
202
trace.warning('%s points to a non-commit object',
133
elif overwrite or not ref_name in target_repo._git.refs:
205
134
target_repo._git.refs[ref_name] = unpeeled or peeled
206
self.target.branch._tag_refs = None
135
updates[tag_name] = self.repository.lookup_foreign_revision_id(peeled)
137
source_revid = self.repository.lookup_foreign_revision_id(peeled)
209
source_revid = self.source.branch.repository.lookup_foreign_revision_id(
211
139
target_revid = target_repo.lookup_foreign_revision_id(
212
target_repo._git.refs[ref_name])
140
target_repo._git.refs[ref_name])
214
142
trace.warning('%s does not point to a valid object',
217
except NotCommitError:
218
trace.warning('%s points to a non-commit object',
221
145
conflicts.append((tag_name, source_revid, target_revid))
222
return updates, set(conflicts)
225
class InterTagsFromGitToNonGit(InterTags):
228
def is_compatible(klass, source, target):
229
if not isinstance(source, GitTags):
231
if isinstance(target, GitTags):
235
def merge(self, overwrite=False, ignore_master=False, selector=None):
236
"""See Tags.merge_to."""
237
source_tag_refs = self.source.branch.get_tag_refs()
241
master = self.target.branch.get_master_branch()
242
with cleanup.ExitStack() as es:
243
if master is not None:
244
es.enter_context(master.lock_write())
245
updates, conflicts = self._merge_to(
246
self.target, source_tag_refs, overwrite=overwrite,
248
if master is not None:
249
extra_updates, extra_conflicts = self._merge_to(
250
master.tags, overwrite=overwrite,
251
source_tag_refs=source_tag_refs,
252
ignore_master=ignore_master, selector=selector)
253
updates.update(extra_updates)
254
conflicts.update(extra_conflicts)
255
return updates, conflicts
257
def _merge_to(self, to_tags, source_tag_refs, overwrite=False,
146
return updates, conflicts
148
def _merge_to_git(self, to_tags, source_tag_refs, overwrite=False):
149
target_repo = to_tags.repository
150
if self.repository.has_same_location(target_repo):
153
if getattr(target_repo, "_git", None):
154
return self._merge_to_local_git(target_repo, source_tag_refs, overwrite)
156
return self._merge_to_remote_git(target_repo, source_tag_refs, overwrite)
158
to_tags.branch._tag_refs = None
160
def _merge_to_non_git(self, to_tags, source_tag_refs, overwrite=False):
259
161
unpeeled_map = defaultdict(set)
262
164
result = dict(to_tags.get_tag_dict())
263
165
for ref_name, tag_name, peeled, unpeeled in source_tag_refs:
264
if selector and not selector(tag_name):
266
166
if unpeeled is not None:
267
167
unpeeled_map[peeled].add(unpeeled)
269
bzr_revid = self.source.branch.lookup_foreign_revision_id(peeled)
169
bzr_revid = self.branch.lookup_foreign_revision_id(peeled)
270
170
except NotCommitError:
272
172
if result.get(tag_name) == bzr_revid:
275
175
result[tag_name] = bzr_revid
276
176
updates[tag_name] = bzr_revid
278
conflicts.append((tag_name, bzr_revid, result[tag_name]))
178
conflicts.append((tag_name, bzr_revid, result[n]))
279
179
to_tags._set_tag_dict(result)
280
180
if len(unpeeled_map) > 0:
281
181
map_file = UnpeelMap.from_repository(to_tags.branch.repository)
282
182
map_file.update(unpeeled_map)
283
183
map_file.save_in_repository(to_tags.branch.repository)
284
return updates, set(conflicts)
287
InterTags.register_optimiser(InterTagsFromGitToRemoteGit)
288
InterTags.register_optimiser(InterTagsFromGitToLocalGit)
289
InterTags.register_optimiser(InterTagsFromGitToNonGit)
293
"""Ref-based tag dictionary."""
295
def __init__(self, branch):
297
self.repository = branch.repository
184
return updates, conflicts
186
def merge_to(self, to_tags, overwrite=False, ignore_master=False,
187
source_tag_refs=None):
188
"""See Tags.merge_to."""
189
if source_tag_refs is None:
190
source_tag_refs = self.branch.get_tag_refs()
193
if isinstance(to_tags, GitTags):
194
return self._merge_to_git(to_tags, source_tag_refs,
200
master = to_tags.branch.get_master_branch()
201
if master is not None:
204
updates, conflicts = self._merge_to_non_git(to_tags, source_tag_refs,
206
if master is not None:
207
extra_updates, extra_conflicts = self.merge_to(
208
master.tags, overwrite=overwrite,
209
source_tag_refs=source_tag_refs,
210
ignore_master=ignore_master)
211
updates.update(extra_updates)
212
conflicts += extra_conflicts
213
return updates, conflicts
215
if master is not None:
299
218
def get_tag_dict(self):
301
for (ref_name, tag_name, peeled, unpeeled) in (
302
self.branch.get_tag_refs()):
220
for (ref_name, tag_name, peeled, unpeeled) in self.branch.get_tag_refs():
304
222
bzr_revid = self.branch.lookup_foreign_revision_id(peeled)
305
223
except NotCommitError:
546
441
# Git doesn't do stacking (yet...)
547
442
raise branch.UnstackableBranchFormat(self._format, self.base)
549
def _get_push_origin(self, cs):
550
"""Get the name for the push origin.
552
The exact behaviour is documented in the git-config(1) manpage.
555
return cs.get((b'branch', self.name.encode('utf-8')), b'pushRemote')
558
return cs.get((b'branch', ), b'remote')
561
return cs.get((b'branch', self.name.encode('utf-8')), b'remote')
565
def _get_origin(self, cs):
567
return cs.get((b'branch', self.name.encode('utf-8')), b'remote')
571
def _get_related_push_branch(self, cs):
572
remote = self._get_push_origin(cs)
574
location = cs.get((b"remote", remote), b"url")
578
return git_url_to_bzr_url(location.decode('utf-8'), ref=self.ref)
580
def _get_related_merge_branch(self, cs):
581
remote = self._get_origin(cs)
583
location = cs.get((b"remote", remote), b"url")
588
ref = cs.get((b"branch", remote), b"merge")
592
return git_url_to_bzr_url(location.decode('utf-8'), ref=ref)
594
444
def _get_parent_location(self):
595
445
"""See Branch.get_parent()."""
446
# FIXME: Set "origin" url from .git/config ?
596
447
cs = self.repository._git.get_config_stack()
597
return self._get_related_merge_branch(cs)
599
def _write_git_config(self, cs):
602
self.repository._git._put_named_file('config', f.getvalue())
449
location = cs.get((b"remote", b'origin'), b"url")
455
ref = cs.get((b"remote", b"origin"), b"merge")
461
params['branch'] = ref_to_branch_name(ref).encode('utf-8')
463
params['ref'] = ref.encode('utf-8')
465
url = git_url_to_bzr_url(location)
466
return urlutils.join_segment_parameters(url, params)
604
468
def set_parent(self, location):
469
# FIXME: Set "origin" url in .git/config ?
605
470
cs = self.repository._git.get_config()
606
remote = self._get_origin(cs)
607
this_url = urlutils.strip_segment_parameters(self.user_url)
608
target_url, branch, ref = bzr_url_to_git_url(location)
471
this_url = urlutils.split_segment_parameters(self.user_url)[0]
472
target_url, target_params = urlutils.split_segment_parameters(location)
609
473
location = urlutils.relative_url(this_url, target_url)
610
cs.set((b"remote", remote), b"url", location)
612
cs.set((b"branch", remote), b"merge", branch_name_to_ref(branch))
614
cs.set((b"branch", remote), b"merge", ref)
474
cs.set((b"remote", b"origin"), b"url", location)
475
if 'branch' in target_params:
476
cs.set((b"remote", b"origin"), b"merge",
477
branch_name_to_ref(target_params['branch']))
478
elif 'ref' in target_params:
479
cs.set((b"remote", b"origin"), b"merge",
480
target_params['ref'])
616
482
# TODO(jelmer): Maybe unset rather than setting to HEAD?
617
cs.set((b"branch", remote), b"merge", b'HEAD')
618
self._write_git_config(cs)
483
cs.set((b"remote", b"origin"), b"merge", 'HEAD')
486
self.repository._git._put_named_file('config', f.getvalue())
620
488
def break_lock(self):
621
489
raise NotImplementedError(self.break_lock)
705
572
self._tag_refs = list(self._iter_tag_refs())
706
573
return self._tag_refs
708
def import_last_revision_info_and_tags(self, source, revno, revid,
710
"""Set the last revision info, importing from another repo if necessary.
712
This is used by the bound branch code to upload a revision to
713
the master branch first before updating the tip of the local branch.
714
Revisions referenced by source's tags are also transferred.
716
:param source: Source branch to optionally fetch from
717
:param revno: Revision number of the new tip
718
:param revid: Revision id of the new tip
719
:param lossy: Whether to discard metadata that can not be
721
:return: Tuple with the new revision number and revision id
722
(should only be different from the arguments when lossy=True)
724
push_result = source.push(
725
self, stop_revision=revid, lossy=lossy, _stop_revno=revno)
726
return (push_result.new_revno, push_result.new_revid)
728
def reconcile(self, thorough=True):
729
"""Make sure the data stored in this branch is consistent."""
730
from ..reconcile import ReconcileResult
732
return ReconcileResult()
735
576
class LocalGitBranch(GitBranch):
736
577
"""A local Git branch."""
738
579
def __init__(self, controldir, repository, ref):
739
580
super(LocalGitBranch, self).__init__(controldir, repository, ref,
740
LocalGitBranchFormat())
581
LocalGitBranchFormat())
742
583
def create_checkout(self, to_location, revision_id=None, lightweight=False,
743
accelerator_tree=None, hardlink=False):
584
accelerator_tree=None, hardlink=False):
744
585
t = transport.get_transport(to_location)
746
587
format = self._get_checkout_format(lightweight=lightweight)
864
703
def create_memorytree(self):
865
704
from .memorytree import GitMemoryTree
866
return GitMemoryTree(self, self.repository._git.object_store,
705
return GitMemoryTree(self, self.repository._git.object_store, self.head)
707
def reference_parent(self, path, file_id=None, possible_transports=None):
708
"""Return the parent branch for a tree-reference file_id
710
:param path: The path of the file_id in the tree
711
:param file_id: Optional file_id of the tree reference
712
:return: A branch associated with the file_id
714
# FIXME should provide multiple branches, based on config
715
url = urlutils.join(self.user_url, path)
716
return branch.Branch.open(
718
possible_transports=possible_transports)
870
722
def _quick_lookup_revno(local_branch, remote_branch, revid):
871
if not isinstance(revid, bytes):
723
if type(revid) is not str:
872
724
raise TypeError(revid)
873
725
# Try in source branch first, it'll be faster
874
726
with local_branch.lock_read():
875
if not _calculate_revnos(local_branch):
878
728
return local_branch.revision_id_to_revno(revid)
879
729
except errors.NoSuchRevision:
880
730
graph = local_branch.repository.get_graph()
882
return graph.find_distance_to_null(
883
revid, [(revision.NULL_REVISION, 0)])
732
return graph.find_distance_to_null(revid,
733
[(revision.NULL_REVISION, 0)])
884
734
except errors.GhostRevisionsHaveNoRevno:
885
if not _calculate_revnos(remote_branch):
887
735
# FIXME: Check using graph.find_distance_to_null() ?
888
736
with remote_branch.lock_read():
889
737
return remote_branch.revision_id_to_revno(revid)
979
826
if isinstance(target, GitBranch):
980
827
# InterLocalGitRemoteGitBranch or InterToGitBranch should be used
982
if (getattr(cls._get_interrepo(source, target), "fetch_objects", None)
829
if getattr(cls._get_interrepo(source, target), "fetch_objects", None) is None:
984
830
# fetch_objects is necessary for this to work
988
def fetch(self, stop_revision=None, fetch_tags=None, limit=None, lossy=False):
990
stop_revision, fetch_tags=fetch_tags, limit=limit, lossy=lossy)
991
return _mod_repository.FetchResult()
834
def fetch(self, stop_revision=None, fetch_tags=None, limit=None):
835
self.fetch_objects(stop_revision, fetch_tags=fetch_tags, limit=limit)
993
def fetch_objects(self, stop_revision, fetch_tags, limit=None, lossy=False, tag_selector=None):
837
def fetch_objects(self, stop_revision, fetch_tags, limit=None):
994
838
interrepo = self._get_interrepo(self.source, self.target)
995
839
if fetch_tags is None:
996
840
c = self.source.get_config_stack()
997
841
fetch_tags = c.get('branch.fetch_tags')
999
842
def determine_wants(heads):
1000
843
if stop_revision is None:
1003
846
except KeyError:
1004
847
self._last_revid = revision.NULL_REVISION
1006
self._last_revid = self.source.lookup_foreign_revision_id(
849
self._last_revid = self.source.lookup_foreign_revision_id(head)
1009
851
self._last_revid = stop_revision
1010
852
real = interrepo.get_determine_wants_revids(
1011
[self._last_revid], include_tags=fetch_tags, tag_selector=tag_selector)
853
[self._last_revid], include_tags=fetch_tags)
1012
854
return real(heads)
1013
855
pack_hint, head, refs = interrepo.fetch_objects(
1014
determine_wants, self.source.mapping, limit=limit,
856
determine_wants, self.source.mapping, limit=limit)
1016
857
if (pack_hint is not None and
1017
self.target.repository._format.pack_compresses):
858
self.target.repository._format.pack_compresses):
1018
859
self.target.repository.pack(hint=pack_hint)
1019
860
return head, refs
1021
def _update_revisions(self, stop_revision=None, overwrite=False, tag_selector=None):
1022
head, refs = self.fetch_objects(stop_revision, fetch_tags=None, tag_selector=tag_selector)
862
def _update_revisions(self, stop_revision=None, overwrite=False):
863
head, refs = self.fetch_objects(stop_revision, fetch_tags=None)
1024
865
prev_last_revid = None
1026
867
prev_last_revid = self.target.last_revision()
1027
self.target.generate_revision_history(
1028
self._last_revid, last_rev=prev_last_revid,
1029
other_branch=self.source)
868
self.target.generate_revision_history(self._last_revid,
869
last_rev=prev_last_revid, other_branch=self.source)
1030
870
return head, refs
1032
def update_references(self, revid=None):
1034
revid = self.target.last_revision()
1035
tree = self.target.repository.revision_tree(revid)
1037
with tree.get_file('.gitmodules') as f:
1038
for path, url, section in parse_submodules(
1039
GitConfigFile.from_file(f)):
1040
self.target.set_reference_info(
1041
tree.path2id(path.decode('utf-8')), url.decode('utf-8'),
1042
path.decode('utf-8'))
1043
except errors.NoSuchFile:
1046
872
def _basic_pull(self, stop_revision, overwrite, run_hooks,
1047
_override_hook_target, _hook_master, tag_selector=None):
873
_override_hook_target, _hook_master):
1048
874
if overwrite is True:
1049
875
overwrite = set(["history", "tags"])
1051
877
overwrite = set()
1052
878
result = GitBranchPullResult()
1053
879
result.source_branch = self.source
1100
924
bound_location = self.target.get_bound_location()
1101
925
if local and not bound_location:
1102
926
raise errors.LocalRequiresBoundBranch()
1103
928
source_is_master = False
1104
with cleanup.ExitStack() as es:
1105
es.enter_context(self.source.lock_read())
1107
# bound_location comes from a config file, some care has to be
1108
# taken to relate it to source.user_url
1109
normalized = urlutils.normalize_url(bound_location)
1111
relpath = self.source.user_transport.relpath(normalized)
1112
source_is_master = (relpath == '')
1113
except (errors.PathNotChild, urlutils.InvalidURL):
1114
source_is_master = False
1115
if not local and bound_location and not source_is_master:
1116
# not pulling from master, so we need to update master.
1117
master_branch = self.target.get_master_branch(possible_transports)
1118
es.enter_context(master_branch.lock_write())
1119
# pull from source into master.
1120
master_branch.pull(self.source, overwrite, stop_revision,
1123
master_branch = None
1124
return self._basic_pull(stop_revision, overwrite, run_hooks,
1125
_override_hook_target,
1126
_hook_master=master_branch,
1127
tag_selector=tag_selector)
929
self.source.lock_read()
931
# bound_location comes from a config file, some care has to be
932
# taken to relate it to source.user_url
933
normalized = urlutils.normalize_url(bound_location)
935
relpath = self.source.user_transport.relpath(normalized)
936
source_is_master = (relpath == '')
937
except (errors.PathNotChild, urlutils.InvalidURL):
938
source_is_master = False
939
if not local and bound_location and not source_is_master:
940
# not pulling from master, so we need to update master.
941
master_branch = self.target.get_master_branch(possible_transports)
942
master_branch.lock_write()
946
# pull from source into master.
947
master_branch.pull(self.source, overwrite, stop_revision,
949
result = self._basic_pull(stop_revision, overwrite, run_hooks,
950
_override_hook_target, _hook_master=master_branch)
955
master_branch.unlock()
1129
def _basic_push(self, overwrite, stop_revision, tag_selector=None):
958
def _basic_push(self, overwrite, stop_revision):
1130
959
if overwrite is True:
1131
960
overwrite = set(["history", "tags"])
1133
962
overwrite = set()
1134
963
result = branch.BranchPushResult()
1135
964
result.source_branch = self.source
1136
965
result.target_branch = self.target
1137
966
result.old_revno, result.old_revid = self.target.last_revision_info()
1138
967
result.new_git_head, remote_refs = self._update_revisions(
1139
stop_revision, overwrite=("history" in overwrite),
1140
tag_selector=tag_selector)
1141
tags_ret = self.source.tags.merge_to(
1142
self.target.tags, "tags" in overwrite, ignore_master=True,
1143
selector=tag_selector)
968
stop_revision, overwrite=("history" in overwrite))
969
tags_ret = self.source.tags.merge_to(self.target.tags,
970
"tags" in overwrite, ignore_master=True)
1144
971
(result.tag_updates, result.tag_conflicts) = tags_ret
1145
972
result.new_revno, result.new_revid = self.target.last_revision_info()
1146
self.update_references(revid=result.new_revid)
1150
976
class InterGitBranch(branch.GenericInterBranch):
1151
977
"""InterBranch implementation that pulls between Git branches."""
1153
def fetch(self, stop_revision=None, fetch_tags=None, limit=None, lossy=False):
979
def fetch(self, stop_revision=None, fetch_tags=None, limit=None):
1154
980
raise NotImplementedError(self.fetch)
1169
995
return (isinstance(source, LocalGitBranch) and
1170
996
isinstance(target, RemoteGitBranch))
1172
def _basic_push(self, overwrite, stop_revision, tag_selector=None):
998
def _basic_push(self, overwrite, stop_revision):
1173
999
result = GitBranchPushResult()
1174
1000
result.source_branch = self.source
1175
1001
result.target_branch = self.target
1176
1002
if stop_revision is None:
1177
1003
stop_revision = self.source.last_revision()
1179
1004
def get_changed_refs(old_refs):
1180
1005
old_ref = old_refs.get(self.target.ref, None)
1181
1006
if old_ref is None:
1182
1007
result.old_revid = revision.NULL_REVISION
1184
result.old_revid = self.target.lookup_foreign_revision_id(
1186
new_ref = self.source.repository.lookup_bzr_revision_id(
1009
result.old_revid = self.target.lookup_foreign_revision_id(old_ref)
1010
new_ref = self.source.repository.lookup_bzr_revision_id(stop_revision)[0]
1188
1011
if not overwrite:
1189
if remote_divergence(
1191
self.source.repository._git.object_store):
1012
if remote_divergence(old_ref, new_ref, self.source.repository._git.object_store):
1192
1013
raise errors.DivergedBranches(self.source, self.target)
1193
refs = {self.target.ref: new_ref}
1014
refs = { self.target.ref: new_ref }
1194
1015
result.new_revid = stop_revision
1195
for name, sha in viewitems(
1196
self.source.repository._git.refs.as_dict(b"refs/tags")):
1197
if tag_selector and not tag_selector(name):
1199
if sha not in self.source.repository._git:
1200
trace.mutter('Ignoring missing SHA: %s', sha)
1016
for name, sha in self.source.repository._git.refs.as_dict("refs/tags").iteritems():
1202
1017
refs[tag_name_to_ref(name)] = sha
1204
self.target.repository.send_pack(
1019
self.target.repository.send_pack(get_changed_refs,
1206
1020
self.source.repository._git.object_store.generate_pack_data)
1222
1036
return (isinstance(source, GitBranch) and
1223
1037
isinstance(target, LocalGitBranch))
1225
def fetch(self, stop_revision=None, fetch_tags=None, limit=None, lossy=False):
1226
interrepo = _mod_repository.InterRepository.get(
1227
self.source.repository, self.target.repository)
1039
def fetch(self, stop_revision=None, fetch_tags=None, limit=None):
1040
interrepo = _mod_repository.InterRepository.get(self.source.repository,
1041
self.target.repository)
1228
1042
if stop_revision is None:
1229
1043
stop_revision = self.source.last_revision()
1230
if fetch_tags is None:
1231
c = self.source.get_config_stack()
1232
fetch_tags = c.get('branch.fetch_tags')
1233
1044
determine_wants = interrepo.get_determine_wants_revids(
1234
1045
[stop_revision], include_tags=fetch_tags)
1235
interrepo.fetch_objects(determine_wants, limit=limit, lossy=lossy)
1236
return _mod_repository.FetchResult()
1046
interrepo.fetch_objects(determine_wants, limit=limit)
1238
def _basic_push(self, overwrite=False, stop_revision=None, tag_selector=None):
1048
def _basic_push(self, overwrite=False, stop_revision=None):
1239
1049
if overwrite is True:
1240
1050
overwrite = set(["history", "tags"])
1242
1052
overwrite = set()
1243
1053
result = GitBranchPushResult()
1244
1054
result.source_branch = self.source
1245
1055
result.target_branch = self.target
1246
1056
result.old_revid = self.target.last_revision()
1247
1057
refs, stop_revision = self.update_refs(stop_revision)
1248
self.target.generate_revision_history(
1250
(result.old_revid if ("history" not in overwrite) else None),
1251
other_branch=self.source)
1252
tags_ret = self.source.tags.merge_to(
1254
overwrite=("tags" in overwrite),
1255
selector=tag_selector)
1058
self.target.generate_revision_history(stop_revision,
1059
(result.old_revid if ("history" not in overwrite) else None),
1060
other_branch=self.source)
1061
tags_ret = self.source.tags.merge_to(self.target.tags,
1062
source_tag_refs=remote_refs_dict_to_tag_refs(refs),
1063
overwrite=("tags" in overwrite))
1256
1064
if isinstance(tags_ret, tuple):
1257
1065
(result.tag_updates, result.tag_conflicts) = tags_ret
1263
1071
def update_refs(self, stop_revision=None):
1264
1072
interrepo = _mod_repository.InterRepository.get(
1265
self.source.repository, self.target.repository)
1073
self.source.repository, self.target.repository)
1266
1074
c = self.source.get_config_stack()
1267
1075
fetch_tags = c.get('branch.fetch_tags')
1269
1077
if stop_revision is None:
1270
result = interrepo.fetch(branches=[self.source.ref], include_tags=fetch_tags)
1078
refs = interrepo.fetch(branches=["HEAD"], include_tags=fetch_tags)
1272
head = result.refs[self.source.ref]
1273
1081
except KeyError:
1274
1082
stop_revision = revision.NULL_REVISION
1276
1084
stop_revision = self.target.lookup_foreign_revision_id(head)
1278
result = interrepo.fetch(
1279
revision_id=stop_revision, include_tags=fetch_tags)
1280
return result.refs, stop_revision
1086
refs = interrepo.fetch(revision_id=stop_revision, include_tags=fetch_tags)
1087
return refs, stop_revision
1282
1089
def pull(self, stop_revision=None, overwrite=False,
1283
possible_transports=None, run_hooks=True, local=False,
1090
possible_transports=None, run_hooks=True, local=False):
1285
1091
# This type of branch can't be bound.
1287
1093
raise errors.LocalRequiresBoundBranch()
1288
1094
if overwrite is True:
1289
1095
overwrite = set(["history", "tags"])
1291
1097
overwrite = set()
1293
1099
result = GitPullResult()
1340
1145
return (not isinstance(source, GitBranch) and
1341
1146
isinstance(target, GitBranch))
1343
def _get_new_refs(self, stop_revision=None, fetch_tags=None,
1148
def _get_new_refs(self, stop_revision=None, fetch_tags=None):
1345
1149
if not self.source.is_locked():
1346
1150
raise errors.ObjectNotLocked(self.source)
1347
1151
if stop_revision is None:
1348
1152
(stop_revno, stop_revision) = self.source.last_revision_info()
1349
elif stop_revno is None:
1351
stop_revno = self.source.revision_id_to_revno(stop_revision)
1352
except errors.NoSuchRevision:
1354
if not isinstance(stop_revision, bytes):
1154
stop_revno = self.source.revision_id_to_revno(stop_revision)
1155
if type(stop_revision) is not str:
1355
1156
raise TypeError(stop_revision)
1356
1157
main_ref = self.target.ref
1357
refs = {main_ref: (None, stop_revision)}
1158
refs = { main_ref: (None, stop_revision) }
1358
1159
if fetch_tags is None:
1359
1160
c = self.source.get_config_stack()
1360
1161
fetch_tags = c.get('branch.fetch_tags')
1361
for name, revid in viewitems(self.source.tags.get_tag_dict()):
1162
for name, revid in self.source.tags.get_tag_dict().iteritems():
1362
1163
if self.source.repository.has_revision(revid):
1363
1164
ref = tag_name_to_ref(name)
1364
1165
if not check_ref_format(ref):
1365
1166
warning("skipping tag with invalid characters %s (%s)",
1369
1170
# FIXME: Skip tags that are not in the ancestry
1370
1171
refs[ref] = (None, revid)
1371
1172
return refs, main_ref, (stop_revno, stop_revision)
1373
def _update_refs(self, result, old_refs, new_refs, overwrite, tag_selector):
1174
def _update_refs(self, result, old_refs, new_refs, overwrite):
1374
1175
mutter("updating refs. old refs: %r, new refs: %r",
1375
1176
old_refs, new_refs)
1376
1177
result.tag_updates = {}
1377
1178
result.tag_conflicts = []
1378
1179
ret = dict(old_refs)
1380
1180
def ref_equals(refs, ref, git_sha, revid):
1382
1182
value = refs[ref]
1422
1220
except ValueError:
1425
result.tag_conflicts.append(
1426
(name, revid, ret[name][1]))
1223
result.tag_conflicts.append((name, revid, ret[name][1]))
1428
1225
ret[ref] = (git_sha, revid)
1431
def fetch(self, stop_revision=None, fetch_tags=None, lossy=False,
1228
def fetch(self, stop_revision=None, fetch_tags=None, lossy=False, limit=None):
1433
1229
if stop_revision is None:
1434
1230
stop_revision = self.source.last_revision()
1437
for k, v in viewitems(self.source.tags.get_tag_dict()):
1233
for k, v in self.source.tags.get_tag_dict().iteritems():
1438
1234
ret.append((None, v))
1439
1235
ret.append((None, stop_revision))
1441
revidmap = self.interrepo.fetch_objects(ret, lossy=lossy, limit=limit)
1237
self.interrepo.fetch_objects(ret, lossy=lossy, limit=limit)
1442
1238
except NoPushSupport:
1443
1239
raise errors.NoRoundtrippingSupport(self.source, self.target)
1444
return _mod_repository.FetchResult(revidmap={
1445
old_revid: new_revid
1446
for (old_revid, (new_sha, new_revid)) in revidmap.items()})
1448
1241
def pull(self, overwrite=False, stop_revision=None, local=False,
1449
possible_transports=None, run_hooks=True, _stop_revno=None,
1242
possible_transports=None, run_hooks=True):
1451
1243
result = GitBranchPullResult()
1452
1244
result.source_branch = self.source
1453
1245
result.target_branch = self.target
1454
1246
with self.source.lock_read(), self.target.lock_write():
1455
1247
new_refs, main_ref, stop_revinfo = self._get_new_refs(
1456
stop_revision, stop_revno=_stop_revno)
1458
1249
def update_refs(old_refs):
1459
return self._update_refs(result, old_refs, new_refs, overwrite, tag_selector)
1250
return self._update_refs(result, old_refs, new_refs, overwrite)
1461
result.revidmap, old_refs, new_refs = (
1462
self.interrepo.fetch_refs(update_refs, lossy=False))
1252
result.revidmap, old_refs, new_refs = self.interrepo.fetch_refs(
1253
update_refs, lossy=False)
1463
1254
except NoPushSupport:
1464
1255
raise errors.NoRoundtrippingSupport(self.source, self.target)
1465
(old_sha1, result.old_revid) = old_refs.get(
1466
main_ref, (ZERO_SHA, NULL_REVISION))
1256
(old_sha1, result.old_revid) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
1467
1257
if result.old_revid is None:
1468
result.old_revid = self.target.lookup_foreign_revision_id(
1258
result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
1470
1259
result.new_revid = new_refs[main_ref][1]
1471
1260
result.local_branch = None
1472
1261
result.master_branch = self.target
1478
1267
def push(self, overwrite=False, stop_revision=None, lossy=False,
1479
_override_hook_source_branch=None, _stop_revno=None,
1268
_override_hook_source_branch=None):
1481
1269
result = GitBranchPushResult()
1482
1270
result.source_branch = self.source
1483
1271
result.target_branch = self.target
1484
1272
result.local_branch = None
1485
1273
result.master_branch = result.target_branch
1486
1274
with self.source.lock_read(), self.target.lock_write():
1487
new_refs, main_ref, stop_revinfo = self._get_new_refs(
1488
stop_revision, stop_revno=_stop_revno)
1275
new_refs, main_ref, stop_revinfo = self._get_new_refs(stop_revision)
1490
1276
def update_refs(old_refs):
1491
return self._update_refs(result, old_refs, new_refs, overwrite, tag_selector)
1277
return self._update_refs(result, old_refs, new_refs, overwrite)
1493
result.revidmap, old_refs, new_refs = (
1494
self.interrepo.fetch_refs(
1495
update_refs, lossy=lossy, overwrite=overwrite))
1279
result.revidmap, old_refs, new_refs = self.interrepo.fetch_refs(
1280
update_refs, lossy=lossy, overwrite=overwrite)
1496
1281
except NoPushSupport:
1497
1282
raise errors.NoRoundtrippingSupport(self.source, self.target)
1498
(old_sha1, result.old_revid) = old_refs.get(
1499
main_ref, (ZERO_SHA, NULL_REVISION))
1500
if lossy or result.old_revid is None:
1501
result.old_revid = self.target.lookup_foreign_revision_id(
1283
(old_sha1, result.old_revid) = old_refs.get(main_ref, (ZERO_SHA, NULL_REVISION))
1284
if result.old_revid is None:
1285
result.old_revid = self.target.lookup_foreign_revision_id(old_sha1)
1503
1286
result.new_revid = new_refs[main_ref][1]
1504
(result.new_original_revno,
1505
result.new_original_revid) = stop_revinfo
1287
(result.new_original_revno, result.new_original_revid) = stop_revinfo
1506
1288
for hook in branch.Branch.hooks['post_push']: