540
532
return pack_hints, last_imported
543
class InterFromGitRepository(InterRepository):
545
_matching_repo_format = GitRepositoryFormat()
547
def _target_has_shas(self, shas):
548
raise NotImplementedError(self._target_has_shas)
550
def get_determine_wants_heads(self, wants, include_tags=False):
552
def determine_wants(refs):
553
potential = set(wants)
555
for k, unpeeled in refs.iteritems():
556
if k.endswith("^{}"):
560
if unpeeled == ZERO_SHA:
562
potential.add(unpeeled)
563
return list(potential - self._target_has_shas(potential))
564
return determine_wants
566
def determine_wants_all(self, refs):
567
raise NotImplementedError(self.determine_wants_all)
570
def _get_repo_format_to_test():
573
def copy_content(self, revision_id=None):
574
"""See InterRepository.copy_content."""
575
self.fetch(revision_id, find_ghosts=False)
577
def search_missing_revision_ids(self,
578
find_ghosts=True, revision_ids=None, if_present_ids=None,
580
if limit is not None:
581
raise errors.FetchLimitUnsupported(self)
585
todo.extend(revision_ids)
587
todo.extend(revision_ids)
588
for revid in revision_ids:
589
if revid == NULL_REVISION:
591
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
592
git_shas.append(git_sha)
593
walker = Walker(self.source._git.object_store,
594
include=git_shas, exclude=[
595
sha for sha in self.target.controldir.get_refs_container().as_dict().values()
597
missing_revids = set()
599
missing_revids.add(self.source.lookup_foreign_revision_id(entry.commit.id))
600
return self.source.revision_ids_to_search_result(missing_revids)
603
class InterGitNonGitRepository(InterFromGitRepository):
604
"""Base InterRepository that copies revisions from a Git into a non-Git
607
def _target_has_shas(self, shas):
611
revid = self.source.lookup_foreign_revision_id(sha)
612
except NotCommitError:
613
# Commit is definitely not present
617
return set([revids[r] for r in self.target.has_revisions(revids)])
619
def determine_wants_all(self, refs):
621
for k, v in refs.iteritems():
622
# For non-git target repositories, only worry about peeled
625
potential.add(self.source.controldir.get_peeled(k) or v)
626
return list(potential - self._target_has_shas(potential))
628
def get_determine_wants_heads(self, wants, include_tags=False):
630
def determine_wants(refs):
631
potential = set(wants)
633
for k, unpeeled in refs.iteritems():
636
if unpeeled == ZERO_SHA:
638
potential.add(self.source.controldir.get_peeled(k) or unpeeled)
639
return list(potential - self._target_has_shas(potential))
640
return determine_wants
642
def _warn_slow(self):
644
'Fetching from Git to Bazaar repository. '
645
'For better performance, fetch into a Git repository.')
647
def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
648
"""Fetch objects from a remote server.
650
:param determine_wants: determine_wants callback
651
:param mapping: BzrGitMapping to use
652
:param limit: Maximum number of commits to import.
653
:return: Tuple with pack hint, last imported revision id and remote refs
655
raise NotImplementedError(self.fetch_objects)
657
def get_determine_wants_revids(self, revids, include_tags=False):
659
for revid in set(revids):
660
if self.target.has_revision(revid):
662
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
664
return self.get_determine_wants_heads(wants, include_tags=include_tags)
666
def fetch(self, revision_id=None, find_ghosts=False,
667
mapping=None, fetch_spec=None, include_tags=False):
669
mapping = self.source.get_mapping()
670
if revision_id is not None:
671
interesting_heads = [revision_id]
672
elif fetch_spec is not None:
673
recipe = fetch_spec.get_recipe()
674
if recipe[0] in ("search", "proxy-search"):
675
interesting_heads = recipe[1]
677
raise AssertionError("Unsupported search result type %s" %
680
interesting_heads = None
682
if interesting_heads is not None:
683
determine_wants = self.get_determine_wants_revids(
684
interesting_heads, include_tags=include_tags)
686
determine_wants = self.determine_wants_all
688
(pack_hint, _, remote_refs) = self.fetch_objects(determine_wants,
690
if pack_hint is not None and self.target._format.pack_compresses:
691
self.target.pack(hint=pack_hint)
695
_GIT_PROGRESS_RE = re.compile(r"(.*?): +(\d+)% \((\d+)/(\d+)\)")
696
def report_git_progress(pb, text):
697
text = text.rstrip("\r\n")
698
trace.mutter('git: %s', text)
699
g = _GIT_PROGRESS_RE.match(text)
701
(text, pct, current, total) = g.groups()
702
pb.update(text, int(current), int(total))
704
pb.update(text, 0, 0)
707
535
class DetermineWantsRecorder(object):
709
537
def __init__(self, actual):
719
547
return self.wants
722
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
723
"""InterRepository that copies revisions from a remote Git into a non-Git
726
def get_target_heads(self):
727
# FIXME: This should be more efficient
728
all_revs = self.target.all_revision_ids()
729
parent_map = self.target.get_parent_map(all_revs)
731
map(all_parents.update, parent_map.itervalues())
732
return set(all_revs) - all_parents
734
def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
735
"""See `InterGitNonGitRepository`."""
737
store = BazaarObjectStore(self.target, mapping)
738
with store.lock_write():
739
heads = self.get_target_heads()
740
graph_walker = ObjectStoreGraphWalker(
741
[store._lookup_revision_sha1(head) for head in heads],
742
lambda sha: store[sha].parents)
743
wants_recorder = DetermineWantsRecorder(determine_wants)
745
pb = ui.ui_factory.nested_progress_bar()
747
objects_iter = self.source.fetch_objects(
748
wants_recorder, graph_walker, store.get_raw,
749
progress=lambda text: report_git_progress(pb, text),)
750
trace.mutter("Importing %d new revisions",
751
len(wants_recorder.wants))
752
(pack_hint, last_rev) = import_git_objects(self.target,
753
mapping, objects_iter, store, wants_recorder.wants, pb,
755
return (pack_hint, last_rev, wants_recorder.remote_refs)
760
def is_compatible(source, target):
761
"""Be compatible with GitRepository."""
762
if not isinstance(source, RemoteGitRepository):
764
if not target.supports_rich_root():
766
if isinstance(target, GitRepository):
768
if not getattr(target._format, "supports_full_versioned_files", True):
773
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
774
"""InterRepository that copies revisions from a local Git into a non-Git
777
def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
778
"""See `InterGitNonGitRepository`."""
780
remote_refs = self.source.controldir.get_refs_container().as_dict()
781
wants = determine_wants(remote_refs)
783
pb = ui.ui_factory.nested_progress_bar()
784
target_git_object_retriever = BazaarObjectStore(self.target, mapping)
786
target_git_object_retriever.lock_write()
788
(pack_hint, last_rev) = import_git_objects(self.target,
789
mapping, self.source._git.object_store,
790
target_git_object_retriever, wants, pb, limit)
791
return (pack_hint, last_rev, remote_refs)
793
target_git_object_retriever.unlock()
798
def is_compatible(source, target):
799
"""Be compatible with GitRepository."""
800
if not isinstance(source, LocalGitRepository):
802
if not target.supports_rich_root():
804
if isinstance(target, GitRepository):
806
if not getattr(target._format, "supports_full_versioned_files", True):
811
class InterGitGitRepository(InterFromGitRepository):
812
"""InterRepository that copies between Git repositories."""
814
def fetch_refs(self, update_refs, lossy=False):
816
raise errors.LossyPushToSameVCS(self.source, self.target)
817
old_refs = self.target.controldir.get_refs_container()
819
def determine_wants(heads):
820
old_refs = dict([(k, (v, None)) for (k, v) in heads.as_dict().iteritems()])
821
new_refs = update_refs(old_refs)
822
ref_changes.update(new_refs)
823
return [sha1 for (sha1, bzr_revid) in new_refs.itervalues()]
824
self.fetch_objects(determine_wants, lossy=lossy)
825
for k, (git_sha, bzr_revid) in ref_changes.iteritems():
826
self.target._git.refs[k] = git_sha
827
new_refs = self.target.controldir.get_refs_container()
828
return None, old_refs, new_refs
830
def fetch_objects(self, determine_wants, mapping=None, limit=None, lossy=False):
832
raise errors.LossyPushToSameVCS(self.source, self.target)
833
if limit is not None:
834
raise errors.FetchLimitUnsupported(self)
835
graphwalker = self.target._git.get_graph_walker()
836
if (isinstance(self.source, LocalGitRepository) and
837
isinstance(self.target, LocalGitRepository)):
838
pb = ui.ui_factory.nested_progress_bar()
840
refs = self.source._git.fetch(self.target._git, determine_wants,
841
lambda text: report_git_progress(pb, text))
844
return (None, None, refs)
845
elif (isinstance(self.source, LocalGitRepository) and
846
isinstance(self.target, RemoteGitRepository)):
847
raise NotImplementedError
848
elif (isinstance(self.source, RemoteGitRepository) and
849
isinstance(self.target, LocalGitRepository)):
850
pb = ui.ui_factory.nested_progress_bar()
852
if CAPABILITY_THIN_PACK in self.source.controldir._client._fetch_capabilities:
853
# TODO(jelmer): Avoid reading entire file into memory and
854
# only processing it after the whole file has been fetched.
860
self.target._git.object_store.move_in_thin_pack(f)
865
f, commit, abort = self.target._git.object_store.add_pack()
867
refs = self.source.controldir.fetch_pack(
868
determine_wants, graphwalker, f.write,
869
lambda text: report_git_progress(pb, text))
871
return (None, None, refs)
872
except BaseException:
878
raise AssertionError("fetching between %r and %r not supported" %
879
(self.source, self.target))
881
def _target_has_shas(self, shas):
882
return set([sha for sha in shas if sha in self.target._git.object_store])
884
def fetch(self, revision_id=None, find_ghosts=False,
885
mapping=None, fetch_spec=None, branches=None, limit=None, include_tags=False):
887
mapping = self.source.get_mapping()
889
if revision_id is not None:
891
elif fetch_spec is not None:
892
recipe = fetch_spec.get_recipe()
893
if recipe[0] in ("search", "proxy-search"):
896
raise AssertionError(
897
"Unsupported search result type %s" % recipe[0])
899
if branches is not None:
900
def determine_wants(refs):
902
for name, value in refs.iteritems():
903
if value == ZERO_SHA:
906
if name in branches or (include_tags and is_tag(name)):
909
elif fetch_spec is None and revision_id is None:
910
determine_wants = self.determine_wants_all
912
determine_wants = self.get_determine_wants_revids(args, include_tags=include_tags)
913
wants_recorder = DetermineWantsRecorder(determine_wants)
914
self.fetch_objects(wants_recorder, mapping, limit=limit)
915
return wants_recorder.remote_refs
918
def is_compatible(source, target):
919
"""Be compatible with GitRepository."""
920
return (isinstance(source, GitRepository) and
921
isinstance(target, GitRepository))
923
def get_determine_wants_revids(self, revids, include_tags=False):
925
for revid in set(revids):
926
if self.target.has_revision(revid):
928
git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
930
return self.get_determine_wants_heads(wants, include_tags=include_tags)
932
def determine_wants_all(self, refs):
933
potential = set([v for v in refs.values() if not v == ZERO_SHA])
934
return list(potential - self._target_has_shas(potential))