14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
from bzrlib import osutils, ui, urlutils
18
from bzrlib.errors import InvalidRevisionId
17
from cStringIO import StringIO
19
from dulwich.client import SimpleFetchGraphWalker
20
from dulwich.objects import Commit
27
from bzrlib.errors import (
19
31
from bzrlib.inventory import Inventory
20
32
from bzrlib.repository import InterRepository
21
33
from bzrlib.trace import info
22
34
from bzrlib.tsort import topo_sort
24
from bzrlib.plugins.git import git
25
36
from bzrlib.plugins.git.repository import (
26
37
LocalGitRepository,
41
from bzrlib.plugins.git.converter import GitObjectConverter
30
42
from bzrlib.plugins.git.remote import RemoteGitRepository
32
from dulwich.objects import Commit
34
from cStringIO import StringIO
37
46
class BzrFetchGraphWalker(object):
47
"""GraphWalker implementation that uses a Bazaar repository."""
39
49
def __init__(self, repository, mapping):
40
50
self.repository = repository
63
76
self.heads.update([p for p in ps if not p in self.done])
66
return self.mapping.revision_id_bzr_to_foreign(ret)
79
return self.mapping.revision_id_bzr_to_foreign(ret)[0]
67
80
except InvalidRevisionId:
72
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, executable):
85
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, gitmap, executable):
73
86
"""Import a git blob object into a bzr repository.
75
88
:param repo: bzr repository
86
99
ie.text_size = len(blob.data)
87
100
ie.text_sha1 = osutils.sha_string(blob.data)
88
101
ie.executable = executable
91
def import_git_tree(repo, mapping, path, tree, inv, parent_invs, lookup_object):
102
gitmap._idmap.add_entry(blob.sha().hexdigest(), "blob", (ie.file_id, ie.revision))
105
def import_git_tree(repo, mapping, path, tree, inv, parent_invs,
106
gitmap, lookup_object):
92
107
"""Import a git tree object into a bzr repository.
94
109
:param repo: A Bzr repository object
112
128
child_path = urlutils.join(path, name)
113
129
if entry_kind == 0:
114
130
tree = lookup_object(hexsha)
115
import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, lookup_object)
131
import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, gitmap, lookup_object)
116
132
elif entry_kind == 1:
117
133
blob = lookup_object(hexsha)
118
134
fs_mode = mode & 0777
119
import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, bool(fs_mode & 0111))
135
import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, gitmap, bool(fs_mode & 0111))
121
137
raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
124
def import_git_objects(repo, mapping, object_iter, pb=None):
140
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever,
125
142
"""Import a set of git objects into a bzr repository.
127
144
:param repo: Bazaar repository
129
146
:param object_iter: Iterator over Git objects.
131
148
# TODO: a more (memory-)efficient implementation of this
133
for i, o in enumerate(object_iter):
135
pb.update("fetching objects", i)
140
152
# Find and convert commit objects
141
for o in objects.itervalues():
153
for o in object_iter.iterobjects():
142
154
if isinstance(o, Commit):
143
155
rev = mapping.import_commit(o)
144
root_trees[rev.revision_id] = objects[o.tree]
156
root_trees[rev.revision_id] = object_iter[o.tree]
145
157
revisions[rev.revision_id] = rev
146
158
graph.append((rev.revision_id, rev.parent_ids))
159
target_git_object_retriever._idmap.add_entry(o.sha().hexdigest(), "commit", (rev.revision_id, o._tree))
147
160
# Order the revisions
148
161
# Create the inventory objects
149
162
for i, revid in enumerate(topo_sort(graph)):
157
170
inv = Inventory()
158
171
inv.revision_id = rev.revision_id
159
172
def lookup_object(sha):
162
return reconstruct_git_object(repo, mapping, sha)
173
if sha in object_iter:
174
return object_iter[sha]
175
return target_git_object_retriever[sha]
163
176
parent_invs = [repo.get_inventory(r) for r in rev.parent_ids]
164
import_git_tree(repo, mapping, "", root_tree, inv, parent_invs, lookup_object)
177
import_git_tree(repo, mapping, "", root_tree, inv, parent_invs,
178
target_git_object_retriever, lookup_object)
165
179
repo.add_revision(rev.revision_id, rev, inv)
168
def reconstruct_git_commit(repo, rev):
169
raise NotImplementedError(self.reconstruct_git_commit)
172
def reconstruct_git_object(repo, mapping, sha):
174
revid = mapping.revision_id_foreign_to_bzr(sha)
176
rev = repo.get_revision(revid)
177
except NoSuchRevision:
180
return reconstruct_git_commit(rev)
184
raise KeyError("No such object %s" % sha)
187
class InterGitRepository(InterRepository):
182
class InterGitNonGitRepository(InterRepository):
189
184
_matching_repo_format = GitFormat()
196
191
"""See InterRepository.copy_content."""
197
192
self.fetch(revision_id, pb, find_ghosts=False)
199
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
202
mapping = self.source.get_mapping()
194
def fetch_objects(self, determine_wants, mapping, pb=None):
203
195
def progress(text):
204
pb.note("git: %s", text)
205
def determine_wants(heads):
206
if revision_id is None:
209
ret = [mapping.revision_id_bzr_to_foreign(revision_id)]
210
return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
196
pb.update("git: %s" % text.rstrip("\r\n"), 0, 0)
211
197
graph_walker = BzrFetchGraphWalker(self.target, mapping)
214
200
create_pb = pb = ui.ui_factory.nested_progress_bar()
201
target_git_object_retriever = GitObjectConverter(self.target, mapping)
216
204
self.target.lock_write()
218
206
self.target.start_write_group()
220
import_git_objects(self.target, mapping,
221
iter(self.source.fetch_objects(determine_wants, graph_walker,
208
objects_iter = self.source.fetch_objects(determine_wants,
210
target_git_object_retriever.__getitem__,
212
import_git_objects(self.target, mapping, objects_iter,
213
target_git_object_retriever, pb)
224
215
self.target.commit_write_group()
229
220
create_pb.finished()
222
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
223
mapping=None, fetch_spec=None):
224
self.fetch_refs(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts,
225
mapping=mapping, fetch_spec=fetch_spec)
227
def fetch_refs(self, revision_id=None, pb=None, find_ghosts=False,
228
mapping=None, fetch_spec=None):
230
mapping = self.source.get_mapping()
231
if revision_id is not None:
232
interesting_heads = [revision_id]
233
elif fetch_spec is not None:
234
interesting_heads = fetch_spec.heads
236
interesting_heads = None
238
def determine_wants(refs):
240
if interesting_heads is None:
241
ret = [sha for (ref, sha) in refs.iteritems() if not ref.endswith("^{}")]
243
ret = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in interesting_heads]
244
return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
245
self.fetch_objects(determine_wants, mapping, pb)
232
249
def is_compatible(source, target):
233
250
"""Be compatible with GitRepository."""
234
251
# FIXME: Also check target uses VersionedFile
235
252
return (isinstance(source, GitRepository) and
236
target.supports_rich_root())
253
target.supports_rich_root() and
254
not isinstance(target, GitRepository))
257
class InterGitRepository(InterRepository):
259
_matching_repo_format = GitFormat()
262
def _get_repo_format_to_test():
265
def copy_content(self, revision_id=None, pb=None):
266
"""See InterRepository.copy_content."""
267
self.fetch(revision_id, pb, find_ghosts=False)
269
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
270
mapping=None, fetch_spec=None):
272
mapping = self.source.get_mapping()
274
info("git: %s", text)
276
if revision_id is not None:
277
args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
278
elif fetch_spec is not None:
279
args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in fetch_spec.heads]
280
if fetch_spec is None and revision_id is None:
281
determine_wants = r.object_store.determine_wants_all
283
determine_wants = lambda x: [y for y in args if not y in r.object_store]
285
graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)
286
f, commit = r.object_store.add_pack()
288
self.source._git.fetch_pack(path, determine_wants, graphwalker, f.write, progress)
296
def is_compatible(source, target):
297
"""Be compatible with GitRepository."""
298
return (isinstance(source, GitRepository) and
299
isinstance(target, GitRepository))