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
18
from bzrlib.errors import InvalidRevisionId, NoSuchRevision
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
25
24
from bzrlib.plugins.git.repository import (
26
25
LocalGitRepository,
29
from bzrlib.plugins.git.shamap import GitObjectConverter
30
30
from bzrlib.plugins.git.remote import RemoteGitRepository
33
from dulwich.client import SimpleFetchGraphWalker
32
34
from dulwich.objects import Commit
34
36
from cStringIO import StringIO
37
39
class BzrFetchGraphWalker(object):
40
"""GraphWalker implementation that uses a Bazaar repository."""
39
42
def __init__(self, repository, mapping):
40
43
self.repository = repository
43
46
self.heads = set(repository.all_revision_ids())
50
return iter(self.next, None)
46
52
def ack(self, sha):
47
53
revid = self.mapping.revision_id_foreign_to_bzr(sha)
50
56
def remove(self, revid):
51
57
self.done.add(revid)
58
if revid in self.heads:
53
59
self.heads.remove(revid)
54
60
if revid in self.parents:
55
61
for p in self.parents[revid]:
121
127
raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
124
def import_git_objects(repo, mapping, object_iter, pb=None):
130
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever,
125
132
"""Import a set of git objects into a bzr repository.
127
134
:param repo: Bazaar repository
129
136
:param object_iter: Iterator over Git objects.
131
138
# TODO: a more (memory-)efficient implementation of this
133
for i, o in enumerate(object_iter):
135
pb.update("fetching objects", i)
140
142
# Find and convert commit objects
141
for o in objects.itervalues():
143
for o in object_iter.iterobjects():
142
144
if isinstance(o, Commit):
143
145
rev = mapping.import_commit(o)
144
root_trees[rev.revision_id] = objects[o.tree]
146
root_trees[rev.revision_id] = object_iter[o.tree]
145
147
revisions[rev.revision_id] = rev
146
148
graph.append((rev.revision_id, rev.parent_ids))
147
149
# Order the revisions
157
159
inv = Inventory()
158
160
inv.revision_id = rev.revision_id
159
161
def lookup_object(sha):
162
return reconstruct_git_object(repo, mapping, sha)
162
if sha in object_iter:
163
return object_iter[sha]
164
return target_git_object_retriever(sha)
163
165
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)
166
import_git_tree(repo, mapping, "", root_tree, inv, parent_invs,
165
168
repo.add_revision(rev.revision_id, rev, inv)
168
def reconstruct_git_commit(repo, rev):
169
raise NotImplementedError(self.reconstruct_git_commit)
172
171
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)
172
import pdb; pdb.set_trace()
184
176
raise KeyError("No such object %s" % sha)
187
class InterGitRepository(InterRepository):
179
class InterGitNonGitRepository(InterRepository):
189
181
_matching_repo_format = GitFormat()
196
188
"""See InterRepository.copy_content."""
197
189
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()
191
def fetch_objects(self, determine_wants, mapping, pb=None):
203
192
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))]
193
pb.update("git: %s" % text.rstrip("\r\n"), 0, 0)
211
194
graph_walker = BzrFetchGraphWalker(self.target, mapping)
214
197
create_pb = pb = ui.ui_factory.nested_progress_bar()
198
target_git_object_retriever = GitObjectConverter(self.target, mapping)
216
201
self.target.lock_write()
218
203
self.target.start_write_group()
220
import_git_objects(self.target, mapping,
221
iter(self.source.fetch_objects(determine_wants, graph_walker,
205
objects_iter = self.source.fetch_objects(determine_wants,
207
target_git_object_retriever.__getitem__,
209
import_git_objects(self.target, mapping, objects_iter,
210
target_git_object_retriever, pb)
224
212
self.target.commit_write_group()
229
217
create_pb.finished()
219
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
222
mapping = self.source.get_mapping()
223
def determine_wants(heads):
224
if revision_id is None:
227
ret = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
228
return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
229
return self.fetch_objects(determine_wants, mapping, pb)
232
232
def is_compatible(source, target):
233
233
"""Be compatible with GitRepository."""
234
234
# FIXME: Also check target uses VersionedFile
235
235
return (isinstance(source, GitRepository) and
236
target.supports_rich_root())
236
target.supports_rich_root() and
237
not isinstance(target, GitRepository))
240
class InterGitRepository(InterRepository):
242
_matching_repo_format = GitFormat()
245
def _get_repo_format_to_test():
248
def copy_content(self, revision_id=None, pb=None):
249
"""See InterRepository.copy_content."""
250
self.fetch(revision_id, pb, find_ghosts=False)
252
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
255
mapping = self.source.get_mapping()
257
info("git: %s", text)
259
if revision_id is None:
260
determine_wants = lambda x: [y for y in x.values() if not y in r.object_store]
262
args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
263
determine_wants = lambda x: [y for y in args if not y in r.object_store]
265
graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)
266
f, commit = r.object_store.add_pack()
268
self.source._git.fetch_pack(path, determine_wants, graphwalker, f.write, progress)
276
def is_compatible(source, target):
277
"""Be compatible with GitRepository."""
278
return (isinstance(source, GitRepository) and
279
isinstance(target, GitRepository))