225
224
elif kind[1] not in (None, "directory"):
226
225
raise AssertionError(kind[1])
227
226
if (path[0] not in (None, "") and
228
parent[0] in tree.inventory and
227
tree.has_id(parent[0]) and
229
228
tree.inventory[parent[0]].kind == "directory"):
231
230
new_trees[posixpath.dirname(path[0])] = parent[0]
233
232
# Fetch contents of the blobs that were changed
234
233
for (path, ie), chunks in tree.iter_files_bytes(
235
234
[(ie.file_id, (path, ie)) for (path, ie) in new_blobs]):
287
286
shamap[ie.file_id] = obj.id
289
class PackTupleIterable(object):
291
def __init__(self, store):
293
self.store.lock_read()
299
def add(self, sha, path):
300
self.objects[sha] = path
303
return len(self.objects)
306
return ((self.store[object_id], path) for (object_id, path) in
307
self.objects.iteritems())
290
310
class BazaarObjectStore(BaseObjectStore):
291
311
"""A Git-style object store backed onto a Bazaar repository."""
293
313
def __init__(self, repository, mapping=None):
294
314
self.repository = repository
315
self._map_updated = False
295
317
if mapping is None:
296
318
self.mapping = default_mapping
298
320
self.mapping = mapping
299
321
self._cache = cache_from_repository(repository)
300
self._content_cache_types = ("tree")
322
self._content_cache_types = ("tree",)
301
323
self.start_write_group = self._cache.idmap.start_write_group
302
324
self.abort_write_group = self._cache.idmap.abort_write_group
303
325
self.commit_write_group = self._cache.idmap.commit_write_group
304
326
self.tree_cache = LRUTreeCache(self.repository)
327
self.unpeel_map = UnpeelMap.from_repository(self.repository)
329
def _missing_revisions(self, revisions):
330
return self._cache.idmap.missing_revisions(revisions)
306
332
def _update_sha_map(self, stop_revision=None):
333
if not self.is_locked():
334
raise AssertionError()
335
if self._map_updated:
337
if (stop_revision is not None and
338
not self._missing_revisions([stop_revision])):
307
340
graph = self.repository.get_graph()
308
341
if stop_revision is None:
309
heads = graph.heads(self.repository.all_revision_ids())
342
all_revids = self.repository.all_revision_ids()
343
missing_revids = self._missing_revisions(all_revids)
311
345
heads = set([stop_revision])
312
missing_revids = self._cache.idmap.missing_revisions(heads)
314
parents = graph.get_parent_map(heads)
316
for p in parents.values():
317
todo.update([x for x in p if x not in missing_revids])
318
heads = self._cache.idmap.missing_revisions(todo)
319
missing_revids.update(heads)
346
missing_revids = self._missing_revisions(heads)
348
parents = graph.get_parent_map(heads)
350
for p in parents.values():
351
todo.update([x for x in p if x not in missing_revids])
352
heads = self._missing_revisions(todo)
353
missing_revids.update(heads)
320
354
if NULL_REVISION in missing_revids:
321
355
missing_revids.remove(NULL_REVISION)
322
356
missing_revids = self.repository.has_revisions(missing_revids)
323
357
if not missing_revids:
358
if stop_revision is None:
359
self._map_updated = True
325
361
self.start_write_group()
342
380
self._update_sha_map()
343
381
return iter(self._cache.idmap.sha1s())
345
def _reconstruct_commit(self, rev, tree_sha, roundtrip, verifiers):
383
def _reconstruct_commit(self, rev, tree_sha, lossy, verifiers):
346
384
"""Reconstruct a Commit object.
348
386
:param rev: Revision object
349
387
:param tree_sha: SHA1 of the root tree object
350
:param roundtrip: Whether or not to roundtrip bzr metadata
388
:param lossy: Whether or not to roundtrip bzr metadata
351
389
:param verifiers: Verifiers for the commits
352
390
:return: Commit object
357
395
except errors.NoSuchRevision:
359
397
return self.mapping.export_commit(rev, tree_sha, parent_lookup,
360
roundtrip, verifiers)
362
def _create_fileid_map_blob(self, inv):
400
def _create_fileid_map_blob(self, tree):
363
401
# FIXME: This can probably be a lot more efficient,
364
402
# not all files necessarily have to be processed.
366
for (path, ie) in inv.iter_entries():
404
for (path, ie) in tree.inventory.iter_entries():
367
405
if self.mapping.generate_file_id(path) != ie.file_id:
368
406
file_ids[path] = ie.file_id
369
407
return self.mapping.export_fileid_map(file_ids)
371
def _revision_to_objects(self, rev, tree, roundtrip):
409
def _revision_to_objects(self, rev, tree, lossy):
372
410
"""Convert a revision to a set of git objects.
374
412
:param rev: Bazaar revision object
375
413
:param tree: Bazaar revision tree
376
:param roundtrip: Whether to roundtrip all Bazaar revision data
414
:param lossy: Whether to not roundtrip all Bazaar revision data
378
416
unusual_modes = extract_unusual_modes(rev)
379
417
present_parents = self.repository.has_revisions(rev.parent_ids)
396
434
base_sha1 = self._lookup_revision_sha1(rev.parent_ids[0])
397
435
root_tree = self[self[base_sha1].tree]
398
436
root_ie = tree.inventory.root
399
if roundtrip and self.mapping.BZR_FILE_IDS_FILE is not None:
400
b = self._create_fileid_map_blob(tree.inventory)
437
if not lossy and self.mapping.BZR_FILE_IDS_FILE is not None:
438
b = self._create_fileid_map_blob(tree)
401
439
if b is not None:
402
root_tree[self.mapping.BZR_FILE_IDS_FILE] = ((stat.S_IFREG | 0644), b.id)
440
root_tree[self.mapping.BZR_FILE_IDS_FILE] = (
441
(stat.S_IFREG | 0644), b.id)
403
442
yield self.mapping.BZR_FILE_IDS_FILE, b, None
404
443
yield "", root_tree, root_ie
406
testament3 = StrictTestament3(rev, tree.inventory)
445
if getattr(StrictTestament3, "from_revision_tree", None):
446
testament3 = StrictTestament3(rev, tree)
448
testament3 = StrictTestament3(rev, tree.inventory)
407
449
verifiers = { "testament3-sha1": testament3.as_sha1() }
410
452
commit_obj = self._reconstruct_commit(rev, root_tree.id,
411
roundtrip=roundtrip, verifiers=verifiers)
453
lossy=lossy, verifiers=verifiers)
413
455
foreign_revid, mapping = mapping_registry.parse_revision_id(
425
467
rev = self.repository.get_revision(revid)
426
468
tree = self.tree_cache.revision_tree(rev.revision_id)
427
469
updater = self._get_updater(rev)
428
for path, obj, ie in self._revision_to_objects(rev, tree,
470
# FIXME JRV 2011-12-15: Shouldn't we try both values for lossy ?
471
for path, obj, ie in self._revision_to_objects(rev, tree, lossy=(not self.mapping.roundtripping)):
430
472
if isinstance(obj, Commit):
431
testament3 = StrictTestament3(rev, tree.inventory)
473
if getattr(StrictTestament3, "from_revision_tree", None):
474
testament3 = StrictTestament3(rev, tree)
476
testament3 = StrictTestament3(rev, tree.inventory)
432
477
ie = { "testament3-sha1": testament3.as_sha1() }
433
478
updater.add_object(obj, ie, path)
434
479
commit_obj = updater.finish()
483
528
[(entry.file_id, entry.revision, None)]).next().id
485
530
raise AssertionError("unknown entry kind '%s'" % entry.kind)
486
tree = directory_to_tree(inv[fileid], get_ie_sha1, unusual_modes,
531
tree = directory_to_tree(bzr_tree.inventory[fileid], get_ie_sha1, unusual_modes,
487
532
self.mapping.BZR_DUMMY_FILE)
488
if (inv.root.file_id == fileid and
533
if (bzr_tree.get_root_id() == fileid and
489
534
self.mapping.BZR_FILE_IDS_FILE is not None):
490
b = self._create_fileid_map_blob(inv)
537
b = self._create_fileid_map_blob(bzr_tree)
491
538
# If this is the root tree, add the file ids
492
tree[self.mapping.BZR_FILE_IDS_FILE] = ((stat.S_IFREG | 0644), b.id)
493
_check_expected_sha(expected_sha, tree)
539
tree[self.mapping.BZR_FILE_IDS_FILE] = (
540
(stat.S_IFREG | 0644), b.id)
542
_check_expected_sha(expected_sha, tree)
496
545
def get_parents(self, sha):
530
574
def __contains__(self, sha):
531
575
# See if sha is in map
533
(type, type_data) = self.lookup_git_sha(sha)
535
return self.repository.has_revision(type_data[0])
537
return self.repository.texts.has_key(type_data)
539
return self.repository.has_revision(type_data[1])
577
for (type, type_data) in self.lookup_git_sha(sha):
579
if self.repository.has_revision(type_data[0]):
582
if self.repository.texts.has_key(type_data):
585
if self.repository.has_revision(type_data[1]):
588
raise AssertionError("Unknown object type '%s'" % type)
541
raise AssertionError("Unknown object type '%s'" % type)
545
def lookup_git_shas(self, shas, update_map=True):
546
from dulwich.protocol import ZERO_SHA
596
self._map_updated = False
597
self.repository.lock_read()
598
return LogicalLockResult(self.unlock)
600
def lock_write(self):
602
self._map_updated = False
603
self.repository.lock_write()
604
return LogicalLockResult(self.unlock)
607
return (self._locked is not None)
611
self._map_updated = False
612
self.repository.unlock()
614
def lookup_git_shas(self, shas):
549
617
if sha == ZERO_SHA:
550
ret[sha] = ("commit", (NULL_REVISION, None, {}))
618
ret[sha] = [("commit", (NULL_REVISION, None, {}))]
553
ret[sha] = self._cache.idmap.lookup_git_sha(sha)
621
ret[sha] = list(self._cache.idmap.lookup_git_sha(sha))
556
# if not, see if there are any unconverted revisions and add
557
# them to the map, search for sha in map again
558
self._update_sha_map()
561
ret[sha] = self._cache.idmap.lookup_git_sha(sha)
623
# if not, see if there are any unconverted revisions and
624
# add them to the map, search for sha in map again
625
self._update_sha_map()
627
ret[sha] = list(self._cache.idmap.lookup_git_sha(sha))
566
def lookup_git_sha(self, sha, update_map=True):
567
return self.lookup_git_shas([sha], update_map=update_map)[sha]
632
def lookup_git_sha(self, sha):
633
return self.lookup_git_shas([sha])[sha]
569
635
def __getitem__(self, sha):
570
636
if self._cache.content_cache is not None:
572
638
return self._cache.content_cache[sha]
575
(type, type_data) = self.lookup_git_sha(sha)
576
# convert object to git object
578
(revid, tree_sha, verifiers) = type_data
580
rev = self.repository.get_revision(revid)
581
except errors.NoSuchRevision:
582
trace.mutter('entry for %s %s in shamap: %r, but not found in '
583
'repository', type, sha, type_data)
585
commit = self._reconstruct_commit(rev, tree_sha, roundtrip=True,
587
_check_expected_sha(sha, commit)
590
(fileid, revision) = type_data
591
return self._reconstruct_blobs([(fileid, revision, sha)]).next()
593
(fileid, revid) = type_data
595
tree = self.tree_cache.revision_tree(revid)
596
rev = self.repository.get_revision(revid)
597
except errors.NoSuchRevision:
598
trace.mutter('entry for %s %s in shamap: %r, but not found in repository', type, sha, type_data)
600
unusual_modes = extract_unusual_modes(rev)
602
return self._reconstruct_tree(fileid, revid, tree.inventory,
603
unusual_modes, expected_sha=sha)
604
except errors.NoSuchRevision:
641
for (kind, type_data) in self.lookup_git_sha(sha):
642
# convert object to git object
644
(revid, tree_sha, verifiers) = type_data
646
rev = self.repository.get_revision(revid)
647
except errors.NoSuchRevision:
648
if revid == NULL_REVISION:
649
raise AssertionError(
650
"should not try to look up NULL_REVISION")
651
trace.mutter('entry for %s %s in shamap: %r, but not '
652
'found in repository', kind, sha, type_data)
654
# FIXME: the type data should say whether conversion was lossless
655
commit = self._reconstruct_commit(rev, tree_sha,
656
lossy=(not self.mapping.roundtripping), verifiers=verifiers)
657
_check_expected_sha(sha, commit)
660
(fileid, revision) = type_data
661
blobs = self._reconstruct_blobs([(fileid, revision, sha)])
664
(fileid, revid) = type_data
666
tree = self.tree_cache.revision_tree(revid)
667
rev = self.repository.get_revision(revid)
668
except errors.NoSuchRevision:
669
trace.mutter('entry for %s %s in shamap: %r, but not found in '
670
'repository', kind, sha, type_data)
672
unusual_modes = extract_unusual_modes(rev)
674
return self._reconstruct_tree(fileid, revid,
675
tree, unusual_modes, expected_sha=sha)
676
except errors.NoSuchRevision:
679
raise AssertionError("Unknown object type '%s'" % kind)
607
raise AssertionError("Unknown object type '%s'" % type)
609
683
def generate_lossy_pack_contents(self, have, want, progress=None,
610
684
get_tagged=None):
621
695
processed = set()
622
696
ret = self.lookup_git_shas(have + want)
623
697
for commit_sha in have:
698
commit_sha = self.unpeel_map.peel_tag(commit_sha, commit_sha)
625
(type, (revid, tree_sha)) = ret[commit_sha]
700
for (type, type_data) in ret[commit_sha]:
701
assert type == "commit"
702
processed.add(type_data[0])
629
assert type == "commit"
704
trace.mutter("unable to find remote ref %s", commit_sha)
632
706
for commit_sha in want:
633
707
if commit_sha in have:
636
(type, (revid, tree_sha)) = ret[commit_sha]
710
for (type, type_data) in ret[commit_sha]:
711
assert type == "commit"
712
pending.add(type_data[0])
640
assert type == "commit"
643
todo = _find_missing_bzr_revids(self.repository.get_parent_map,
645
trace.mutter('sending revisions %r', todo)
716
graph = self.repository.get_graph()
717
todo = _find_missing_bzr_revids(graph, pending, processed)
718
ret = PackTupleIterable(self)
647
719
pb = ui.ui_factory.nested_progress_bar()
649
721
for i, revid in enumerate(todo):
650
722
pb.update("generating git objects", i, len(todo))
651
rev = self.repository.get_revision(revid)
724
rev = self.repository.get_revision(revid)
725
except errors.NoSuchRevision:
652
727
tree = self.tree_cache.revision_tree(revid)
653
for path, obj, ie in self._revision_to_objects(rev, tree,
654
roundtrip=not lossy):
655
ret.append((obj, path))
728
for path, obj, ie in self._revision_to_objects(rev, tree, lossy=lossy):
729
ret.add(obj.id, path)
660
734
def add_thin_pack(self):