78
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, gitmap, executable):
118
def import_git_blob(texts, mapping, path, hexsha, base_inv, parent_id,
119
revision_id, parent_invs, shagitmap, lookup_object, executable, symlink):
79
120
"""Import a git blob object into a bzr repository.
81
:param repo: bzr repository
122
:param texts: VersionedFiles to add to
82
123
:param path: Path in the tree
83
124
:param blob: A git blob
125
:return: Inventory delta for this file
85
127
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
ie = inv.add_path(path, "file", file_id)
91
ie.revision = text_revision
92
ie.text_size = len(blob.data)
93
ie.text_sha1 = osutils.sha_string(blob.data)
132
# We just have to hope this is indeed utf-8:
133
ie = cls(file_id, urlutils.basename(path).decode("utf-8"),
94
135
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):
136
# See if this has changed at all
138
base_sha = shagitmap.lookup_blob(file_id, base_inv.revision_id)
142
if (base_sha == hexsha and base_inv[file_id].executable == ie.executable
143
and base_inv[file_id].kind == ie.kind):
144
# If nothing has changed since the base revision, we're done
146
if base_sha == hexsha:
147
ie.text_size = base_inv[file_id].text_size
148
ie.text_sha1 = base_inv[file_id].text_sha1
149
ie.symlink_target = base_inv[file_id].symlink_target
150
ie.revision = base_inv[file_id].revision
152
blob = lookup_object(hexsha)
153
if ie.kind == "symlink":
154
ie.symlink_target = blob.data
158
ie.text_size = len(blob.data)
159
ie.text_sha1 = osutils.sha_string(blob.data)
160
# Check what revision we should store
162
for pinv in parent_invs:
163
if not file_id in pinv:
165
if pinv[file_id].text_sha1 == ie.text_sha1:
166
# found a revision in one of the parents to use
167
ie.revision = pinv[file_id].revision
169
parent_keys.append((file_id, pinv[file_id].revision))
170
if ie.revision is None:
171
# Need to store a new revision
172
ie.revision = revision_id
173
assert file_id is not None
174
assert ie.revision is not None
175
texts.add_lines((file_id, ie.revision), parent_keys,
176
osutils.split_lines(blob.data))
177
if "verify" in debug.debug_flags:
178
assert text_to_blob(blob.data).id == hexsha
179
shagitmap.add_entry(hexsha, "blob", (ie.file_id, ie.revision))
180
if file_id in base_inv:
181
old_path = base_inv.id2path(file_id)
184
return [(old_path, path, file_id, ie)]
187
def import_git_tree(texts, mapping, path, hexsha, base_inv, parent_id,
188
revision_id, parent_invs, shagitmap, lookup_object):
100
189
"""Import a git tree object into a bzr repository.
102
:param repo: A Bzr repository object
191
:param texts: VersionedFiles object to add to
103
192
:param path: Path in the tree
104
193
:param tree: A git tree object
105
:param inv: Inventory object
194
:param base_inv: Base inventory against which to return inventory delta
195
:return: Inventory delta for this subtree
107
198
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
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))
199
# We just have to hope this is indeed utf-8:
200
ie = InventoryDirectory(file_id, urlutils.basename(path.decode("utf-8")),
202
if not file_id in base_inv:
203
# Newly appeared here
204
ie.revision = revision_id
205
texts.add_lines((file_id, ie.revision), [], [])
206
invdelta.append((None, path, file_id, ie))
208
# See if this has changed at all
210
base_sha = shagitmap.lookup_tree(file_id, base_inv.revision_id)
214
if base_sha == hexsha:
215
# If nothing has changed since the base revision, we're done
217
# Remember for next time
218
existing_children = set()
219
if "verify" in debug.debug_flags:
222
shagitmap.add_entry(hexsha, "tree", (file_id, revision_id))
224
tree = lookup_object(hexsha)
115
225
for mode, name, hexsha in tree.entries():
116
entry_kind = (mode & 0700000) / 0100000
117
226
basename = name.decode("utf-8")
121
child_path = urlutils.join(path, name)
123
tree = lookup_object(hexsha)
124
import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, gitmap, lookup_object)
125
elif entry_kind == 1:
126
blob = lookup_object(hexsha)
127
fs_mode = mode & 0777
128
import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, gitmap, bool(fs_mode & 0111))
130
raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
227
existing_children.add(basename)
228
child_path = osutils.pathjoin(path, name)
229
if stat.S_ISDIR(mode):
230
subinvdelta, grandchildmodes = import_git_tree(texts, mapping, child_path, hexsha,
231
base_inv, file_id, revision_id, parent_invs, shagitmap,
233
invdelta.extend(subinvdelta)
234
child_modes.update(grandchildmodes)
236
fs_mode = stat.S_IMODE(mode)
237
symlink = stat.S_ISLNK(mode)
238
subinvdelta = import_git_blob(texts, mapping, child_path, hexsha,
239
base_inv, file_id, revision_id, parent_invs, shagitmap,
240
lookup_object, bool(fs_mode & 0111), symlink)
241
invdelta.extend(subinvdelta)
242
if mode not in (DEFAULT_TREE_MODE, DEFAULT_FILE_MODE, DEFAULT_SYMLINK_MODE, DEFAULT_FILE_MODE|0111):
243
child_modes[child_path] = mode
244
# Remove any children that have disappeared
245
if file_id in base_inv:
246
deletable = [v for k,v in base_inv[file_id].children.iteritems() if k not in existing_children]
249
invdelta.append((base_inv.id2path(ie.file_id), None, ie.file_id, None))
250
if ie.kind == "directory":
251
deletable.extend(ie.children.values())
252
return invdelta, child_modes
133
255
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever,
135
257
"""Import a set of git objects into a bzr repository.
137
259
:param repo: Bazaar repository
269
parent_invs_cache = LRUCache(50)
145
270
# Find and convert commit objects
146
for o in object_iter.iterobjects():
273
pb.update("finding revisions to fetch", len(graph), None)
275
assert isinstance(head, str)
277
o = object_iter[head]
147
280
if isinstance(o, Commit):
148
281
rev = mapping.import_commit(o)
149
root_trees[rev.revision_id] = object_iter[o.tree]
282
if repo.has_revision(rev.revision_id):
284
root_trees[rev.revision_id] = o.tree
150
285
revisions[rev.revision_id] = rev
151
286
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))
287
target_git_object_retriever._idmap.add_entry(o.sha().hexdigest(),
288
"commit", (rev.revision_id, o._tree))
289
heads.extend([p for p in o.parents if p not in checked])
290
elif isinstance(o, Tag):
291
heads.append(o.object[1])
293
trace.warning("Unable to import head object %r" % o)
153
295
# Order the revisions
154
296
# Create the inventory objects
155
297
for i, revid in enumerate(topo_sort(graph)):
156
298
if pb is not None:
157
299
pb.update("fetching revisions", i, len(graph))
158
root_tree = root_trees[revid]
159
300
rev = revisions[revid]
160
301
# We have to do this here, since we have to walk the tree and
161
# we need to make sure to import the blobs / trees with the riht
302
# we need to make sure to import the blobs / trees with the right
162
303
# path; this may involve adding them more than once.
164
inv.revision_id = rev.revision_id
165
304
def lookup_object(sha):
166
if sha in object_iter:
167
306
return object_iter[sha]
168
return target_git_object_retriever[sha]
169
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)
172
repo.add_revision(rev.revision_id, rev, inv)
308
return target_git_object_retriever[sha]
310
for parent_id in rev.parent_ids:
312
parent_invs.append(parent_invs_cache[parent_id])
314
parent_inv = repo.get_inventory(parent_id)
315
parent_invs.append(parent_inv)
316
parent_invs_cache[parent_id] = parent_inv
317
if parent_invs == []:
318
base_inv = Inventory(root_id=None)
320
base_inv = parent_invs[0]
321
inv_delta, unusual_modes = import_git_tree(repo.texts, mapping, "",
322
root_trees[revid], base_inv, None, revid, parent_invs,
323
target_git_object_retriever._idmap, lookup_object)
324
if unusual_modes != {}:
325
ret = "unusual modes: \n"
326
for item in unusual_modes.iteritems():
327
ret += "\t%s: %o\n" % item
328
raise AssertionError(ret)
330
basis_id = rev.parent_ids[0]
332
basis_id = NULL_REVISION
333
rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id,
334
inv_delta, rev.revision_id, rev.parent_ids)
335
parent_invs_cache[rev.revision_id] = inv
336
repo.add_revision(rev.revision_id, rev)
337
target_git_object_retriever._idmap.commit()
175
340
class InterGitNonGitRepository(InterRepository):
341
"""Base InterRepository that copies revisions from a Git into a non-Git
177
_matching_repo_format = GitFormat()
344
_matching_repo_format = GitRepositoryFormat()
180
347
def _get_repo_format_to_test():
184
351
"""See InterRepository.copy_content."""
185
352
self.fetch(revision_id, pb, find_ghosts=False)
354
def fetch(self, revision_id=None, pb=None, find_ghosts=False, mapping=None,
356
self.fetch_refs(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts,
357
mapping=mapping, fetch_spec=fetch_spec)
359
def fetch_refs(self, revision_id=None, pb=None, find_ghosts=False,
360
mapping=None, fetch_spec=None):
362
mapping = self.source.get_mapping()
363
if revision_id is not None:
364
interesting_heads = [revision_id]
365
elif fetch_spec is not None:
366
interesting_heads = fetch_spec.heads
368
interesting_heads = None
370
def determine_wants(refs):
372
if interesting_heads is None:
373
ret = [sha for (ref, sha) in refs.iteritems() if not ref.endswith("^{}")]
375
ret = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in interesting_heads if revid != NULL_REVISION]
376
return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
377
self.fetch_objects(determine_wants, mapping, pb)
382
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
383
"""InterRepository that copies revisions from a remote Git into a non-Git
187
386
def fetch_objects(self, determine_wants, mapping, pb=None):
188
387
def progress(text):
189
388
pb.update("git: %s" % text.rstrip("\r\n"), 0, 0)
193
392
create_pb = pb = ui.ui_factory.nested_progress_bar()
194
target_git_object_retriever = GitObjectConverter(self.target, mapping)
393
target_git_object_retriever = BazaarObjectStore(self.target, mapping)
396
def record_determine_wants(heads):
397
wants = determine_wants(heads)
398
recorded_wants.extend(wants)
197
402
self.target.lock_write()
199
404
self.target.start_write_group()
201
objects_iter = self.source.fetch_objects(determine_wants,
406
objects_iter = self.source.fetch_objects(
407
record_determine_wants,
203
target_git_object_retriever.__getitem__,
409
target_git_object_retriever.get_raw,
205
411
import_git_objects(self.target, mapping, objects_iter,
206
target_git_object_retriever, pb)
208
self.target.commit_write_group()
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
def is_compatible(source, target):
229
"""Be compatible with GitRepository."""
230
# FIXME: Also check target uses VersionedFile
231
return (isinstance(source, GitRepository) and
412
target_git_object_retriever, recorded_wants, pb)
414
self.target.commit_write_group()
422
def is_compatible(source, target):
423
"""Be compatible with GitRepository."""
424
# FIXME: Also check target uses VersionedFile
425
return (isinstance(source, RemoteGitRepository) and
426
target.supports_rich_root() and
427
not isinstance(target, GitRepository))
430
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
431
"""InterRepository that copies revisions from a remote Git into a non-Git
434
def fetch_objects(self, determine_wants, mapping, pb=None):
435
wants = determine_wants(self.source._git.get_refs())
438
create_pb = pb = ui.ui_factory.nested_progress_bar()
439
target_git_object_retriever = BazaarObjectStore(self.target, mapping)
441
self.target.lock_write()
443
self.target.start_write_group()
445
import_git_objects(self.target, mapping,
446
self.source._git.object_store,
447
target_git_object_retriever, wants, pb)
449
self.target.commit_write_group()
457
def is_compatible(source, target):
458
"""Be compatible with GitRepository."""
459
# FIXME: Also check target uses VersionedFile
460
return (isinstance(source, LocalGitRepository) and
232
461
target.supports_rich_root() and
233
462
not isinstance(target, GitRepository))
236
465
class InterGitRepository(InterRepository):
466
"""InterRepository that copies between Git repositories."""
238
_matching_repo_format = GitFormat()
468
_matching_repo_format = GitRepositoryFormat()
241
471
def _get_repo_format_to_test():