15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
from bzrlib import osutils, ui, urlutils
18
from bzrlib.errors import InvalidRevisionId, NoSuchRevision
18
from bzrlib.errors import InvalidRevisionId
19
19
from bzrlib.inventory import Inventory
20
20
from bzrlib.repository import InterRepository
21
21
from bzrlib.trace import info
22
22
from bzrlib.tsort import topo_sort
24
from bzrlib.plugins.git import git
24
25
from bzrlib.plugins.git.repository import (
25
26
LocalGitRepository,
29
from bzrlib.plugins.git.converter import GitObjectConverter
30
30
from bzrlib.plugins.git.remote import RemoteGitRepository
33
32
from dulwich.client import SimpleFetchGraphWalker
34
33
from dulwich.objects import Commit
39
38
class BzrFetchGraphWalker(object):
40
"""GraphWalker implementation that uses a Bazaar repository."""
42
40
def __init__(self, repository, mapping):
43
41
self.repository = repository
69
64
self.heads.update([p for p in ps if not p in self.done])
72
return self.mapping.revision_id_bzr_to_foreign(ret)[0]
67
return self.mapping.revision_id_bzr_to_foreign(ret)
73
68
except InvalidRevisionId:
78
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, gitmap, executable):
73
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, executable):
79
74
"""Import a git blob object into a bzr repository.
81
76
:param repo: bzr repository
92
87
ie.text_size = len(blob.data)
93
88
ie.text_sha1 = osutils.sha_string(blob.data)
94
89
ie.executable = executable
95
gitmap._idmap.add_entry(blob.sha().hexdigest(), "blob", (ie.file_id, ie.revision))
98
def import_git_tree(repo, mapping, path, tree, inv, parent_invs,
99
gitmap, lookup_object):
92
def import_git_tree(repo, mapping, path, tree, inv, parent_invs, lookup_object):
100
93
"""Import a git tree object into a bzr repository.
102
95
:param repo: A Bzr repository object
112
105
ie = inv.add_path(path, "directory", file_id)
113
106
ie.revision = text_revision
114
gitmap._idmap.add_entry(tree.sha().hexdigest(), "tree", (file_id, text_revision))
115
107
for mode, name, hexsha in tree.entries():
116
108
entry_kind = (mode & 0700000) / 0100000
117
109
basename = name.decode("utf-8")
121
113
child_path = urlutils.join(path, name)
122
114
if entry_kind == 0:
123
115
tree = lookup_object(hexsha)
124
import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, gitmap, lookup_object)
116
import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, lookup_object)
125
117
elif entry_kind == 1:
126
118
blob = lookup_object(hexsha)
127
119
fs_mode = mode & 0777
128
import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, gitmap, bool(fs_mode & 0111))
120
import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, bool(fs_mode & 0111))
130
122
raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
133
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever,
125
def import_git_objects(repo, mapping, object_iter, pb=None):
135
126
"""Import a set of git objects into a bzr repository.
137
128
:param repo: Bazaar repository
139
130
:param object_iter: Iterator over Git objects.
141
132
# TODO: a more (memory-)efficient implementation of this
134
for i, o in enumerate(object_iter):
136
pb.update("fetching objects", i)
145
141
# Find and convert commit objects
146
for o in object_iter.iterobjects():
142
for o in objects.itervalues():
147
143
if isinstance(o, Commit):
148
144
rev = mapping.import_commit(o)
149
root_trees[rev.revision_id] = object_iter[o.tree]
145
root_trees[rev.revision_id] = objects[o.tree]
150
146
revisions[rev.revision_id] = rev
151
147
graph.append((rev.revision_id, rev.parent_ids))
152
target_git_object_retriever._idmap.add_entry(o.sha().hexdigest(), "commit", (rev.revision_id, o._tree))
153
148
# Order the revisions
154
149
# Create the inventory objects
155
150
for i, revid in enumerate(topo_sort(graph)):
163
158
inv = Inventory()
164
159
inv.revision_id = rev.revision_id
165
160
def lookup_object(sha):
166
if sha in object_iter:
167
return object_iter[sha]
168
return target_git_object_retriever[sha]
163
return reconstruct_git_object(repo, mapping, sha)
169
164
parent_invs = [repo.get_inventory(r) for r in rev.parent_ids]
170
165
import_git_tree(repo, mapping, "", root_tree, inv, parent_invs,
171
target_git_object_retriever, lookup_object)
172
167
repo.add_revision(rev.revision_id, rev, inv)
170
def reconstruct_git_commit(repo, rev):
171
raise NotImplementedError(self.reconstruct_git_commit)
174
def reconstruct_git_object(repo, mapping, sha):
176
revid = mapping.revision_id_foreign_to_bzr(sha)
178
rev = repo.get_revision(revid)
179
except NoSuchRevision:
182
return reconstruct_git_commit(rev)
186
raise KeyError("No such object %s" % sha)
175
189
class InterGitNonGitRepository(InterRepository):
177
191
_matching_repo_format = GitFormat()
184
198
"""See InterRepository.copy_content."""
185
199
self.fetch(revision_id, pb, find_ghosts=False)
187
def fetch_objects(self, determine_wants, mapping, pb=None):
201
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
204
mapping = self.source.get_mapping()
188
205
def progress(text):
189
pb.update("git: %s" % text.rstrip("\r\n"), 0, 0)
206
pb.note("git: %s", text)
207
def determine_wants(heads):
208
if revision_id is None:
211
ret = [mapping.revision_id_bzr_to_foreign(revision_id)]
212
return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
190
213
graph_walker = BzrFetchGraphWalker(self.target, mapping)
193
216
create_pb = pb = ui.ui_factory.nested_progress_bar()
194
target_git_object_retriever = GitObjectConverter(self.target, mapping)
197
218
self.target.lock_write()
199
220
self.target.start_write_group()
201
objects_iter = self.source.fetch_objects(determine_wants,
203
target_git_object_retriever.__getitem__,
205
import_git_objects(self.target, mapping, objects_iter,
206
target_git_object_retriever, pb)
222
import_git_objects(self.target, mapping,
223
iter(self.source.fetch_objects(determine_wants, graph_walker,
208
226
self.target.commit_write_group()
213
231
create_pb.finished()
215
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
218
mapping = self.source.get_mapping()
219
def determine_wants(heads):
220
if revision_id is None:
223
ret = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
224
return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
225
return self.fetch_objects(determine_wants, mapping, pb)
228
234
def is_compatible(source, target):
229
235
"""Be compatible with GitRepository."""
255
261
if revision_id is None:
256
262
determine_wants = lambda x: [y for y in x.values() if not y in r.object_store]
258
args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
264
args = [mapping.revision_id_bzr_to_foreign(revision_id)]
259
265
determine_wants = lambda x: [y for y in args if not y in r.object_store]
261
267
graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)