101
99
"""Lookup a Git sha in the database.
102
100
:param sha: Git object sha
103
101
:return: (type, type_data) with type_data:
104
commit: revid, tree_sha, verifiers
102
revision: revid, tree sha
108
104
raise NotImplementedError(self.lookup_git_sha)
148
144
class ContentCache(object):
149
145
"""Object that can cache Git objects."""
151
def add(self, object):
153
raise NotImplementedError(self.add)
155
def add_multi(self, objects):
156
"""Add multiple objects."""
160
147
def __getitem__(self, sha):
161
148
"""Retrieve an item, by SHA."""
162
149
raise NotImplementedError(self.__getitem__)
165
152
class BzrGitCacheFormat(object):
166
"""Bazaar-Git Cache Format."""
168
154
def get_format_string(self):
169
155
"""Return a single-line unique format string for this cache format."""
174
160
raise NotImplementedError(self.open)
176
162
def initialize(self, transport):
177
"""Create a new instance of this cache format at transport."""
178
163
transport.put_bytes('format', self.get_format_string())
221
206
class CacheUpdater(object):
222
"""Base class for objects that can update a bzr-git cache."""
224
def add_object(self, obj, ie, path):
227
:param obj: Object type ("commit", "blob" or "tree")
228
:param ie: Inventory entry (for blob/tree) or testament_sha in case
230
:param path: Path of the object (optional)
208
def add_object(self, obj, ie):
232
209
raise NotImplementedError(self.add_object)
234
211
def finish(self):
244
221
self._cache_updater_klass = cache_updater_klass
246
223
def get_updater(self, rev):
247
"""Update an object that implements the CacheUpdater interface for
250
224
return self._cache_updater_klass(self, rev)
263
236
self._commit = None
264
237
self._entries = []
266
def add_object(self, obj, ie, path):
239
def add_object(self, obj, ie):
267
240
if obj.type_name == "commit":
268
241
self._commit = obj
269
assert type(ie) is dict
270
type_data = (self.revid, self._commit.tree, ie)
243
type_data = (self.revid, self._commit.tree)
271
244
self.cache.idmap._by_revid[self.revid] = obj.id
272
245
elif obj.type_name in ("blob", "tree"):
273
246
if ie is not None:
277
250
revision = self.revid
278
251
type_data = (ie.file_id, revision)
279
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] = obj.id
252
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] =\
281
255
raise AssertionError
282
256
self.cache.idmap._by_sha[obj.id] = (obj.type_name, type_data)
329
def add_object(self, obj, ie, path):
302
def add_object(self, obj, ie):
330
303
if obj.type_name == "commit":
331
304
self._commit = obj
332
self._testament3_sha1 = ie["testament3-sha1"]
333
assert type(ie) is dict
334
306
elif obj.type_name == "tree":
335
307
if ie is not None:
336
308
self._trees.append((obj.id, ie.file_id, self.revid))
350
322
"replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
353
"replace into commits (sha1, revid, tree_sha, testament3_sha1) values (?, ?, ?, ?)",
354
(self._commit.id, self.revid, self._commit.tree, self._testament3_sha1))
325
"replace into commits (sha1, revid, tree_sha) values (?, ?, ?)",
326
(self._commit.id, self.revid, self._commit.tree))
355
327
return self._commit
406
377
create unique index if not exists trees_sha1 on trees(sha1);
407
378
create unique index if not exists trees_fileid_revid on trees(fileid, revid);
410
self.db.executescript(
411
"ALTER TABLE commits ADD testament3_sha1 TEXT;")
412
except sqlite3.OperationalError:
413
pass # Column already exists.
415
381
def __repr__(self):
416
382
return "%s(%r)" % (self.__class__.__name__, self.path)
418
384
def lookup_commit(self, revid):
419
cursor = self.db.execute("select sha1 from commits where revid = ?",
421
row = cursor.fetchone()
385
row = self.db.execute("select sha1 from commits where revid = ?", (revid,)).fetchone()
422
386
if row is not None:
444
408
:param sha: Git object sha
445
409
:return: (type, type_data) with type_data:
446
commit: revid, tree sha, verifiers
410
revision: revid, tree sha
450
row = self.db.execute("select revid, tree_sha, testament3_sha1 from commits where sha1 = ?", (sha,)).fetchone()
412
row = self.db.execute("select revid, tree_sha from commits where sha1 = ?", (sha,)).fetchone()
451
413
if row is not None:
452
return ("commit", (row[0], row[1], {"testament3-sha1": row[2]}))
414
return ("commit", row)
453
415
row = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,)).fetchone()
454
416
if row is not None:
455
417
return ("blob", row)
480
441
self._commit = None
481
442
self._entries = []
483
def add_object(self, obj, ie, path):
444
def add_object(self, obj, ie):
484
445
sha = obj.sha().digest()
485
446
if obj.type_name == "commit":
486
447
self.db["commit\0" + self.revid] = "\0".join((sha, obj.tree))
487
assert type(ie) is dict, "was %r" % ie
488
type_data = (self.revid, obj.tree, ie["testament3-sha1"])
448
type_data = (self.revid, obj.tree)
489
449
self._commit = obj
490
451
elif obj.type_name == "blob":
509
470
TdbBzrGitCache = lambda p: BzrGitCache(TdbGitShaMap(p), None, TdbCacheUpdater)
511
472
class TdbGitCacheFormat(BzrGitCacheFormat):
512
"""Cache format for tdb-based caches."""
514
474
def get_format_string(self):
515
475
return 'bzr-git sha map version 3 using tdb\n'
581
541
def lookup_blob_id(self, fileid, revision):
582
542
return sha_to_hex(self.db["\0".join(("blob", fileid, revision))])
584
544
def lookup_git_sha(self, sha):
585
545
"""Lookup a Git sha in the database.
587
547
:param sha: Git object sha
588
548
:return: (type, type_data) with type_data:
589
commit: revid, tree sha
549
revision: revid, tree sha
593
551
if len(sha) == 40:
594
552
sha = hex_to_sha(sha)
595
553
data = self.db["git\0" + sha].split("\0")
596
if data[0] == "commit":
598
return (data[0], (data[1], data[2], {}))
600
return (data[0], (data[1], data[2], {"testament3-sha1": data[3]}))
602
return (data[0], tuple(data[1:]))
554
return (data[0], (data[1], data[2]))
604
556
def missing_revisions(self, revids):
639
591
return ShaFile._parse_legacy_object(entry.get_bytes_as('fulltext'))
642
class GitObjectStoreContentCache(ContentCache):
644
def __init__(self, store):
647
def add_multi(self, objs):
648
self.store.add_objects(objs)
650
def add(self, obj, path):
651
self.store.add_object(obj)
653
def __getitem__(self, sha):
654
return self.store[sha]
657
594
class IndexCacheUpdater(CacheUpdater):
659
596
def __init__(self, cache, rev):
662
599
self.parent_revids = rev.parent_ids
663
600
self._commit = None
664
601
self._entries = []
665
self._cache_objs = set()
667
def add_object(self, obj, ie, path):
603
def add_object(self, obj, ie):
668
604
if obj.type_name == "commit":
669
605
self._commit = obj
670
assert type(ie) is dict
671
607
self.cache.idmap._add_git_sha(obj.id, "commit",
672
(self.revid, obj.tree, ie))
608
(self.revid, obj.tree))
673
609
self.cache.idmap._add_node(("commit", self.revid, "X"),
674
610
" ".join((obj.id, obj.tree)))
675
self._cache_objs.add((obj, path))
676
611
elif obj.type_name == "blob":
677
612
self.cache.idmap._add_git_sha(obj.id, "blob",
678
613
(ie.file_id, ie.revision))
679
614
self.cache.idmap._add_node(("blob", ie.file_id, ie.revision), obj.id)
680
615
if ie.kind == "symlink":
681
self._cache_objs.add((obj, path))
616
self.cache.content_cache.add(obj)
682
617
elif obj.type_name == "tree":
683
618
self.cache.idmap._add_git_sha(obj.id, "tree",
684
619
(ie.file_id, self.revid))
685
self._cache_objs.add((obj, path))
620
self.cache.content_cache.add(obj)
687
622
raise AssertionError
689
624
def finish(self):
690
self.cache.content_cache.add_multi(self._cache_objs)
691
625
return self._commit
696
630
def __init__(self, transport=None):
697
631
mapper = versionedfile.ConstantMapper("trees")
698
shamap = IndexGitShaMap(transport.clone('index'))
699
#trees_store = knit.make_file_factory(True, mapper)(transport)
700
#content_cache = VersionedFilesContentCache(trees_store)
701
from bzrlib.plugins.git.transportgit import TransportObjectStore
702
store = TransportObjectStore(transport.clone('objects'))
703
content_cache = GitObjectStoreContentCache(store)
704
super(IndexBzrGitCache, self).__init__(shamap, content_cache,
632
trees_store = knit.make_file_factory(True, mapper)(transport)
633
super(IndexBzrGitCache, self).__init__(
634
IndexGitShaMap(transport.clone('index')),
635
VersionedFilesContentCache(trees_store),
705
636
IndexCacheUpdater)
708
639
class IndexGitCacheFormat(BzrGitCacheFormat):
710
641
def get_format_string(self):
711
return 'bzr-git sha map with git object cache version 1\n'
642
return 'bzr-git sha map version 1\n'
713
644
def initialize(self, transport):
714
645
super(IndexGitCacheFormat, self).initialize(transport)
715
646
transport.mkdir('index')
716
transport.mkdir('objects')
717
from bzrlib.plugins.git.transportgit import TransportObjectStore
718
TransportObjectStore.init(transport.clone('objects'))
720
648
def open(self, transport):
721
649
return IndexBzrGitCache(transport)
835
763
def _add_git_sha(self, hexsha, type, type_data):
836
764
if hexsha is not None:
837
765
self._name.update(hexsha)
839
td = (type_data[0], type_data[1], type_data[2]["testament3-sha1"])
842
self._add_node(("git", hexsha, "X"), " ".join((type,) + td))
766
self._add_node(("git", hexsha, "X"),
767
" ".join((type, type_data[0], type_data[1])))
844
769
# This object is not represented in Git - perhaps an empty
851
776
def lookup_git_sha(self, sha):
852
777
if len(sha) == 20:
853
778
sha = sha_to_hex(sha)
854
data = self._get_entry(("git", sha, "X")).split(" ", 3)
855
if data[0] == "commit":
856
return ("commit", (data[1], data[2], {"testament3-sha1": data[3]}))
858
return (data[0], tuple(data[1:]))
779
data = self._get_entry(("git", sha, "X")).split(" ", 2)
780
return (data[0], (data[1], data[2]))
860
782
def revids(self):
861
783
"""List the revision ids known."""
883
805
SqliteGitCacheFormat())
884
806
formats.register(IndexGitCacheFormat().get_format_string(),
885
807
IndexGitCacheFormat())
886
# In the future, this will become the default:
887
# formats.register('default', IndexGitCacheFormat())
891
formats.register('default', SqliteGitCacheFormat())
893
formats.register('default', TdbGitCacheFormat())
808
formats.register('default', IndexGitCacheFormat())
897
811
def migrate_ancient_formats(repo_transport):