1
# Copyright (C) 2009-2018 Jelmer Vernooij <jelmer@jelmer.uk>
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Map from Git sha's to Bazaar objects."""
19
from __future__ import absolute_import
21
from dulwich.objects import (
28
from dulwich.objects import (
39
btree_index as _mod_btree_index,
43
from ..sixish import (
48
from ..transport import (
54
ret = os.path.join(osutils.cache_dir(), "git")
55
if not os.path.isdir(ret):
60
def get_remote_cache_transport(repository):
61
"""Retrieve the transport to use when accessing (unwritable) remote
64
uuid = getattr(repository, "uuid", None)
66
path = get_cache_dir()
68
path = os.path.join(get_cache_dir(), uuid)
69
if not os.path.isdir(path):
71
return get_transport(path)
74
def check_pysqlite_version(sqlite3):
75
"""Check that sqlite library is compatible.
78
if (sqlite3.sqlite_version_info[0] < 3
79
or (sqlite3.sqlite_version_info[0] == 3 and
80
sqlite3.sqlite_version_info[1] < 3)):
81
trace.warning('Needs at least sqlite 3.3.x')
82
raise bzr_errors.BzrError("incompatible sqlite library")
88
check_pysqlite_version(sqlite3)
89
except (ImportError, bzr_errors.BzrError):
90
from pysqlite2 import dbapi2 as sqlite3
91
check_pysqlite_version(sqlite3)
93
trace.warning('Needs at least Python2.5 or Python2.4 with the pysqlite2 '
95
raise bzr_errors.BzrError("missing sqlite library")
98
_mapdbs = threading.local()
102
"""Get a cache for this thread's db connections."""
105
except AttributeError:
110
class GitShaMap(object):
111
"""Git<->Bzr revision id mapping database."""
113
def lookup_git_sha(self, sha):
114
"""Lookup a Git sha in the database.
115
:param sha: Git object sha
116
:return: list with (type, type_data) tuples with type_data:
117
commit: revid, tree_sha, verifiers
121
raise NotImplementedError(self.lookup_git_sha)
123
def lookup_blob_id(self, file_id, revision):
124
"""Retrieve a Git blob SHA by file id.
126
:param file_id: File id of the file/symlink
127
:param revision: revision in which the file was last changed.
129
raise NotImplementedError(self.lookup_blob_id)
131
def lookup_tree_id(self, file_id, revision):
132
"""Retrieve a Git tree SHA by file id.
134
raise NotImplementedError(self.lookup_tree_id)
136
def lookup_commit(self, revid):
137
"""Retrieve a Git commit SHA by Bazaar revision id.
139
raise NotImplementedError(self.lookup_commit)
142
"""List the revision ids known."""
143
raise NotImplementedError(self.revids)
145
def missing_revisions(self, revids):
146
"""Return set of all the revisions that are not present."""
147
present_revids = set(self.revids())
148
if not isinstance(revids, set):
150
return revids - present_revids
153
"""List the SHA1s."""
154
raise NotImplementedError(self.sha1s)
156
def start_write_group(self):
157
"""Start writing changes."""
159
def commit_write_group(self):
160
"""Commit any pending changes."""
162
def abort_write_group(self):
163
"""Abort any pending changes."""
166
class ContentCache(object):
167
"""Object that can cache Git objects."""
169
def add(self, object):
171
raise NotImplementedError(self.add)
173
def add_multi(self, objects):
174
"""Add multiple objects."""
178
def __getitem__(self, sha):
179
"""Retrieve an item, by SHA."""
180
raise NotImplementedError(self.__getitem__)
183
class BzrGitCacheFormat(object):
184
"""Bazaar-Git Cache Format."""
186
def get_format_string(self):
187
"""Return a single-line unique format string for this cache format."""
188
raise NotImplementedError(self.get_format_string)
190
def open(self, transport):
191
"""Open this format on a transport."""
192
raise NotImplementedError(self.open)
194
def initialize(self, transport):
195
"""Create a new instance of this cache format at transport."""
196
transport.put_bytes('format', self.get_format_string())
199
def from_transport(self, transport):
200
"""Open a cache file present on a transport, or initialize one.
202
:param transport: Transport to use
203
:return: A BzrGitCache instance
206
format_name = transport.get_bytes('format')
207
format = formats.get(format_name)
208
except bzr_errors.NoSuchFile:
209
format = formats.get('default')
210
format.initialize(transport)
211
return format.open(transport)
214
def from_repository(cls, repository):
215
"""Open a cache file for a repository.
217
This will use the repository's transport to store the cache file, or
218
use the users global cache directory if the repository has no
219
transport associated with it.
221
:param repository: Repository to open the cache for
222
:return: A `BzrGitCache`
224
from ..transport.local import LocalTransport
225
repo_transport = getattr(repository, "_transport", None)
226
if (repo_transport is not None
227
and isinstance(repo_transport, LocalTransport)):
228
# Even if we don't write to this repo, we should be able
229
# to update its cache.
231
repo_transport = remove_readonly_transport_decorator(
233
except bzr_errors.ReadOnlyError:
237
repo_transport.mkdir('git')
238
except bzr_errors.FileExists:
240
transport = repo_transport.clone('git')
243
if transport is None:
244
transport = get_remote_cache_transport(repository)
245
return cls.from_transport(transport)
248
class CacheUpdater(object):
249
"""Base class for objects that can update a bzr-git cache."""
251
def add_object(self, obj, bzr_key_data, path):
254
:param obj: Object type ("commit", "blob" or "tree")
255
:param bzr_key_data: bzr key store data or testament_sha in case
257
:param path: Path of the object (optional)
259
raise NotImplementedError(self.add_object)
262
raise NotImplementedError(self.finish)
265
class BzrGitCache(object):
266
"""Caching backend."""
268
def __init__(self, idmap, cache_updater_klass):
270
self._cache_updater_klass = cache_updater_klass
272
def get_updater(self, rev):
273
"""Update an object that implements the CacheUpdater interface for
276
return self._cache_updater_klass(self, rev)
279
def DictBzrGitCache():
280
return BzrGitCache(DictGitShaMap(), DictCacheUpdater)
283
class DictCacheUpdater(CacheUpdater):
284
"""Cache updater for dict-based caches."""
286
def __init__(self, cache, rev):
288
self.revid = rev.revision_id
289
self.parent_revids = rev.parent_ids
293
def add_object(self, obj, bzr_key_data, path):
294
if isinstance(obj, tuple):
295
(type_name, hexsha) = obj
297
type_name = obj.type_name.decode('ascii')
299
if not isinstance(hexsha, bytes):
300
raise TypeError(hexsha)
301
if type_name == "commit":
303
if type(bzr_key_data) is not dict:
304
raise TypeError(bzr_key_data)
306
type_data = (self.revid, self._commit.tree, bzr_key_data)
307
self.cache.idmap._by_revid[self.revid] = hexsha
308
elif type_name in ("blob", "tree"):
309
if bzr_key_data is not None:
310
key = type_data = bzr_key_data
311
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[
312
type_data[0]] = hexsha
315
entry = (type_name, type_data)
316
self.cache.idmap._by_sha.setdefault(hexsha, {})[key] = entry
319
if self._commit is None:
320
raise AssertionError("No commit object added")
324
class DictGitShaMap(GitShaMap):
325
"""Git SHA map that uses a dictionary."""
332
def lookup_blob_id(self, fileid, revision):
333
return self._by_fileid[revision][fileid]
335
def lookup_git_sha(self, sha):
336
if not isinstance(sha, bytes):
338
for entry in viewvalues(self._by_sha[sha]):
341
def lookup_tree_id(self, fileid, revision):
342
return self._by_fileid[revision][fileid]
344
def lookup_commit(self, revid):
345
return self._by_revid[revid]
348
for key, entries in viewitems(self._by_sha):
349
for (type, type_data) in viewvalues(entries):
354
return viewkeys(self._by_sha)
357
class SqliteCacheUpdater(CacheUpdater):
359
def __init__(self, cache, rev):
361
self.db = self.cache.idmap.db
362
self.revid = rev.revision_id
367
def add_object(self, obj, bzr_key_data, path):
368
if isinstance(obj, tuple):
369
(type_name, hexsha) = obj
371
type_name = obj.type_name.decode('ascii')
373
if not isinstance(hexsha, bytes):
374
raise TypeError(hexsha)
375
if type_name == "commit":
377
if type(bzr_key_data) is not dict:
378
raise TypeError(bzr_key_data)
379
self._testament3_sha1 = bzr_key_data.get("testament3-sha1")
380
elif type_name == "tree":
381
if bzr_key_data is not None:
382
self._trees.append((hexsha, bzr_key_data[0], bzr_key_data[1]))
383
elif type_name == "blob":
384
if bzr_key_data is not None:
385
self._blobs.append((hexsha, bzr_key_data[0], bzr_key_data[1]))
390
if self._commit is None:
391
raise AssertionError("No commit object added")
393
"replace into trees (sha1, fileid, revid) values (?, ?, ?)",
396
"replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
399
"replace into commits (sha1, revid, tree_sha, testament3_sha1) "
400
"values (?, ?, ?, ?)",
401
(self._commit.id, self.revid, self._commit.tree,
402
self._testament3_sha1))
406
def SqliteBzrGitCache(p):
407
return BzrGitCache(SqliteGitShaMap(p), SqliteCacheUpdater)
410
class SqliteGitCacheFormat(BzrGitCacheFormat):
412
def get_format_string(self):
413
return b'bzr-git sha map version 1 using sqlite\n'
415
def open(self, transport):
417
basepath = transport.local_abspath(".")
418
except bzr_errors.NotLocalUrl:
419
basepath = get_cache_dir()
420
return SqliteBzrGitCache(os.path.join(basepath, "idmap.db"))
423
class SqliteGitShaMap(GitShaMap):
424
"""Bazaar GIT Sha map that uses a sqlite database for storage."""
426
def __init__(self, path=None):
429
self.db = sqlite3.connect(":memory:")
431
if path not in mapdbs():
432
mapdbs()[path] = sqlite3.connect(path)
433
self.db = mapdbs()[path]
434
self.db.text_factory = str
435
self.db.executescript("""
436
create table if not exists commits(
437
sha1 text not null check(length(sha1) == 40),
439
tree_sha text not null check(length(tree_sha) == 40)
441
create index if not exists commit_sha1 on commits(sha1);
442
create unique index if not exists commit_revid on commits(revid);
443
create table if not exists blobs(
444
sha1 text not null check(length(sha1) == 40),
445
fileid text not null,
448
create index if not exists blobs_sha1 on blobs(sha1);
449
create unique index if not exists blobs_fileid_revid on blobs(
451
create table if not exists trees(
452
sha1 text unique not null check(length(sha1) == 40),
453
fileid text not null,
456
create unique index if not exists trees_sha1 on trees(sha1);
457
create unique index if not exists trees_fileid_revid on trees(
461
self.db.executescript(
462
"ALTER TABLE commits ADD testament3_sha1 TEXT;")
463
except sqlite3.OperationalError:
464
pass # Column already exists.
467
return "%s(%r)" % (self.__class__.__name__, self.path)
469
def lookup_commit(self, revid):
470
cursor = self.db.execute("select sha1 from commits where revid = ?",
472
row = cursor.fetchone()
477
def commit_write_group(self):
480
def lookup_blob_id(self, fileid, revision):
481
row = self.db.execute(
482
"select sha1 from blobs where fileid = ? and revid = ?",
483
(fileid, revision)).fetchone()
486
raise KeyError(fileid)
488
def lookup_tree_id(self, fileid, revision):
489
row = self.db.execute(
490
"select sha1 from trees where fileid = ? and revid = ?",
491
(fileid, revision)).fetchone()
494
raise KeyError(fileid)
496
def lookup_git_sha(self, sha):
497
"""Lookup a Git sha in the database.
499
:param sha: Git object sha
500
:return: (type, type_data) with type_data:
501
commit: revid, tree sha, verifiers
506
cursor = self.db.execute(
507
"select revid, tree_sha, testament3_sha1 from commits where "
509
for row in cursor.fetchall():
511
if row[2] is not None:
512
verifiers = {"testament3-sha1": row[2]}
515
yield ("commit", (row[0], row[1], verifiers))
516
cursor = self.db.execute(
517
"select fileid, revid from blobs where sha1 = ?", (sha,))
518
for row in cursor.fetchall():
521
cursor = self.db.execute(
522
"select fileid, revid from trees where sha1 = ?", (sha,))
523
for row in cursor.fetchall():
530
"""List the revision ids known."""
531
return (row for (row,) in self.db.execute("select revid from commits"))
534
"""List the SHA1s."""
535
for table in ("blobs", "commits", "trees"):
536
for (sha,) in self.db.execute("select sha1 from %s" % table):
537
yield sha.encode('ascii')
540
class TdbCacheUpdater(CacheUpdater):
541
"""Cache updater for tdb-based caches."""
543
def __init__(self, cache, rev):
545
self.db = cache.idmap.db
546
self.revid = rev.revision_id
547
self.parent_revids = rev.parent_ids
551
def add_object(self, obj, bzr_key_data, path):
552
if isinstance(obj, tuple):
553
(type_name, hexsha) = obj
554
sha = hex_to_sha(hexsha)
556
type_name = obj.type_name.decode('ascii')
557
sha = obj.sha().digest()
558
if type_name == "commit":
559
self.db[b"commit\0" + self.revid] = b"\0".join((sha, obj.tree))
560
if type(bzr_key_data) is not dict:
561
raise TypeError(bzr_key_data)
562
type_data = (self.revid, obj.tree)
564
type_data += (bzr_key_data["testament3-sha1"],)
568
elif type_name == "blob":
569
if bzr_key_data is None:
572
(b"blob", bzr_key_data[0], bzr_key_data[1]))] = sha
573
type_data = bzr_key_data
574
elif type_name == "tree":
575
if bzr_key_data is None:
577
type_data = bzr_key_data
580
entry = b"\0".join((type_name.encode('ascii'), ) + type_data) + b"\n"
583
oldval = self.db[key]
587
if not oldval.endswith(b'\n'):
588
self.db[key] = b"".join([oldval, b"\n", entry])
590
self.db[key] = b"".join([oldval, entry])
593
if self._commit is None:
594
raise AssertionError("No commit object added")
598
def TdbBzrGitCache(p):
599
return BzrGitCache(TdbGitShaMap(p), TdbCacheUpdater)
602
class TdbGitCacheFormat(BzrGitCacheFormat):
603
"""Cache format for tdb-based caches."""
605
def get_format_string(self):
606
return b'bzr-git sha map version 3 using tdb\n'
608
def open(self, transport):
610
basepath = transport.local_abspath(".")
611
except bzr_errors.NotLocalUrl:
612
basepath = get_cache_dir()
614
return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
617
"Unable to open existing bzr-git cache because 'tdb' is not "
621
class TdbGitShaMap(GitShaMap):
622
"""SHA Map that uses a TDB database.
626
"git <sha1>" -> "<type> <type-data1> <type-data2>"
627
"commit revid" -> "<sha1> <tree-id>"
628
"tree fileid revid" -> "<sha1>"
629
"blob fileid revid" -> "<sha1>"
633
TDB_HASH_SIZE = 50000
635
def __init__(self, path=None):
641
if path not in mapdbs():
642
mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
643
os.O_RDWR | os.O_CREAT)
644
self.db = mapdbs()[path]
646
if int(self.db[b"version"]) not in (2, 3):
648
"SHA Map is incompatible (%s -> %d), rebuilding database.",
649
self.db[b"version"], self.TDB_MAP_VERSION)
653
self.db[b"version"] = b'%d' % self.TDB_MAP_VERSION
655
def start_write_group(self):
656
"""Start writing changes."""
657
self.db.transaction_start()
659
def commit_write_group(self):
660
"""Commit any pending changes."""
661
self.db.transaction_commit()
663
def abort_write_group(self):
664
"""Abort any pending changes."""
665
self.db.transaction_cancel()
668
return "%s(%r)" % (self.__class__.__name__, self.path)
670
def lookup_commit(self, revid):
672
return sha_to_hex(self.db[b"commit\0" + revid][:20])
674
raise KeyError("No cache entry for %r" % revid)
676
def lookup_blob_id(self, fileid, revision):
677
return sha_to_hex(self.db[b"\0".join((b"blob", fileid, revision))])
679
def lookup_git_sha(self, sha):
680
"""Lookup a Git sha in the database.
682
:param sha: Git object sha
683
:return: (type, type_data) with type_data:
684
commit: revid, tree sha
689
sha = hex_to_sha(sha)
690
value = self.db[b"git\0" + sha]
691
for data in value.splitlines():
692
data = data.split(b"\0")
693
type_name = data[0].decode('ascii')
694
if type_name == "commit":
696
yield (type_name, (data[1], data[2], {}))
698
yield (type_name, (data[1], data[2],
699
{"testament3-sha1": data[3]}))
700
elif type_name in ("tree", "blob"):
701
yield (type_name, tuple(data[1:]))
703
raise AssertionError("unknown type %r" % type_name)
705
def missing_revisions(self, revids):
708
if self.db.get(b"commit\0" + revid) is None:
714
return self.db.keys()
715
except AttributeError: # python < 3
716
return self.db.iterkeys()
719
"""List the revision ids known."""
720
for key in self._keys():
721
if key.startswith(b"commit\0"):
725
"""List the SHA1s."""
726
for key in self._keys():
727
if key.startswith(b"git\0"):
728
yield sha_to_hex(key[4:])
731
class VersionedFilesContentCache(ContentCache):
733
def __init__(self, vf):
737
self._vf.insert_record_stream(
738
[versionedfile.ChunkedContentFactory(
739
(obj.id,), [], None, obj.as_legacy_object_chunks())])
741
def __getitem__(self, sha):
742
stream = self._vf.get_record_stream([(sha,)], 'unordered', True)
744
if entry.storage_kind == 'absent':
746
return ShaFile._parse_legacy_object(entry.get_bytes_as('fulltext'))
749
class IndexCacheUpdater(CacheUpdater):
751
def __init__(self, cache, rev):
753
self.revid = rev.revision_id
754
self.parent_revids = rev.parent_ids
758
def add_object(self, obj, bzr_key_data, path):
759
if isinstance(obj, tuple):
760
(type_name, hexsha) = obj
762
type_name = obj.type_name.decode('ascii')
764
if type_name == "commit":
766
if type(bzr_key_data) is not dict:
767
raise TypeError(bzr_key_data)
768
self.cache.idmap._add_git_sha(hexsha, b"commit",
769
(self.revid, obj.tree, bzr_key_data))
770
self.cache.idmap._add_node((b"commit", self.revid, b"X"),
771
b" ".join((hexsha, obj.tree)))
772
elif type_name == "blob":
773
self.cache.idmap._add_git_sha(hexsha, b"blob", bzr_key_data)
774
self.cache.idmap._add_node((b"blob", bzr_key_data[0],
775
bzr_key_data[1]), hexsha)
776
elif type_name == "tree":
777
self.cache.idmap._add_git_sha(hexsha, b"tree", bzr_key_data)
785
class IndexBzrGitCache(BzrGitCache):
787
def __init__(self, transport=None):
788
shamap = IndexGitShaMap(transport.clone('index'))
789
super(IndexBzrGitCache, self).__init__(shamap, IndexCacheUpdater)
792
class IndexGitCacheFormat(BzrGitCacheFormat):
794
def get_format_string(self):
795
return b'bzr-git sha map with git object cache version 1\n'
797
def initialize(self, transport):
798
super(IndexGitCacheFormat, self).initialize(transport)
799
transport.mkdir('index')
800
transport.mkdir('objects')
801
from .transportgit import TransportObjectStore
802
TransportObjectStore.init(transport.clone('objects'))
804
def open(self, transport):
805
return IndexBzrGitCache(transport)
808
class IndexGitShaMap(GitShaMap):
809
"""SHA Map that uses the Bazaar APIs to store a cache.
811
BTree Index file with the following contents:
813
("git", <sha1>, "X") -> "<type> <type-data1> <type-data2>"
814
("commit", <revid>, "X") -> "<sha1> <tree-id>"
815
("blob", <fileid>, <revid>) -> <sha1>
819
def __init__(self, transport=None):
821
if transport is None:
822
self._transport = None
823
self._index = _mod_index.InMemoryGraphIndex(0, key_elements=3)
824
self._builder = self._index
827
self._transport = transport
828
self._index = _mod_index.CombinedGraphIndex([])
829
for name in self._transport.list_dir("."):
830
if not name.endswith(".rix"):
832
x = _mod_btree_index.BTreeGraphIndex(
833
self._transport, name, self._transport.stat(name).st_size)
834
self._index.insert_index(0, x)
837
def from_repository(cls, repository):
838
transport = getattr(repository, "_transport", None)
839
if transport is not None:
841
transport.mkdir('git')
842
except bzr_errors.FileExists:
844
return cls(transport.clone('git'))
845
from ..transport import get_transport
846
return cls(get_transport(get_cache_dir()))
849
if self._transport is not None:
850
return "%s(%r)" % (self.__class__.__name__, self._transport.base)
852
return "%s()" % (self.__class__.__name__)
855
if self._builder is not None:
856
raise bzr_errors.BzrError('builder already open')
857
self.start_write_group()
858
self._builder.add_nodes(
859
((key, value) for (_, key, value) in
860
self._index.iter_all_entries()))
862
for name in self._transport.list_dir('.'):
863
if name.endswith('.rix'):
864
to_remove.append(name)
865
self.commit_write_group()
866
del self._index.indices[1:]
867
for name in to_remove:
868
self._transport.rename(name, name + '.old')
870
def start_write_group(self):
871
if self._builder is not None:
872
raise bzr_errors.BzrError('builder already open')
873
self._builder = _mod_btree_index.BTreeBuilder(0, key_elements=3)
874
self._name = osutils.sha()
876
def commit_write_group(self):
877
if self._builder is None:
878
raise bzr_errors.BzrError('builder not open')
879
stream = self._builder.finish()
880
name = self._name.hexdigest() + ".rix"
881
size = self._transport.put_file(name, stream)
882
index = _mod_btree_index.BTreeGraphIndex(self._transport, name, size)
883
self._index.insert_index(0, index)
887
def abort_write_group(self):
888
if self._builder is None:
889
raise bzr_errors.BzrError('builder not open')
893
def _add_node(self, key, value):
897
self._builder.add_node(key, value)
902
def _get_entry(self, key):
903
entries = self._index.iter_entries([key])
905
return next(entries)[2]
906
except StopIteration:
907
if self._builder is None:
909
entries = self._builder.iter_entries([key])
911
return next(entries)[2]
912
except StopIteration:
915
def _iter_entries_prefix(self, prefix):
916
for entry in self._index.iter_entries_prefix([prefix]):
917
yield (entry[1], entry[2])
918
if self._builder is not None:
919
for entry in self._builder.iter_entries_prefix([prefix]):
920
yield (entry[1], entry[2])
922
def lookup_commit(self, revid):
923
return self._get_entry((b"commit", revid, b"X"))[:40]
925
def _add_git_sha(self, hexsha, type, type_data):
926
if hexsha is not None:
927
self._name.update(hexsha)
928
if type == b"commit":
929
td = (type_data[0], type_data[1])
931
td += (type_data[2]["testament3-sha1"],)
936
self._add_node((b"git", hexsha, b"X"), b" ".join((type,) + td))
938
# This object is not represented in Git - perhaps an empty
940
self._name.update(type + b" ".join(type_data))
942
def lookup_blob_id(self, fileid, revision):
943
return self._get_entry((b"blob", fileid, revision))
945
def lookup_git_sha(self, sha):
947
sha = sha_to_hex(sha)
948
value = self._get_entry((b"git", sha, b"X"))
949
data = value.split(b" ", 3)
950
if data[0] == b"commit":
953
verifiers = {"testament3-sha1": data[3]}
958
yield ("commit", (data[1], data[2], verifiers))
960
yield (data[0].decode('ascii'), tuple(data[1:]))
963
"""List the revision ids known."""
964
for key, value in self._iter_entries_prefix((b"commit", None, None)):
967
def missing_revisions(self, revids):
968
"""Return set of all the revisions that are not present."""
969
missing_revids = set(revids)
970
for _, key, value in self._index.iter_entries((
971
(b"commit", revid, b"X") for revid in revids)):
972
missing_revids.remove(key[1])
973
return missing_revids
976
"""List the SHA1s."""
977
for key, value in self._iter_entries_prefix((b"git", None, None)):
981
formats = registry.Registry()
982
formats.register(TdbGitCacheFormat().get_format_string(),
984
formats.register(SqliteGitCacheFormat().get_format_string(),
985
SqliteGitCacheFormat())
986
formats.register(IndexGitCacheFormat().get_format_string(),
987
IndexGitCacheFormat())
988
# In the future, this will become the default:
989
formats.register('default', IndexGitCacheFormat())
992
def migrate_ancient_formats(repo_transport):
993
# Migrate older cache formats
994
repo_transport = remove_readonly_transport_decorator(repo_transport)
995
has_sqlite = repo_transport.has("git.db")
996
has_tdb = repo_transport.has("git.tdb")
997
if not has_sqlite or has_tdb:
1000
repo_transport.mkdir("git")
1001
except bzr_errors.FileExists:
1003
# Prefer migrating git.db over git.tdb, since the latter may not
1004
# be openable on some platforms.
1006
SqliteGitCacheFormat().initialize(repo_transport.clone("git"))
1007
repo_transport.rename("git.db", "git/idmap.db")
1009
TdbGitCacheFormat().initialize(repo_transport.clone("git"))
1010
repo_transport.rename("git.tdb", "git/idmap.tdb")
1013
def remove_readonly_transport_decorator(transport):
1014
if transport.is_readonly():
1016
return transport._decorated
1017
except AttributeError:
1018
raise bzr_errors.ReadOnlyError(transport)
1022
def from_repository(repository):
1023
"""Open a cache file for a repository.
1025
If the repository is remote and there is no transport available from it
1026
this will use a local file in the users cache directory
1027
(typically ~/.cache/bazaar/git/)
1029
:param repository: A repository object
1031
repo_transport = getattr(repository, "_transport", None)
1032
if repo_transport is not None:
1034
migrate_ancient_formats(repo_transport)
1035
except bzr_errors.ReadOnlyError:
1036
pass # Not much we can do
1037
return BzrGitCacheFormat.from_repository(repository)