94
def _calculate_revnos(branch):
95
if branch._format.stores_revno():
97
config = branch.get_config_stack()
98
return config.get('calculate_revnos')
101
85
class GitPullResult(branch.PullResult):
102
86
"""Result of a pull from a Git branch."""
104
88
def _lookup_revno(self, revid):
105
89
if not isinstance(revid, bytes):
106
90
raise TypeError(revid)
107
if not _calculate_revnos(self.target_branch):
109
91
# Try in source branch first, it'll be faster
110
92
with self.target_branch.lock_read():
111
93
return self.target_branch.revision_id_to_revno(revid)
119
101
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):
104
class GitTags(tag.BasicTags):
105
"""Ref-based tag dictionary."""
107
def __init__(self, branch):
109
self.repository = branch.repository
111
def _merge_to_remote_git(self, target_repo, source_tag_refs,
139
source_tag_refs = self.source.branch.get_tag_refs()
141
116
def get_changed_refs(old_refs):
142
117
ret = dict(old_refs)
143
118
for ref_name, tag_name, peeled, unpeeled in (
144
119
source_tag_refs.iteritems()):
145
if selector and not selector(tag_name):
147
120
if old_refs.get(ref_name) == unpeeled:
149
122
elif overwrite or ref_name not in old_refs:
150
123
ret[ref_name] = unpeeled
151
updates[tag_name] = self.target.branch.repository.lookup_foreign_revision_id(
124
updates[tag_name] = target_repo.lookup_foreign_revision_id(
153
self.target.branch._tag_refs = None
155
127
conflicts.append(
157
129
self.repository.lookup_foreign_revision_id(peeled),
158
self.target.branch.repository.lookup_foreign_revision_id(
130
target_repo.lookup_foreign_revision_id(
159
131
old_refs[ref_name])))
161
self.target.branch.repository.controldir.send_pack(
133
target_repo.controldir.send_pack(
162
134
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):
135
return updates, conflicts
137
def _merge_to_local_git(self, target_repo, source_tag_refs,
184
source_tag_refs = self.source.branch.get_tag_refs()
186
target_repo = self.target.branch.repository
188
141
for ref_name, tag_name, peeled, unpeeled in source_tag_refs:
189
if selector and not selector(tag_name):
191
142
if target_repo._git.refs.get(ref_name) == unpeeled:
193
144
elif overwrite or ref_name not in target_repo._git.refs:
198
149
trace.warning('%s does not point to a valid object',
201
except NotCommitError:
202
trace.warning('%s points to a non-commit object',
205
152
target_repo._git.refs[ref_name] = unpeeled or peeled
206
self.target.branch._tag_refs = None
209
source_revid = self.source.branch.repository.lookup_foreign_revision_id(
155
source_revid = self.repository.lookup_foreign_revision_id(
211
157
target_revid = target_repo.lookup_foreign_revision_id(
212
158
target_repo._git.refs[ref_name])
214
160
trace.warning('%s does not point to a valid object',
217
except NotCommitError:
218
trace.warning('%s points to a non-commit object',
221
163
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,
164
return updates, conflicts
166
def _merge_to_git(self, to_tags, source_tag_refs, overwrite=False):
167
target_repo = to_tags.repository
168
if self.repository.has_same_location(target_repo):
171
if getattr(target_repo, "_git", None):
172
return self._merge_to_local_git(
173
target_repo, source_tag_refs, overwrite)
175
return self._merge_to_remote_git(
176
target_repo, source_tag_refs, overwrite)
178
to_tags.branch._tag_refs = None
180
def _merge_to_non_git(self, to_tags, source_tag_refs, overwrite=False):
259
181
unpeeled_map = defaultdict(set)
262
184
result = dict(to_tags.get_tag_dict())
263
185
for ref_name, tag_name, peeled, unpeeled in source_tag_refs:
264
if selector and not selector(tag_name):
266
186
if unpeeled is not None:
267
187
unpeeled_map[peeled].add(unpeeled)
269
bzr_revid = self.source.branch.lookup_foreign_revision_id(peeled)
189
bzr_revid = self.branch.lookup_foreign_revision_id(peeled)
270
190
except NotCommitError:
272
192
if result.get(tag_name) == bzr_revid:
281
201
map_file = UnpeelMap.from_repository(to_tags.branch.repository)
282
202
map_file.update(unpeeled_map)
283
203
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
204
return updates, conflicts
206
def merge_to(self, to_tags, overwrite=False, ignore_master=False,
207
source_tag_refs=None):
208
"""See Tags.merge_to."""
209
if source_tag_refs is None:
210
source_tag_refs = self.branch.get_tag_refs()
213
if isinstance(to_tags, GitTags):
214
return self._merge_to_git(to_tags, source_tag_refs,
220
master = to_tags.branch.get_master_branch()
221
if master is not None:
224
updates, conflicts = self._merge_to_non_git(
225
to_tags, source_tag_refs, overwrite=overwrite)
226
if master is not None:
227
extra_updates, extra_conflicts = self.merge_to(
228
master.tags, overwrite=overwrite,
229
source_tag_refs=source_tag_refs,
230
ignore_master=ignore_master)
231
updates.update(extra_updates)
232
conflicts += extra_conflicts
233
return updates, conflicts
235
if master is not None:
299
238
def get_tag_dict(self):
604
525
def set_parent(self, location):
605
526
cs = self.repository._git.get_config()
606
527
remote = self._get_origin(cs)
607
this_url = urlutils.strip_segment_parameters(self.user_url)
528
this_url = urlutils.split_segment_parameters(self.user_url)[0]
608
529
target_url, branch, ref = bzr_url_to_git_url(location)
609
530
location = urlutils.relative_url(this_url, target_url)
610
531
cs.set((b"remote", remote), b"url", location)
667
588
return revision.NULL_REVISION
668
589
return self.lookup_foreign_revision_id(self.head)
670
def _basic_push(self, target, overwrite=False, stop_revision=None,
591
def _basic_push(self, target, overwrite=False, stop_revision=None):
672
592
return branch.InterBranch.get(self, target)._basic_push(
673
overwrite, stop_revision, tag_selector=tag_selector)
593
overwrite, stop_revision)
675
595
def lookup_foreign_revision_id(self, foreign_revid):
866
790
return GitMemoryTree(self, self.repository._git.object_store,
793
def reference_parent(self, path, file_id=None, possible_transports=None):
794
"""Return the parent branch for a tree-reference file_id
796
:param path: The path of the file_id in the tree
797
:param file_id: Optional file_id of the tree reference
798
:return: A branch associated with the file_id
800
# FIXME should provide multiple branches, based on config
801
url = urlutils.join(self.user_url, path)
802
return branch.Branch.open(
804
possible_transports=possible_transports)
870
807
def _quick_lookup_revno(local_branch, remote_branch, revid):
871
808
if not isinstance(revid, bytes):
872
809
raise TypeError(revid)
873
810
# Try in source branch first, it'll be faster
874
811
with local_branch.lock_read():
875
if not _calculate_revnos(local_branch):
878
813
return local_branch.revision_id_to_revno(revid)
879
814
except errors.NoSuchRevision:
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()
921
def fetch(self, stop_revision=None, fetch_tags=None, limit=None):
922
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):
924
def fetch_objects(self, stop_revision, fetch_tags, limit=None):
994
925
interrepo = self._get_interrepo(self.source, self.target)
995
926
if fetch_tags is None:
996
927
c = self.source.get_config_stack()
1009
940
self._last_revid = stop_revision
1010
941
real = interrepo.get_determine_wants_revids(
1011
[self._last_revid], include_tags=fetch_tags, tag_selector=tag_selector)
942
[self._last_revid], include_tags=fetch_tags)
1012
943
return real(heads)
1013
944
pack_hint, head, refs = interrepo.fetch_objects(
1014
determine_wants, self.source.mapping, limit=limit,
945
determine_wants, self.source.mapping, limit=limit)
1016
946
if (pack_hint is not None and
1017
947
self.target.repository._format.pack_compresses):
1018
948
self.target.repository.pack(hint=pack_hint)
1019
949
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)
951
def _update_revisions(self, stop_revision=None, overwrite=False):
952
head, refs = self.fetch_objects(stop_revision, fetch_tags=None)
1024
954
prev_last_revid = None
1029
959
other_branch=self.source)
1030
960
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
962
def _basic_pull(self, stop_revision, overwrite, run_hooks,
1047
_override_hook_target, _hook_master, tag_selector=None):
963
_override_hook_target, _hook_master):
1048
964
if overwrite is True:
1049
965
overwrite = set(["history", "tags"])
1050
966
elif not overwrite:
1061
977
(result.old_revno, result.old_revid) = \
1062
978
self.target.last_revision_info()
1063
979
result.new_git_head, remote_refs = self._update_revisions(
1064
stop_revision, overwrite=("history" in overwrite),
1065
tag_selector=tag_selector)
980
stop_revision, overwrite=("history" in overwrite))
1066
981
tags_ret = self.source.tags.merge_to(
1067
982
self.target.tags, ("tags" in overwrite), ignore_master=True)
1068
983
if isinstance(tags_ret, tuple):
1086
1000
def pull(self, overwrite=False, stop_revision=None,
1087
1001
possible_transports=None, _hook_master=None, run_hooks=True,
1088
_override_hook_target=None, local=False, tag_selector=None):
1002
_override_hook_target=None, local=False):
1089
1003
"""See Branch.pull.
1091
1005
:param _hook_master: Private parameter - set the branch to
1100
1014
bound_location = self.target.get_bound_location()
1101
1015
if local and not bound_location:
1102
1016
raise errors.LocalRequiresBoundBranch()
1017
master_branch = None
1103
1018
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)
1019
self.source.lock_read()
1021
# bound_location comes from a config file, some care has to be
1022
# taken to relate it to source.user_url
1023
normalized = urlutils.normalize_url(bound_location)
1025
relpath = self.source.user_transport.relpath(normalized)
1026
source_is_master = (relpath == '')
1027
except (errors.PathNotChild, urlutils.InvalidURL):
1028
source_is_master = False
1029
if not local and bound_location and not source_is_master:
1030
# not pulling from master, so we need to update master.
1031
master_branch = self.target.get_master_branch(possible_transports)
1032
master_branch.lock_write()
1036
# pull from source into master.
1037
master_branch.pull(self.source, overwrite, stop_revision,
1039
result = self._basic_pull(stop_revision, overwrite, run_hooks,
1040
_override_hook_target,
1041
_hook_master=master_branch)
1043
self.source.unlock()
1046
master_branch.unlock()
1129
def _basic_push(self, overwrite, stop_revision, tag_selector=None):
1049
def _basic_push(self, overwrite, stop_revision):
1130
1050
if overwrite is True:
1131
1051
overwrite = set(["history", "tags"])
1132
1052
elif not overwrite:
1136
1056
result.target_branch = self.target
1137
1057
result.old_revno, result.old_revid = self.target.last_revision_info()
1138
1058
result.new_git_head, remote_refs = self._update_revisions(
1139
stop_revision, overwrite=("history" in overwrite),
1140
tag_selector=tag_selector)
1059
stop_revision, overwrite=("history" in overwrite))
1141
1060
tags_ret = self.source.tags.merge_to(
1142
self.target.tags, "tags" in overwrite, ignore_master=True,
1143
selector=tag_selector)
1061
self.target.tags, "tags" in overwrite, ignore_master=True)
1144
1062
(result.tag_updates, result.tag_conflicts) = tags_ret
1145
1063
result.new_revno, result.new_revid = self.target.last_revision_info()
1146
self.update_references(revid=result.new_revid)
1150
1067
class InterGitBranch(branch.GenericInterBranch):
1151
1068
"""InterBranch implementation that pulls between Git branches."""
1153
def fetch(self, stop_revision=None, fetch_tags=None, limit=None, lossy=False):
1070
def fetch(self, stop_revision=None, fetch_tags=None, limit=None):
1154
1071
raise NotImplementedError(self.fetch)
1169
1086
return (isinstance(source, LocalGitBranch) and
1170
1087
isinstance(target, RemoteGitBranch))
1172
def _basic_push(self, overwrite, stop_revision, tag_selector=None):
1089
def _basic_push(self, overwrite, stop_revision):
1173
1090
result = GitBranchPushResult()
1174
1091
result.source_branch = self.source
1175
1092
result.target_branch = self.target
1222
1134
return (isinstance(source, GitBranch) and
1223
1135
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)
1137
def fetch(self, stop_revision=None, fetch_tags=None, limit=None):
1138
interrepo = _mod_repository.InterRepository.get(self.source.repository,
1139
self.target.repository)
1228
1140
if stop_revision is None:
1229
1141
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
1142
determine_wants = interrepo.get_determine_wants_revids(
1234
1143
[stop_revision], include_tags=fetch_tags)
1235
interrepo.fetch_objects(determine_wants, limit=limit, lossy=lossy)
1236
return _mod_repository.FetchResult()
1144
interrepo.fetch_objects(determine_wants, limit=limit)
1238
def _basic_push(self, overwrite=False, stop_revision=None, tag_selector=None):
1146
def _basic_push(self, overwrite=False, stop_revision=None):
1239
1147
if overwrite is True:
1240
1148
overwrite = set(["history", "tags"])
1241
1149
elif not overwrite:
1267
1175
fetch_tags = c.get('branch.fetch_tags')
1269
1177
if stop_revision is None:
1270
result = interrepo.fetch(branches=[self.source.ref], include_tags=fetch_tags)
1178
refs = interrepo.fetch(branches=[self.source.ref], include_tags=fetch_tags)
1272
head = result.refs[self.source.ref]
1180
head = refs[self.source.ref]
1273
1181
except KeyError:
1274
1182
stop_revision = revision.NULL_REVISION
1276
1184
stop_revision = self.target.lookup_foreign_revision_id(head)
1278
result = interrepo.fetch(
1186
refs = interrepo.fetch(
1279
1187
revision_id=stop_revision, include_tags=fetch_tags)
1280
return result.refs, stop_revision
1188
return refs, stop_revision
1282
1190
def pull(self, stop_revision=None, overwrite=False,
1283
possible_transports=None, run_hooks=True, local=False,
1191
possible_transports=None, run_hooks=True, local=False):
1285
1192
# This type of branch can't be bound.
1287
1194
raise errors.LocalRequiresBoundBranch()
1370
1274
refs[ref] = (None, revid)
1371
1275
return refs, main_ref, (stop_revno, stop_revision)
1373
def _update_refs(self, result, old_refs, new_refs, overwrite, tag_selector):
1277
def _update_refs(self, result, old_refs, new_refs, overwrite):
1374
1278
mutter("updating refs. old refs: %r, new refs: %r",
1375
1279
old_refs, new_refs)
1376
1280
result.tag_updates = {}
1438
1340
ret.append((None, v))
1439
1341
ret.append((None, stop_revision))
1441
revidmap = self.interrepo.fetch_objects(ret, lossy=lossy, limit=limit)
1343
self.interrepo.fetch_objects(ret, lossy=lossy, limit=limit)
1442
1344
except NoPushSupport:
1443
1345
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
1347
def pull(self, overwrite=False, stop_revision=None, local=False,
1449
possible_transports=None, run_hooks=True, _stop_revno=None,
1348
possible_transports=None, run_hooks=True, _stop_revno=None):
1451
1349
result = GitBranchPullResult()
1452
1350
result.source_branch = self.source
1453
1351
result.target_branch = self.target
1456
1354
stop_revision, stop_revno=_stop_revno)
1458
1356
def update_refs(old_refs):
1459
return self._update_refs(result, old_refs, new_refs, overwrite, tag_selector)
1357
return self._update_refs(result, old_refs, new_refs, overwrite)
1461
1359
result.revidmap, old_refs, new_refs = (
1462
1360
self.interrepo.fetch_refs(update_refs, lossy=False))
1478
1376
def push(self, overwrite=False, stop_revision=None, lossy=False,
1479
_override_hook_source_branch=None, _stop_revno=None,
1377
_override_hook_source_branch=None, _stop_revno=None):
1481
1378
result = GitBranchPushResult()
1482
1379
result.source_branch = self.source
1483
1380
result.target_branch = self.target
1488
1385
stop_revision, stop_revno=_stop_revno)
1490
1387
def update_refs(old_refs):
1491
return self._update_refs(result, old_refs, new_refs, overwrite, tag_selector)
1388
return self._update_refs(result, old_refs, new_refs, overwrite)
1493
1390
result.revidmap, old_refs, new_refs = (
1494
1391
self.interrepo.fetch_refs(