94
107
if base_hexsha == hexsha and base_mode == mode:
95
108
# If nothing has changed since the base revision, we're done
97
file_id = lookup_file_id(decoded_path)
110
file_id = lookup_file_id(path)
98
111
if stat.S_ISLNK(mode):
99
112
cls = InventoryLink
101
114
cls = InventoryFile
102
ie = cls(file_id, decode_git_path(name), parent_id)
115
ie = cls(file_id, name.decode("utf-8"), parent_id)
103
116
if ie.kind == "file":
104
117
ie.executable = mode_is_executable(mode)
105
118
if base_hexsha == hexsha and mode_kind(base_mode) == mode_kind(mode):
106
base_exec = base_bzr_tree.is_executable(decoded_path)
119
base_exec = base_bzr_tree.is_executable(path)
107
120
if ie.kind == "symlink":
108
ie.symlink_target = base_bzr_tree.get_symlink_target(decoded_path)
121
ie.symlink_target = base_bzr_tree.get_symlink_target(path)
110
ie.text_size = base_bzr_tree.get_file_size(decoded_path)
111
ie.text_sha1 = base_bzr_tree.get_file_sha1(decoded_path)
123
ie.text_size = base_bzr_tree.get_file_size(path)
124
ie.text_sha1 = base_bzr_tree.get_file_sha1(path)
112
125
if ie.kind == "symlink" or ie.executable == base_exec:
113
ie.revision = base_bzr_tree.get_file_revision(decoded_path)
126
ie.revision = base_bzr_tree.get_file_revision(path)
115
128
blob = lookup_object(hexsha)
117
130
blob = lookup_object(hexsha)
118
131
if ie.kind == "symlink":
119
132
ie.revision = None
120
ie.symlink_target = decode_git_path(blob.data)
133
ie.symlink_target = blob.data.decode("utf-8")
122
135
ie.text_size = sum(map(len, blob.chunked))
123
136
ie.text_sha1 = osutils.sha_strings(blob.chunked)
124
137
# Check what revision we should store
126
139
for ptree in parent_bzr_trees:
127
intertree = InterTree.get(ptree, base_bzr_tree)
129
ppath = intertree.find_source_paths(decoded_path, recurse='none')
130
except errors.NoSuchFile:
134
pkind = ptree.kind(ppath)
141
ppath = ptree.id2path(file_id)
142
except errors.NoSuchId:
144
pkind = ptree.kind(ppath, file_id)
135
145
if (pkind == ie.kind and
136
((pkind == "symlink" and ptree.get_symlink_target(ppath) == ie.symlink_target) or
137
(pkind == "file" and ptree.get_file_sha1(ppath) == ie.text_sha1 and
138
ptree.is_executable(ppath) == ie.executable))):
146
((pkind == "symlink" and ptree.get_symlink_target(ppath, file_id) == ie.symlink_target) or
147
(pkind == "file" and ptree.get_file_sha1(ppath, file_id) == ie.text_sha1 and
148
ptree.is_executable(ppath, file_id) == ie.executable))):
139
149
# found a revision in one of the parents to use
140
ie.revision = ptree.get_file_revision(ppath)
150
ie.revision = ptree.get_file_revision(ppath, file_id)
142
parent_key = (file_id, ptree.get_file_revision(ppath))
143
if parent_key not in parent_keys:
152
parent_key = (file_id, ptree.get_file_revision(ppath, file_id))
153
if not parent_key in parent_keys:
144
154
parent_keys.append(parent_key)
145
155
if ie.revision is None:
146
156
# Need to store a new revision
153
163
chunks = blob.chunked
154
164
texts.insert_record_stream([
155
165
ChunkedContentFactory((file_id, ie.revision),
156
tuple(parent_keys), ie.text_sha1, chunks)])
166
tuple(parent_keys), ie.text_sha1, chunks)])
158
168
if base_hexsha is not None:
159
old_path = decoded_path # Renames are not supported yet
169
old_path = path.decode("utf-8") # Renames are not supported yet
160
170
if stat.S_ISDIR(base_mode):
161
invdelta.extend(remove_disappeared_children(
162
base_bzr_tree, old_path, lookup_object(base_hexsha), [],
171
invdelta.extend(remove_disappeared_children(base_bzr_tree, old_path,
172
lookup_object(base_hexsha), [], lookup_object))
166
invdelta.append((old_path, decoded_path, file_id, ie))
175
new_path = path.decode("utf-8")
176
invdelta.append((old_path, new_path, file_id, ie))
167
177
if base_hexsha != hexsha:
168
178
store_updater.add_object(blob, (ie.file_id, ie.revision), path)
178
188
def import_git_submodule(texts, mapping, path, name, hexshas,
179
base_bzr_tree, parent_id, revision_id,
180
parent_bzr_trees, lookup_object,
181
modes, store_updater, lookup_file_id):
189
base_bzr_tree, parent_id, revision_id, parent_bzr_trees, lookup_object,
190
modes, store_updater, lookup_file_id):
182
191
"""Import a git submodule."""
183
192
(base_hexsha, hexsha) = hexshas
184
193
(base_mode, mode) = modes
185
194
if base_hexsha == hexsha and base_mode == mode:
187
path = decode_git_path(path)
188
196
file_id = lookup_file_id(path)
190
ie = TreeReference(file_id, decode_git_path(name), parent_id)
198
ie = TreeReference(file_id, name.decode("utf-8"), parent_id)
191
199
ie.revision = revision_id
192
200
if base_hexsha is not None:
193
old_path = path # Renames are not supported yet
201
old_path = path.decode("utf-8") # Renames are not supported yet
194
202
if stat.S_ISDIR(base_mode):
195
invdelta.extend(remove_disappeared_children(
196
base_bzr_tree, old_path, lookup_object(base_hexsha), [],
203
invdelta.extend(remove_disappeared_children(base_bzr_tree, old_path,
204
lookup_object(base_hexsha), [], lookup_object))
200
207
ie.reference_revision = mapping.revision_id_foreign_to_bzr(hexsha)
216
223
:param lookup_object: Lookup a git object by its SHA1
217
224
:return: Inventory delta, as list
219
if not isinstance(path, str):
226
if type(path) is not unicode:
220
227
raise TypeError(path)
222
229
for name, mode, hexsha in base_tree.iteritems():
223
230
if name in existing_children:
225
c_path = posixpath.join(path, decode_git_path(name))
232
c_path = posixpath.join(path, name.decode("utf-8"))
226
233
file_id = base_bzr_tree.path2id(c_path)
227
234
if file_id is None:
228
235
raise TypeError(file_id)
229
236
ret.append((c_path, None, file_id, None))
230
237
if stat.S_ISDIR(mode):
231
238
ret.extend(remove_disappeared_children(
232
base_bzr_tree, c_path, lookup_object(hexsha), [],
239
base_bzr_tree, c_path, lookup_object(hexsha), [], lookup_object))
237
243
def import_git_tree(texts, mapping, path, name, hexshas,
238
base_bzr_tree, parent_id, revision_id, parent_bzr_trees,
239
lookup_object, modes, store_updater,
240
lookup_file_id, allow_submodules=False):
244
base_bzr_tree, parent_id, revision_id, parent_bzr_trees,
245
lookup_object, modes, store_updater,
246
lookup_file_id, allow_submodules=False):
241
247
"""Import a git tree object into a bzr repository.
243
249
:param texts: VersionedFiles object to add to
244
250
:param path: Path in the tree (str)
245
251
:param name: Name of the tree (str)
246
252
:param tree: A git tree object
247
:param base_bzr_tree: Base inventory against which to return inventory
253
:param base_bzr_tree: Base inventory against which to return inventory delta
249
254
:return: Inventory delta for this subtree
251
256
(base_hexsha, hexsha) = hexshas
252
257
(base_mode, mode) = modes
253
if not isinstance(path, bytes):
258
if type(path) is not str:
254
259
raise TypeError(path)
255
if not isinstance(name, bytes):
260
if type(name) is not str:
256
261
raise TypeError(name)
257
262
if base_hexsha == hexsha and base_mode == mode:
258
263
# If nothing has changed since the base revision, we're done
261
file_id = lookup_file_id(osutils.safe_unicode(path))
262
ie = InventoryDirectory(file_id, decode_git_path(name), parent_id)
266
file_id = lookup_file_id(path)
267
# We just have to hope this is indeed utf-8:
268
ie = InventoryDirectory(file_id, name.decode("utf-8"), parent_id)
263
269
tree = lookup_object(hexsha)
264
270
if base_hexsha is None:
266
old_path = None # Newly appeared here
272
old_path = None # Newly appeared here
268
274
base_tree = lookup_object(base_hexsha)
269
old_path = decode_git_path(path) # Renames aren't supported yet
270
new_path = decode_git_path(path)
275
old_path = path.decode("utf-8") # Renames aren't supported yet
276
new_path = path.decode("utf-8")
271
277
if base_tree is None or type(base_tree) is not Tree:
272
278
ie.revision = revision_id
273
279
invdelta.append((old_path, new_path, ie.file_id, ie))
289
295
child_base_hexsha = None
290
296
child_base_mode = 0
291
297
if stat.S_ISDIR(child_mode):
292
subinvdelta, grandchildmodes = import_git_tree(
293
texts, mapping, child_path, name,
294
(child_base_hexsha, child_hexsha), base_bzr_tree, file_id,
295
revision_id, parent_bzr_trees, lookup_object,
296
(child_base_mode, child_mode), store_updater, lookup_file_id,
297
allow_submodules=allow_submodules)
298
elif S_ISGITLINK(child_mode): # submodule
298
subinvdelta, grandchildmodes = import_git_tree(texts, mapping,
299
child_path, name, (child_base_hexsha, child_hexsha),
300
base_bzr_tree, file_id, revision_id, parent_bzr_trees,
301
lookup_object, (child_base_mode, child_mode), store_updater,
302
lookup_file_id, allow_submodules=allow_submodules)
303
elif S_ISGITLINK(child_mode): # submodule
299
304
if not allow_submodules:
300
305
raise SubmodulesRequireSubtrees()
301
subinvdelta, grandchildmodes = import_git_submodule(
302
texts, mapping, child_path, name,
303
(child_base_hexsha, child_hexsha),
306
subinvdelta, grandchildmodes = import_git_submodule(texts, mapping,
307
child_path, name, (child_base_hexsha, child_hexsha),
304
308
base_bzr_tree, file_id, revision_id, parent_bzr_trees,
305
309
lookup_object, (child_base_mode, child_mode), store_updater,
308
312
if not mapping.is_special_file(name):
309
subinvdelta = import_git_blob(
310
texts, mapping, child_path, name,
313
subinvdelta = import_git_blob(texts, mapping, child_path, name,
311
314
(child_base_hexsha, child_hexsha), base_bzr_tree, file_id,
312
315
revision_id, parent_bzr_trees, lookup_object,
313
(child_base_mode, child_mode), store_updater,
316
(child_base_mode, child_mode), store_updater, lookup_file_id)
317
319
grandchildmodes = {}
318
320
child_modes.update(grandchildmodes)
319
321
invdelta.extend(subinvdelta)
320
322
if child_mode not in (stat.S_IFDIR, DEFAULT_FILE_MODE,
321
stat.S_IFLNK, DEFAULT_FILE_MODE | 0o111,
323
stat.S_IFLNK, DEFAULT_FILE_MODE|0o111,
323
325
child_modes[child_path] = child_mode
324
326
# Remove any children that have disappeared
325
327
if base_tree is not None and type(base_tree) is Tree:
326
invdelta.extend(remove_disappeared_children(
327
base_bzr_tree, old_path, base_tree, existing_children,
328
invdelta.extend(remove_disappeared_children(base_bzr_tree, old_path,
329
base_tree, existing_children, lookup_object))
329
330
store_updater.add_object(tree, (file_id, revision_id), path)
330
331
return invdelta, child_modes
333
334
def verify_commit_reconstruction(target_git_object_retriever, lookup_object,
334
o, rev, ret_tree, parent_trees, mapping,
335
unusual_modes, verifiers):
335
o, rev, ret_tree, parent_trees, mapping, unusual_modes, verifiers):
336
336
new_unusual_modes = mapping.export_unusual_file_modes(rev)
337
337
if new_unusual_modes != unusual_modes:
338
338
raise AssertionError("unusual modes don't match: %r != %r" % (
339
339
unusual_modes, new_unusual_modes))
340
340
# Verify that we can reconstruct the commit properly
341
341
rec_o = target_git_object_retriever._reconstruct_commit(rev, o.tree, True,
344
344
raise AssertionError("Reconstructed commit differs: %r != %r" % (
348
for path, obj, ie in _tree_to_objects(
349
ret_tree, parent_trees, target_git_object_retriever._cache.idmap,
350
unusual_modes, mapping.BZR_DUMMY_FILE):
348
for path, obj, ie in _tree_to_objects(ret_tree, parent_trees,
349
target_git_object_retriever._cache.idmap, unusual_modes,
350
mapping.BZR_DUMMY_FILE):
351
351
old_obj_id = tree_lookup_path(lookup_object, o.tree, path)[1]
352
352
new_objs[path] = obj
353
353
if obj.id != old_obj_id:
354
354
diff.append((path, lookup_object(old_obj_id), obj))
355
355
for (path, old_obj, new_obj) in diff:
356
while (old_obj.type_name == "tree"
357
and new_obj.type_name == "tree"
358
and sorted(old_obj) == sorted(new_obj)):
356
while (old_obj.type_name == "tree" and
357
new_obj.type_name == "tree" and
358
sorted(old_obj) == sorted(new_obj)):
359
359
for name in old_obj:
360
360
if old_obj[name][0] != new_obj[name][0]:
361
raise AssertionError(
362
"Modes for %s differ: %o != %o" %
361
raise AssertionError("Modes for %s differ: %o != %o" %
363
362
(path, old_obj[name][0], new_obj[name][0]))
364
363
if old_obj[name][1] != new_obj[name][1]:
365
364
# Found a differing child, delve deeper
404
403
base_tree = lookup_object(o.parents[0]).tree
405
404
base_mode = stat.S_IFDIR
406
405
store_updater = target_git_object_retriever._get_updater(rev)
407
inv_delta, unusual_modes = import_git_tree(
408
repo.texts, mapping, b"", b"", (base_tree, o.tree), base_bzr_tree,
409
None, rev.revision_id, parent_trees, lookup_object,
410
(base_mode, stat.S_IFDIR), store_updater,
411
mapping.generate_file_id,
412
allow_submodules=repo._format.supports_tree_reference)
406
tree_supplement = mapping.get_fileid_map(lookup_object, o.tree)
407
inv_delta, unusual_modes = import_git_tree(repo.texts,
408
mapping, "", "", (base_tree, o.tree), base_bzr_tree,
409
None, rev.revision_id, parent_trees,
410
lookup_object, (base_mode, stat.S_IFDIR), store_updater,
411
tree_supplement.lookup_file_id,
412
allow_submodules=getattr(repo._format, "supports_tree_reference",
413
414
if unusual_modes != {}:
414
415
for path, mode in unusual_modes.iteritems():
415
416
warn_unusual_mode(rev.foreign_revid, path, mode)
420
421
basis_id = NULL_REVISION
421
422
base_bzr_inventory = None
423
base_bzr_inventory = base_bzr_tree.root_inventory
424
rev.inventory_sha1, inv = repo.add_inventory_by_delta(
425
basis_id, inv_delta, rev.revision_id, rev.parent_ids,
425
base_bzr_inventory = base_bzr_tree.root_inventory
426
except AttributeError: # bzr < 2.6
427
base_bzr_inventory = base_bzr_tree.inventory
428
rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id,
429
inv_delta, rev.revision_id, rev.parent_ids,
427
431
ret_tree = InventoryRevisionTree(repo, inv, rev.revision_id)
428
432
# Check verifiers
429
433
if verifiers and roundtrip_revid is not None:
430
434
testament = StrictTestament3(rev, ret_tree)
431
calculated_verifiers = {"testament3-sha1": testament.as_sha1()}
435
calculated_verifiers = { "testament3-sha1": testament.as_sha1() }
432
436
if calculated_verifiers != verifiers:
433
437
trace.mutter("Testament SHA1 %r for %r did not match %r.",
434
438
calculated_verifiers["testament3-sha1"],
435
439
rev.revision_id, verifiers["testament3-sha1"])
436
440
rev.revision_id = original_revid
437
rev.inventory_sha1, inv = repo.add_inventory_by_delta(
438
basis_id, inv_delta, rev.revision_id, rev.parent_ids,
441
rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id,
442
inv_delta, rev.revision_id, rev.parent_ids, base_bzr_tree)
440
443
ret_tree = InventoryRevisionTree(repo, inv, rev.revision_id)
442
445
calculated_verifiers = {}
445
448
trees_cache.add(ret_tree)
446
449
repo.add_revision(rev.revision_id, rev)
447
450
if "verify" in debug.debug_flags:
448
verify_commit_reconstruction(
449
target_git_object_retriever, lookup_object, o, rev, ret_tree,
450
parent_trees, mapping, unusual_modes, verifiers)
451
verify_commit_reconstruction(target_git_object_retriever,
452
lookup_object, o, rev, ret_tree, parent_trees, mapping,
453
unusual_modes, verifiers)
453
456
def import_git_objects(repo, mapping, object_iter,
454
target_git_object_retriever, heads, pb=None,
457
target_git_object_retriever, heads, pb=None, limit=None):
456
458
"""Import a set of git objects into a bzr repository.
458
460
:param repo: Target Bazaar repository