136
130
for revid in revision_ids:
137
131
if revid == NULL_REVISION:
140
git_sha = self.source_store._lookup_revision_sha1(revid)
142
raise NoSuchRevision(revid, self.source)
133
git_sha = self.source_store._lookup_revision_sha1(revid)
143
134
git_shas.append(git_sha)
135
walker = Walker(self.source_store,
136
include=git_shas, exclude=[
148
137
sha for sha in self.target.controldir.get_refs_container().as_dict().values()
149
138
if sha != ZERO_SHA])
150
139
missing_revids = set()
151
140
for entry in walker:
152
for (kind, type_data) in self.source_store.lookup_git_sha(
141
for (kind, type_data) in self.source_store.lookup_git_sha(entry.commit.id):
154
142
if kind == "commit":
155
143
missing_revids.add(type_data[0])
156
return self.source.revision_ids_to_search_result(missing_revids)
144
return self.source.revision_ids_to_search_result(missing_revids)
158
146
def _warn_slow(self):
159
147
if not config.GlobalConfig().suppress_warning('slow_intervcs_push'):
213
201
stop_revids.append(revid)
215
203
graph = self.source.get_graph()
216
with ui.ui_factory.nested_progress_bar() as pb:
204
pb = ui.ui_factory.nested_progress_bar()
217
206
while stop_revids:
218
207
new_stop_revids = []
219
208
for revid in stop_revids:
220
209
sha1 = revid_sha_map.get(revid)
221
if (revid not in missing and
222
self._revision_needs_fetching(sha1, revid)):
210
if (not revid in missing and
211
self._revision_needs_fetching(sha1, revid)):
223
212
missing.add(revid)
224
213
new_stop_revids.append(revid)
225
214
stop_revids = set()
226
215
parent_map = graph.get_parent_map(new_stop_revids)
227
for parent_revids in parent_map.values():
216
for parent_revids in parent_map.itervalues():
228
217
stop_revids.update(parent_revids)
229
218
pb.update("determining revisions to fetch", len(missing))
230
221
return graph.iter_topo_order(missing)
232
223
def _get_target_bzr_refs(self):
236
227
with Git SHA, Bazaar revid as values.
239
231
for k in self.target._git.refs.allkeys():
241
v = self.target._git.refs.read_ref(k)
233
v = self.target._git.refs[k]
246
if v and not v.startswith(SYMREF):
248
for (kind, type_data) in self.source_store.lookup_git_sha(
250
if kind == "commit" and self.source.has_revision(
238
for (kind, type_data) in self.source_store.lookup_git_sha(v):
239
if kind == "commit" and self.source.has_revision(type_data[0]):
256
246
bzr_refs[k] = (v, revid)
259
249
def fetch_refs(self, update_refs, lossy, overwrite=False):
260
250
self._warn_slow()
262
251
with self.source_store.lock_read():
263
252
old_refs = self._get_target_bzr_refs()
264
253
new_refs = update_refs(old_refs)
265
254
revidmap = self.fetch_objects(
266
[(git_sha, bzr_revid)
267
for (git_sha, bzr_revid) in new_refs.values()
268
if git_sha is None or not git_sha.startswith(SYMREF)],
270
for name, (gitid, revid) in new_refs.items():
255
[(git_sha, bzr_revid) for (git_sha, bzr_revid) in new_refs.values() if git_sha is None or not git_sha.startswith('ref:')], lossy=lossy)
256
for name, (gitid, revid) in new_refs.iteritems():
271
257
if gitid is None:
273
259
gitid = revidmap[revid][0]
275
261
gitid = self.source_store._lookup_revision_sha1(revid)
276
if gitid.startswith(SYMREF):
277
self.target_refs.set_symbolic_ref(
278
name, gitid[len(SYMREF):])
281
old_git_id = old_refs[name][0]
283
self.target_refs.add_if_new(name, gitid)
285
self.target_refs.set_if_equals(name, old_git_id, gitid)
286
result_refs[name] = (gitid, revid if not lossy else self.mapping.revision_id_foreign_to_bzr(gitid))
287
return revidmap, old_refs, result_refs
262
if len(gitid) != 40 and not gitid.startswith('ref: '):
263
raise AssertionError("invalid ref contents: %r" % gitid)
264
self.target_refs[name] = gitid
265
return revidmap, old_refs, new_refs
289
267
def fetch_objects(self, revs, lossy, limit=None):
290
268
if not lossy and not self.mapping.roundtripping:
291
269
for git_sha, bzr_revid in revs:
292
if (bzr_revid is not None and
293
needs_roundtripping(self.source, bzr_revid)):
270
if bzr_revid is not None and needs_roundtripping(self.source, bzr_revid):
294
271
raise NoPushSupport(self.source, self.target, self.mapping,
296
273
with self.source_store.lock_read():
297
274
todo = list(self.missing_revisions(revs))[:limit]
299
with ui.ui_factory.nested_progress_bar() as pb:
276
pb = ui.ui_factory.nested_progress_bar()
300
278
object_generator = MissingObjectsIterator(
301
279
self.source_store, self.source, pb)
302
280
for (old_revid, git_sha) in object_generator.import_revisions(
305
new_revid = self.mapping.revision_id_foreign_to_bzr(
283
new_revid = self.mapping.revision_id_foreign_to_bzr(git_sha)
308
285
new_revid = old_revid
310
287
self.mapping.revision_id_bzr_to_foreign(old_revid)
311
288
except InvalidRevisionId:
289
refname = self.mapping.revid_as_refname(old_revid)
290
self.target_refs[refname] = git_sha
313
291
revidmap[old_revid] = (git_sha, new_revid)
314
292
self.target_store.add_objects(object_generator)
317
297
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
318
fetch_spec=None, mapped_refs=None, lossy=False):
298
fetch_spec=None, mapped_refs=None):
319
299
if mapped_refs is not None:
320
300
stop_revisions = mapped_refs
321
301
elif revision_id is not None:
325
305
if recipe[0] in ("search", "proxy-search"):
326
306
stop_revisions = [(None, revid) for revid in recipe[1]]
328
raise AssertionError(
329
"Unsupported search result type %s" % recipe[0])
308
raise AssertionError("Unsupported search result type %s" % recipe[0])
331
stop_revisions = [(None, revid)
332
for revid in self.source.all_revision_ids()]
310
stop_revisions = [(None, revid) for revid in self.source.all_revision_ids()]
333
311
self._warn_slow()
335
revidmap = self.fetch_objects(stop_revisions, lossy=lossy)
313
self.fetch_objects(stop_revisions, lossy=False)
336
314
except NoPushSupport:
337
315
raise NoRoundtrippingSupport(self.source, self.target)
338
return FetchResult(revidmap)
341
318
def is_compatible(source, target):
352
329
raise NoPushSupport(self.source, self.target, self.mapping)
353
330
unpeel_map = UnpeelMap.from_repository(self.source)
356
def git_update_refs(old_refs):
332
def determine_wants(old_refs):
359
k: (v, None) for (k, v) in old_refs.items()}
360
new_refs = update_refs(self.old_refs)
361
for name, (gitid, revid) in new_refs.items():
334
self.old_refs = dict([(k, (v, None)) for (k, v) in old_refs.iteritems()])
335
self.new_refs = update_refs(self.old_refs)
336
for name, (gitid, revid) in self.new_refs.iteritems():
362
337
if gitid is None:
363
338
git_sha = self.source_store._lookup_revision_sha1(revid)
364
gitid = unpeel_map.re_unpeel_tag(
365
git_sha, old_refs.get(name))
339
gitid = unpeel_map.re_unpeel_tag(git_sha, old_refs.get(name))
366
340
if not overwrite:
367
if remote_divergence(
368
old_refs.get(name), gitid, self.source_store):
341
if remote_divergence(old_refs.get(name), gitid, self.source_store):
369
342
raise DivergedBranches(self.source, self.target)
370
343
ret[name] = gitid
372
345
self._warn_slow()
373
346
with self.source_store.lock_read():
374
result = self.target.send_pack(
375
git_update_refs, self.source_store.generate_lossy_pack_data)
376
if result is not None and not isinstance(result, dict):
377
for ref, error in result.ref_status.items():
379
raise RemoteGitError(
380
'unable to update ref %r: %s' % (ref, error))
381
new_refs = result.refs
382
else: # dulwich < 0.20.3
347
new_refs = self.target.send_pack(determine_wants,
348
self.source_store.generate_lossy_pack_data)
384
349
# FIXME: revidmap?
385
return revidmap, self.old_refs, new_refs
350
return revidmap, self.old_refs, self.new_refs
388
353
def is_compatible(source, target):
412
363
def _target_has_shas(self, shas):
413
364
raise NotImplementedError(self._target_has_shas)
415
def get_determine_wants_heads(self, wants, include_tags=False, tag_selector=None):
366
def get_determine_wants_heads(self, wants, include_tags=False):
416
367
wants = set(wants)
418
368
def determine_wants(refs):
420
for k, v in refs.items():
421
if k.endswith(ANNOTATED_TAG_SUFFIX):
422
unpeel_lookup[v] = refs[k[:-len(ANNOTATED_TAG_SUFFIX)]]
423
potential = set([unpeel_lookup.get(w, w) for w in wants])
369
potential = set(wants)
425
for k, sha in refs.items():
426
if k.endswith(ANNOTATED_TAG_SUFFIX):
429
tag_name = ref_to_tag_name(k)
432
if tag_selector and not tag_selector(tag_name):
371
for k, unpeeled in refs.iteritems():
372
if k.endswith("^{}"):
376
if unpeeled == ZERO_SHA:
378
potential.add(unpeeled)
437
379
return list(potential - self._target_has_shas(potential))
438
380
return determine_wants
449
391
self.fetch(revision_id, find_ghosts=False)
451
393
def search_missing_revision_ids(self,
452
find_ghosts=True, revision_ids=None,
453
if_present_ids=None, limit=None):
394
find_ghosts=True, revision_ids=None, if_present_ids=None,
454
396
if limit is not None:
455
397
raise FetchLimitUnsupported(self)
456
if revision_ids is None and if_present_ids is None:
457
todo = set(self.source.all_revision_ids())
460
if revision_ids is not None:
461
for revid in revision_ids:
462
if not self.source.has_revision(revid):
463
raise NoSuchRevision(revid, self.source)
464
todo.update(revision_ids)
465
if if_present_ids is not None:
466
todo.update(if_present_ids)
467
result_set = todo.difference(self.target.all_revision_ids())
468
result_parents = set(itertools.chain.from_iterable(
469
self.source.get_graph().get_parent_map(result_set).values()))
470
included_keys = result_set.intersection(result_parents)
471
start_keys = result_set.difference(included_keys)
472
exclude_keys = result_parents.difference(result_set)
473
return GitSearchResult(start_keys, exclude_keys, result_set)
401
todo.extend(revision_ids)
403
todo.extend(revision_ids)
404
with self.lock_read():
405
for revid in revision_ids:
406
if revid == NULL_REVISION:
408
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
409
git_shas.append(git_sha)
410
walker = Walker(self.source._git.object_store,
411
include=git_shas, exclude=[
412
sha for sha in self.target.controldir.get_refs_container().as_dict().values()
414
missing_revids = set()
416
missing_revids.add(self.source.lookup_foreign_revision_id(entry.commit.id))
417
return self.source.revision_ids_to_search_result(missing_revids)
476
420
class InterGitNonGitRepository(InterFromGitRepository):
492
436
def determine_wants_all(self, refs):
493
437
potential = set()
494
for k, v in refs.items():
438
for k, v in refs.iteritems():
495
439
# For non-git target repositories, only worry about peeled
496
440
if v == ZERO_SHA:
498
442
potential.add(self.source.controldir.get_peeled(k) or v)
499
443
return list(potential - self._target_has_shas(potential))
445
def get_determine_wants_heads(self, wants, include_tags=False):
447
def determine_wants(refs):
448
potential = set(wants)
450
for k, unpeeled in refs.iteritems():
453
if unpeeled == ZERO_SHA:
455
potential.add(self.source.controldir.get_peeled(k) or unpeeled)
456
return list(potential - self._target_has_shas(potential))
457
return determine_wants
501
459
def _warn_slow(self):
502
460
if not config.GlobalConfig().suppress_warning('slow_intervcs_push'):
510
468
:param determine_wants: determine_wants callback
511
469
:param mapping: BzrGitMapping to use
512
470
:param limit: Maximum number of commits to import.
513
:return: Tuple with pack hint, last imported revision id and remote
471
:return: Tuple with pack hint, last imported revision id and remote refs
516
473
raise NotImplementedError(self.fetch_objects)
518
def get_determine_wants_revids(self, revids, include_tags=False, tag_selector=None):
475
def get_determine_wants_revids(self, revids, include_tags=False):
520
477
for revid in set(revids):
521
478
if self.target.has_revision(revid):
523
480
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
524
481
wants.add(git_sha)
525
return self.get_determine_wants_heads(
526
wants, include_tags=include_tags, tag_selector=tag_selector)
482
return self.get_determine_wants_heads(wants, include_tags=include_tags)
528
484
def fetch(self, revision_id=None, find_ghosts=False,
529
mapping=None, fetch_spec=None, include_tags=False, lossy=False):
485
mapping=None, fetch_spec=None, include_tags=False):
530
486
if mapping is None:
531
487
mapping = self.source.get_mapping()
532
488
if revision_id is not None:
580
533
lambda sha: store[sha].parents)
581
534
wants_recorder = DetermineWantsRecorder(determine_wants)
583
with ui.ui_factory.nested_progress_bar() as pb:
536
pb = ui.ui_factory.nested_progress_bar()
584
538
objects_iter = self.source.fetch_objects(
585
539
wants_recorder, graph_walker, store.get_raw)
586
540
trace.mutter("Importing %d new revisions",
587
541
len(wants_recorder.wants))
588
(pack_hint, last_rev) = import_git_objects(
589
self.target, mapping, objects_iter, store,
590
wants_recorder.wants, pb, limit)
542
(pack_hint, last_rev) = import_git_objects(self.target,
543
mapping, objects_iter, store, wants_recorder.wants, pb,
591
545
return (pack_hint, last_rev, wants_recorder.remote_refs)
594
550
def is_compatible(source, target):
613
569
self._warn_slow()
614
570
remote_refs = self.source.controldir.get_refs_container().as_dict()
615
571
wants = determine_wants(remote_refs)
573
pb = ui.ui_factory.nested_progress_bar()
616
574
target_git_object_retriever = get_object_store(self.target, mapping)
617
with ui.ui_factory.nested_progress_bar() as pb:
618
576
target_git_object_retriever.lock_write()
620
(pack_hint, last_rev) = import_git_objects(
621
self.target, mapping, self.source._git.object_store,
578
(pack_hint, last_rev) = import_git_objects(self.target,
579
mapping, self.source._git.object_store,
622
580
target_git_object_retriever, wants, pb, limit)
623
581
return (pack_hint, last_rev, remote_refs)
625
583
target_git_object_retriever.unlock()
628
588
def is_compatible(source, target):
646
606
raise LossyPushToSameVCS(self.source, self.target)
647
607
old_refs = self.target.controldir.get_refs_container()
650
609
def determine_wants(heads):
651
old_refs = dict([(k, (v, None))
652
for (k, v) in heads.as_dict().items()])
610
old_refs = dict([(k, (v, None)) for (k, v) in heads.as_dict().iteritems()])
653
611
new_refs = update_refs(old_refs)
654
612
ref_changes.update(new_refs)
655
return [sha1 for (sha1, bzr_revid) in new_refs.values()]
613
return [sha1 for (sha1, bzr_revid) in new_refs.itervalues()]
656
614
self.fetch_objects(determine_wants, lossy=lossy)
657
for k, (git_sha, bzr_revid) in ref_changes.items():
615
for k, (git_sha, bzr_revid) in ref_changes.iteritems():
658
616
self.target._git.refs[k] = git_sha
659
617
new_refs = self.target.controldir.get_refs_container()
660
618
return None, old_refs, new_refs
662
def fetch_objects(self, determine_wants, mapping=None, limit=None,
620
def fetch_objects(self, determine_wants, mapping=None, limit=None, lossy=False):
664
621
raise NotImplementedError(self.fetch_objects)
666
623
def _target_has_shas(self, shas):
668
[sha for sha in shas if sha in self.target._git.object_store])
624
return set([sha for sha in shas if sha in self.target._git.object_store])
670
626
def fetch(self, revision_id=None, find_ghosts=False,
671
mapping=None, fetch_spec=None, branches=None, limit=None,
672
include_tags=False, lossy=False):
627
mapping=None, fetch_spec=None, branches=None, limit=None, include_tags=False):
673
628
if mapping is None:
674
629
mapping = self.source.get_mapping()
675
630
if revision_id is not None:
683
638
"Unsupported search result type %s" % recipe[0])
685
640
if branches is not None:
686
determine_wants = self.get_determine_wants_branches(
687
branches, include_tags=include_tags)
641
def determine_wants(refs):
643
for name, value in refs.iteritems():
644
if value == ZERO_SHA:
647
if name in branches or (include_tags and is_tag(name)):
688
650
elif fetch_spec is None and revision_id is None:
689
651
determine_wants = self.determine_wants_all
691
determine_wants = self.get_determine_wants_revids(
692
args, include_tags=include_tags)
653
determine_wants = self.get_determine_wants_revids(args, include_tags=include_tags)
693
654
wants_recorder = DetermineWantsRecorder(determine_wants)
694
self.fetch_objects(wants_recorder, mapping, limit=limit, lossy=lossy)
695
result = FetchResult()
696
result.refs = wants_recorder.remote_refs
655
self.fetch_objects(wants_recorder, mapping, limit=limit)
656
return wants_recorder.remote_refs
699
def get_determine_wants_revids(self, revids, include_tags=False, tag_selector=None):
658
def get_determine_wants_revids(self, revids, include_tags=False):
701
660
for revid in set(revids):
702
661
if revid == NULL_REVISION:
704
663
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
705
664
wants.add(git_sha)
706
return self.get_determine_wants_heads(wants, include_tags=include_tags, tag_selector=tag_selector)
708
def get_determine_wants_branches(self, branches, include_tags=False):
709
def determine_wants(refs):
711
for name, value in refs.items():
712
if value == ZERO_SHA:
715
if name.endswith(ANNOTATED_TAG_SUFFIX):
718
if name in branches or (include_tags and is_tag(name)):
721
return determine_wants
665
return self.get_determine_wants_heads(wants, include_tags=include_tags)
723
667
def determine_wants_all(self, refs):
725
v for k, v in refs.items()
726
if not v == ZERO_SHA and not k.endswith(ANNOTATED_TAG_SUFFIX)])
668
potential = set([v for v in refs.values() if not v == ZERO_SHA])
727
669
return list(potential - self._target_has_shas(potential))
730
672
class InterLocalGitLocalGitRepository(InterGitGitRepository):
732
def fetch_objects(self, determine_wants, mapping=None, limit=None,
674
def fetch_objects(self, determine_wants, mapping=None, limit=None, lossy=False):
735
676
raise LossyPushToSameVCS(self.source, self.target)
736
677
if limit is not None:
737
678
raise FetchLimitUnsupported(self)
738
from .remote import DefaultProgressReporter
739
with ui.ui_factory.nested_progress_bar() as pb:
740
progress = DefaultProgressReporter(pb).progress
741
refs = self.source._git.fetch(
742
self.target._git, determine_wants,
679
refs = self.source._git.fetch(self.target._git, determine_wants)
744
680
return (None, None, refs)
753
689
class InterRemoteGitLocalGitRepository(InterGitGitRepository):
755
def fetch_objects(self, determine_wants, mapping=None, limit=None,
691
def fetch_objects(self, determine_wants, mapping=None, limit=None, lossy=False):
758
693
raise LossyPushToSameVCS(self.source, self.target)
759
694
if limit is not None:
760
695
raise FetchLimitUnsupported(self)
761
696
graphwalker = self.target._git.get_graph_walker()
762
if (CAPABILITY_THIN_PACK in
763
self.source.controldir._client._fetch_capabilities):
697
if CAPABILITY_THIN_PACK in self.source.controldir._client._fetch_capabilities:
764
698
# TODO(jelmer): Avoid reading entire file into memory and
765
699
# only processing it after the whole file has been fetched.