57
def get_remote_cache_transport():
57
def get_remote_cache_transport(repository):
58
58
"""Retrieve the transport to use when accessing (unwritable) remote
61
return get_transport(get_cache_dir())
61
uuid = getattr(repository, "uuid", None)
63
path = get_cache_dir()
65
path = os.path.join(get_cache_dir(), uuid)
66
if not os.path.isdir(path):
68
return get_transport(path)
64
71
def check_pysqlite_version(sqlite3):
100
107
def lookup_git_sha(self, sha):
101
108
"""Lookup a Git sha in the database.
102
109
:param sha: Git object sha
103
:return: (type, type_data) with type_data:
110
:return: list with (type, type_data) tuples with type_data:
104
111
commit: revid, tree_sha, verifiers
105
112
blob: fileid, revid
106
113
tree: fileid, revid
121
128
raise NotImplementedError(self.lookup_tree_id)
130
def lookup_commit(self, revid):
131
"""Retrieve a Git commit SHA by Bazaar revision id.
133
raise NotImplementedError(self.lookup_commit)
123
135
def revids(self):
124
136
"""List the revision ids known."""
125
137
raise NotImplementedError(self.revids)
203
215
:param repository: Repository to open the cache for
204
216
:return: A `BzrGitCache`
218
from bzrlib.transport.local import LocalTransport
206
219
repo_transport = getattr(repository, "_transport", None)
207
if repo_transport is not None:
208
# Even if we don't write to this repo, we should be able
220
if (repo_transport is not None and
221
isinstance(repo_transport, LocalTransport)):
222
# Even if we don't write to this repo, we should be able
209
223
# to update its cache.
210
repo_transport = remove_readonly_transport_decorator(repo_transport)
212
repo_transport.mkdir('git')
213
except bzrlib.errors.FileExists:
215
transport = repo_transport.clone('git')
225
repo_transport = remove_readonly_transport_decorator(repo_transport)
226
except bzrlib.errors.ReadOnlyError:
230
repo_transport.mkdir('git')
231
except bzrlib.errors.FileExists:
233
transport = repo_transport.clone('git')
217
transport = get_remote_cache_transport()
236
if transport is None:
237
transport = get_remote_cache_transport(repository)
218
238
return cls.from_transport(transport)
221
241
class CacheUpdater(object):
222
242
"""Base class for objects that can update a bzr-git cache."""
224
def add_object(self, obj, ie, path):
244
def add_object(self, obj, bzr_key_data, path):
225
245
"""Add an object.
227
247
:param obj: Object type ("commit", "blob" or "tree")
228
:param ie: Inventory entry (for blob/tree) or testament_sha in case
248
:param bzr_key_data: bzr key store data or testament_sha in case
230
250
:param path: Path of the object (optional)
263
283
self._commit = None
264
284
self._entries = []
266
def add_object(self, obj, ie, path):
286
def add_object(self, obj, bzr_key_data, path):
267
287
if obj.type_name == "commit":
268
288
self._commit = obj
269
assert type(ie) is dict
270
type_data = (self.revid, self._commit.tree, ie)
289
assert type(bzr_key_data) is dict
291
type_data = (self.revid, self._commit.tree, bzr_key_data)
271
292
self.cache.idmap._by_revid[self.revid] = obj.id
272
293
elif obj.type_name in ("blob", "tree"):
294
if bzr_key_data is not None:
274
295
if obj.type_name == "blob":
275
revision = ie.revision
296
revision = bzr_key_data[1]
277
298
revision = self.revid
278
type_data = (ie.file_id, revision)
299
key = type_data = (bzr_key_data[0], revision)
279
300
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] = obj.id
281
302
raise AssertionError
282
self.cache.idmap._by_sha[obj.id] = (obj.type_name, type_data)
303
entry = (obj.type_name, type_data)
304
self.cache.idmap._by_sha.setdefault(obj.id, {})[key] = entry
284
306
def finish(self):
285
307
if self._commit is None:
299
321
return self._by_fileid[revision][fileid]
301
323
def lookup_git_sha(self, sha):
302
return self._by_sha[sha]
324
for entry in self._by_sha[sha].itervalues():
304
327
def lookup_tree_id(self, fileid, revision):
305
328
return self._by_fileid[revision][fileid]
308
331
return self._by_revid[revid]
310
333
def revids(self):
311
for key, (type, type_data) in self._by_sha.iteritems():
334
for key, entries in self._by_sha.iteritems():
335
for (type, type_data) in entries.values():
316
340
return self._by_sha.iterkeys()
329
def add_object(self, obj, ie, path):
353
def add_object(self, obj, bzr_key_data, path):
330
354
if obj.type_name == "commit":
331
355
self._commit = obj
332
self._testament3_sha1 = ie["testament3-sha1"]
333
assert type(ie) is dict
356
assert type(bzr_key_data) is dict
357
self._testament3_sha1 = bzr_key_data.get("testament3-sha1")
334
358
elif obj.type_name == "tree":
336
self._trees.append((obj.id, ie.file_id, self.revid))
359
if bzr_key_data is not None:
360
self._trees.append((obj.id, bzr_key_data[0], self.revid))
337
361
elif obj.type_name == "blob":
339
self._blobs.append((obj.id, ie.file_id, ie.revision))
362
if bzr_key_data is not None:
363
self._blobs.append((obj.id, bzr_key_data[0], bzr_key_data[1]))
341
365
raise AssertionError
447
471
tree: fileid, revid
448
472
blob: fileid, revid
450
row = self.db.execute("select revid, tree_sha, testament3_sha1 from commits where sha1 = ?", (sha,)).fetchone()
452
return ("commit", (row[0], row[1], {"testament3-sha1": row[2]}))
453
row = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,)).fetchone()
456
row = self.db.execute("select fileid, revid from trees where sha1 = ?", (sha,)).fetchone()
475
cursor = self.db.execute("select revid, tree_sha, testament3_sha1 from commits where sha1 = ?", (sha,))
476
for row in cursor.fetchall():
478
if row[2] is not None:
479
verifiers = {"testament3-sha1": row[2]}
482
yield ("commit", (row[0], row[1], verifiers))
483
cursor = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,))
484
for row in cursor.fetchall():
487
cursor = self.db.execute("select fileid, revid from trees where sha1 = ?", (sha,))
488
for row in cursor.fetchall():
461
494
def revids(self):
462
495
"""List the revision ids known."""
480
513
self._commit = None
481
514
self._entries = []
483
def add_object(self, obj, ie, path):
516
def add_object(self, obj, bzr_key_data, path):
484
517
sha = obj.sha().digest()
485
518
if obj.type_name == "commit":
486
519
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"])
520
assert type(bzr_key_data) is dict, "was %r" % bzr_key_data
521
type_data = (self.revid, obj.tree)
523
type_data += (bzr_key_data["testament3-sha1"],)
489
526
self._commit = obj
490
527
elif obj.type_name == "blob":
528
if bzr_key_data is None:
493
self.db["\0".join(("blob", ie.file_id, ie.revision))] = sha
494
type_data = (ie.file_id, ie.revision)
530
self.db["\0".join(("blob", bzr_key_data[0], bzr_key_data[1]))] = sha
531
type_data = bzr_key_data
495
532
elif obj.type_name == "tree":
533
if bzr_key_data is None:
498
type_data = (ie.file_id, self.revid)
535
(file_id, ) = bzr_key_data
536
type_data = (file_id, self.revid)
500
538
raise AssertionError
501
self.db["git\0" + sha] = "\0".join((obj.type_name, ) + type_data)
539
entry = "\0".join((obj.type_name, ) + type_data) + "\n"
542
oldval = self.db[key]
546
if oldval[-1] != "\n":
547
self.db[key] = "".join([oldval, "\n", entry])
549
self.db[key] = "".join([oldval, entry])
503
551
def finish(self):
504
552
if self._commit is None:
517
566
def open(self, transport):
519
basepath = transport.local_abspath(".")
568
basepath = transport.local_abspath(".").encode(osutils._fs_enc)
520
569
except bzrlib.errors.NotLocalUrl:
521
570
basepath = get_cache_dir()
571
assert isinstance(basepath, str)
523
573
return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
524
574
except ImportError:
600
assert isinstance(path, str)
550
601
if not mapdbs().has_key(path):
551
602
mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
552
603
os.O_RDWR|os.O_CREAT)
576
627
return "%s(%r)" % (self.__class__.__name__, self.path)
578
629
def lookup_commit(self, revid):
579
return sha_to_hex(self.db["commit\0" + revid][:20])
631
return sha_to_hex(self.db["commit\0" + revid][:20])
633
raise KeyError("No cache entry for %r" % revid)
581
635
def lookup_blob_id(self, fileid, revision):
582
636
return sha_to_hex(self.db["\0".join(("blob", fileid, revision))])
593
647
if len(sha) == 40:
594
648
sha = hex_to_sha(sha)
595
data = self.db["git\0" + sha].split("\0")
596
if data[0] == "commit":
598
return (data[0], (data[1], data[2], {}))
649
value = self.db["git\0" + sha]
650
for data in value.splitlines():
651
data = data.split("\0")
652
if data[0] == "commit":
654
yield (data[0], (data[1], data[2], {}))
656
yield (data[0], (data[1], data[2], {"testament3-sha1": data[3]}))
657
elif data[0] in ("tree", "blob"):
658
yield (data[0], tuple(data[1:]))
600
return (data[0], (data[1], data[2], {"testament3-sha1": data[3]}))
602
return (data[0], tuple(data[1:]))
660
raise AssertionError("unknown type %r" % data[0])
604
662
def missing_revisions(self, revids):
664
722
self._entries = []
665
723
self._cache_objs = set()
667
def add_object(self, obj, ie, path):
725
def add_object(self, obj, bzr_key_data, path):
668
726
if obj.type_name == "commit":
669
727
self._commit = obj
670
assert type(ie) is dict
728
assert type(bzr_key_data) is dict
671
729
self.cache.idmap._add_git_sha(obj.id, "commit",
672
(self.revid, obj.tree, ie))
730
(self.revid, obj.tree, bzr_key_data))
673
731
self.cache.idmap._add_node(("commit", self.revid, "X"),
674
732
" ".join((obj.id, obj.tree)))
675
733
self._cache_objs.add((obj, path))
676
734
elif obj.type_name == "blob":
677
self.cache.idmap._add_git_sha(obj.id, "blob",
678
(ie.file_id, ie.revision))
679
self.cache.idmap._add_node(("blob", ie.file_id, ie.revision), obj.id)
680
if ie.kind == "symlink":
681
self._cache_objs.add((obj, path))
735
self.cache.idmap._add_git_sha(obj.id, "blob", bzr_key_data)
736
self.cache.idmap._add_node(("blob", bzr_key_data[0],
737
bzr_key_data[1]), obj.id)
682
738
elif obj.type_name == "tree":
683
739
self.cache.idmap._add_git_sha(obj.id, "tree",
684
(ie.file_id, self.revid))
740
(bzr_key_data[0], self.revid))
685
741
self._cache_objs.add((obj, path))
687
743
raise AssertionError
822
878
except StopIteration:
825
def _iter_keys_prefix(self, prefix):
881
def _iter_entries_prefix(self, prefix):
826
882
for entry in self._index.iter_entries_prefix([prefix]):
883
yield (entry[1], entry[2])
828
884
if self._builder is not None:
829
885
for entry in self._builder.iter_entries_prefix([prefix]):
886
yield (entry[1], entry[2])
832
888
def lookup_commit(self, revid):
833
889
return self._get_entry(("commit", revid, "X"))[:40]
836
892
if hexsha is not None:
837
893
self._name.update(hexsha)
838
894
if type == "commit":
839
td = (type_data[0], type_data[1], type_data[2]["testament3-sha1"])
895
td = (type_data[0], type_data[1])
897
td += (type_data[2]["testament3-sha1"],)
842
902
self._add_node(("git", hexsha, "X"), " ".join((type,) + td))
851
911
def lookup_git_sha(self, sha):
852
912
if len(sha) == 20:
853
913
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:]))
915
for key, value in self._iter_entries_prefix(("git", sha, None)):
917
data = value.split(" ", 3)
918
if data[0] == "commit":
920
verifiers = {"testament3-sha1": data[3]}
923
yield ("commit", (data[1], data[2], verifiers))
925
yield (data[0], tuple(data[1:]))
860
929
def revids(self):
861
930
"""List the revision ids known."""
862
for key in self._iter_keys_prefix(("commit", None, None)):
931
for key, value in self._iter_entries_prefix(("commit", None, None)):
865
934
def missing_revisions(self, revids):
874
943
"""List the SHA1s."""
875
for key in self._iter_keys_prefix(("git", None, None)):
944
for key, value in self._iter_entries_prefix(("git", None, None)):
897
966
def migrate_ancient_formats(repo_transport):
967
# Migrate older cache formats
968
repo_transport = remove_readonly_transport_decorator(repo_transport)
969
has_sqlite = repo_transport.has("git.db")
970
has_tdb = repo_transport.has("git.tdb")
971
if not has_sqlite or has_tdb:
974
repo_transport.mkdir("git")
975
except bzrlib.errors.FileExists:
898
977
# Prefer migrating git.db over git.tdb, since the latter may not
899
978
# be openable on some platforms.
900
if repo_transport.has("git.db"):
901
980
SqliteGitCacheFormat().initialize(repo_transport.clone("git"))
902
981
repo_transport.rename("git.db", "git/idmap.db")
903
elif repo_transport.has("git.tdb"):
904
983
TdbGitCacheFormat().initialize(repo_transport.clone("git"))
905
984
repo_transport.rename("git.tdb", "git/idmap.tdb")
908
987
def remove_readonly_transport_decorator(transport):
909
988
if transport.is_readonly():
910
return transport._decorated
990
return transport._decorated
991
except AttributeError:
992
raise bzrlib.errors.ReadOnlyError(transport)
923
1005
repo_transport = getattr(repository, "_transport", None)
924
1006
if repo_transport is not None:
925
# Migrate older cache formats
926
repo_transport = remove_readonly_transport_decorator(repo_transport)
928
repo_transport.mkdir("git")
929
except bzrlib.errors.FileExists:
932
1008
migrate_ancient_formats(repo_transport)
1009
except bzrlib.errors.ReadOnlyError:
1010
pass # Not much we can do
933
1011
return BzrGitCacheFormat.from_repository(repository)