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, NoSuchRevision
19
from bzrlib.inventory import Inventory
20
from bzrlib.repository import InterRepository
21
from bzrlib.trace import info
17
from cStringIO import StringIO
19
from dulwich.client import (
20
SimpleFetchGraphWalker,
22
from dulwich.objects import (
32
from bzrlib.errors import (
36
from bzrlib.inventory import (
39
from bzrlib.repository import (
22
42
from bzrlib.tsort import topo_sort
44
from bzrlib.plugins.git.converter import (
24
47
from bzrlib.plugins.git.repository import (
29
from bzrlib.plugins.git.converter import GitObjectConverter
30
from bzrlib.plugins.git.remote import RemoteGitRepository
33
from dulwich.client import SimpleFetchGraphWalker
34
from dulwich.objects import Commit
36
from cStringIO import StringIO
52
from bzrlib.plugins.git.remote import (
39
57
class BzrFetchGraphWalker(object):
78
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, gitmap, executable):
96
def import_git_blob(texts, mapping, path, blob, inv, parent_invs, shagitmap,
79
98
"""Import a git blob object into a bzr repository.
81
:param repo: bzr repository
100
:param texts: VersionedFiles to add to
82
101
:param path: Path in the tree
83
102
:param blob: A git blob
103
:return: Inventory entry
85
105
file_id = mapping.generate_file_id(path)
86
text_revision = inv.revision_id
87
repo.texts.add_lines((file_id, text_revision),
88
[(file_id, p[file_id].revision) for p in parent_invs if file_id in p],
89
osutils.split_lines(blob.data))
90
106
ie = inv.add_path(path, "file", file_id)
91
ie.revision = text_revision
92
107
ie.text_size = len(blob.data)
93
108
ie.text_sha1 = osutils.sha_string(blob.data)
94
109
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):
110
# See if this is the same revision as one of the parents unchanged
112
for pinv in parent_invs:
113
if not file_id in pinv:
115
if pinv[file_id].text_sha1 == ie.text_sha1:
116
ie.revision = pinv[file_id].revision
118
parent_keys.append((file_id, pinv[file_id].revision))
119
ie.revision = inv.revision_id
120
assert file_id is not None
121
assert ie.revision is not None
122
texts.add_lines((file_id, ie.revision), parent_keys,
123
osutils.split_lines(blob.data))
124
shagitmap.add_entry(blob.sha().hexdigest(), "blob",
125
(ie.file_id, ie.revision))
129
def import_git_tree(texts, mapping, path, tree, inv, parent_invs, shagitmap,
100
131
"""Import a git tree object into a bzr repository.
102
:param repo: A Bzr repository object
133
:param texts: VersionedFiles object to add to
103
134
:param path: Path in the tree
104
135
:param tree: A git tree object
105
136
:param inv: Inventory object
107
138
file_id = mapping.generate_file_id(path)
108
text_revision = inv.revision_id
109
repo.texts.add_lines((file_id, text_revision),
110
[(file_id, p[file_id].revision) for p in parent_invs if file_id in p],
112
139
ie = inv.add_path(path, "directory", file_id)
113
ie.revision = text_revision
114
gitmap._idmap.add_entry(tree.sha().hexdigest(), "tree", (file_id, text_revision))
141
for pinv in parent_invs:
142
if not file_id in pinv:
145
if shagitmap.lookup_tree(path, pinv.revision_id) == tree.id:
146
ie.revision = pinv[file_id].revision
150
parent_keys.append((file_id, pinv[file_id].revision))
115
151
for mode, name, hexsha in tree.entries():
116
152
entry_kind = (mode & 0700000) / 0100000
117
153
basename = name.decode("utf-8")
119
155
child_path = name
121
157
child_path = urlutils.join(path, name)
158
obj = lookup_object(hexsha)
122
159
if entry_kind == 0:
123
tree = lookup_object(hexsha)
124
import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, gitmap, lookup_object)
160
import_git_tree(texts, mapping, child_path, obj, inv, parent_invs,
161
shagitmap, lookup_object)
125
162
elif entry_kind == 1:
126
blob = lookup_object(hexsha)
127
163
fs_mode = mode & 0777
128
import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, gitmap, bool(fs_mode & 0111))
164
import_git_blob(texts, mapping, child_path, obj, inv, parent_invs,
165
shagitmap, bool(fs_mode & 0111))
130
167
raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
168
ie.revision = inv.revision_id
169
texts.add_lines((file_id, ie.revision), parent_keys, [])
170
shagitmap.add_entry(tree.id, "tree", (file_id, ie.revision))
133
174
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever,
149
190
root_trees[rev.revision_id] = object_iter[o.tree]
150
191
revisions[rev.revision_id] = rev
151
192
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))
193
target_git_object_retriever._idmap.add_entry(o.sha().hexdigest(),
194
"commit", (rev.revision_id, o._tree))
153
195
# Order the revisions
154
196
# Create the inventory objects
155
197
for i, revid in enumerate(topo_sort(graph)):
167
209
return object_iter[sha]
168
210
return target_git_object_retriever[sha]
169
211
parent_invs = [repo.get_inventory(r) for r in rev.parent_ids]
170
import_git_tree(repo, mapping, "", root_tree, inv, parent_invs,
171
target_git_object_retriever, lookup_object)
212
import_git_tree(repo.texts, mapping, "", root_tree, inv, parent_invs,
213
target_git_object_retriever._idmap, lookup_object)
172
214
repo.add_revision(rev.revision_id, rev, inv)
215
target_git_object_retriever._idmap.commit()
175
218
class InterGitNonGitRepository(InterRepository):
213
256
create_pb.finished()
215
258
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
259
mapping=None, fetch_spec=None):
260
self.fetch_refs(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts,
261
mapping=mapping, fetch_spec=fetch_spec)
263
def fetch_refs(self, revision_id=None, pb=None, find_ghosts=False,
264
mapping=None, fetch_spec=None):
217
265
if mapping is None:
218
266
mapping = self.source.get_mapping()
219
def determine_wants(heads):
220
if revision_id is None:
267
if revision_id is not None:
268
interesting_heads = [revision_id]
269
elif fetch_spec is not None:
270
interesting_heads = fetch_spec.heads
272
interesting_heads = None
274
def determine_wants(refs):
276
if interesting_heads is None:
277
ret = [sha for (ref, sha) in refs.iteritems() if not ref.endswith("^{}")]
223
ret = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
279
ret = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in interesting_heads]
224
280
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)
281
self.fetch_objects(determine_wants, mapping, pb)
228
285
def is_compatible(source, target):
246
303
self.fetch(revision_id, pb, find_ghosts=False)
248
305
def fetch(self, revision_id=None, pb=None, find_ghosts=False,
306
mapping=None, fetch_spec=None):
250
307
if mapping is None:
251
308
mapping = self.source.get_mapping()
252
309
def progress(text):
253
info("git: %s", text)
310
trace.info("git: %s", text)
254
311
r = self.target._git
255
if revision_id is None:
256
determine_wants = lambda x: [y for y in x.values() if not y in r.object_store]
312
if revision_id is not None:
258
313
args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
314
elif fetch_spec is not None:
315
args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in fetch_spec.heads]
316
if fetch_spec is None and revision_id is None:
317
determine_wants = r.object_store.determine_wants_all
259
319
determine_wants = lambda x: [y for y in args if not y in r.object_store]
261
321
graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)