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 (
40
btree_index as _mod_btree_index,
44
from ..sixish import (
49
from ..transport import (
50
get_transport_from_path,
55
path = os.path.join(bedding.cache_dir(), "git")
56
if not os.path.isdir(path):
61
def get_remote_cache_transport(repository):
62
"""Retrieve the transport to use when accessing (unwritable) remote
65
uuid = getattr(repository, "uuid", None)
67
path = get_cache_dir()
69
path = os.path.join(get_cache_dir(), uuid)
70
if not os.path.isdir(path):
72
return get_transport_from_path(path)
75
def check_pysqlite_version(sqlite3):
76
"""Check that sqlite library is compatible.
79
if (sqlite3.sqlite_version_info[0] < 3
80
or (sqlite3.sqlite_version_info[0] == 3 and
81
sqlite3.sqlite_version_info[1] < 3)):
82
trace.warning('Needs at least sqlite 3.3.x')
83
raise bzr_errors.BzrError("incompatible sqlite library")
89
check_pysqlite_version(sqlite3)
90
except (ImportError, bzr_errors.BzrError):
91
from pysqlite2 import dbapi2 as sqlite3
92
check_pysqlite_version(sqlite3)
94
trace.warning('Needs at least Python2.5 or Python2.4 with the pysqlite2 '
96
raise bzr_errors.BzrError("missing sqlite library")
99
_mapdbs = threading.local()
103
"""Get a cache for this thread's db connections."""
106
except AttributeError:
111
class GitShaMap(object):
112
"""Git<->Bzr revision id mapping database."""
114
def lookup_git_sha(self, sha):
115
"""Lookup a Git sha in the database.
116
:param sha: Git object sha
117
:return: list with (type, type_data) tuples with type_data:
118
commit: revid, tree_sha, verifiers
122
raise NotImplementedError(self.lookup_git_sha)
124
def lookup_blob_id(self, file_id, revision):
125
"""Retrieve a Git blob SHA by file id.
127
:param file_id: File id of the file/symlink
128
:param revision: revision in which the file was last changed.
130
raise NotImplementedError(self.lookup_blob_id)
132
def lookup_tree_id(self, file_id, revision):
133
"""Retrieve a Git tree SHA by file id.
135
raise NotImplementedError(self.lookup_tree_id)
137
def lookup_commit(self, revid):
138
"""Retrieve a Git commit SHA by Bazaar revision id.
140
raise NotImplementedError(self.lookup_commit)
143
"""List the revision ids known."""
144
raise NotImplementedError(self.revids)
146
def missing_revisions(self, revids):
147
"""Return set of all the revisions that are not present."""
148
present_revids = set(self.revids())
149
if not isinstance(revids, set):
151
return revids - present_revids
154
"""List the SHA1s."""
155
raise NotImplementedError(self.sha1s)
157
def start_write_group(self):
158
"""Start writing changes."""
160
def commit_write_group(self):
161
"""Commit any pending changes."""
163
def abort_write_group(self):
164
"""Abort any pending changes."""
167
class ContentCache(object):
168
"""Object that can cache Git objects."""
170
def add(self, object):
172
raise NotImplementedError(self.add)
174
def add_multi(self, objects):
175
"""Add multiple objects."""
179
def __getitem__(self, sha):
180
"""Retrieve an item, by SHA."""
181
raise NotImplementedError(self.__getitem__)
184
class BzrGitCacheFormat(object):
185
"""Bazaar-Git Cache Format."""
187
def get_format_string(self):
188
"""Return a single-line unique format string for this cache format."""
189
raise NotImplementedError(self.get_format_string)
191
def open(self, transport):
192
"""Open this format on a transport."""
193
raise NotImplementedError(self.open)
195
def initialize(self, transport):
196
"""Create a new instance of this cache format at transport."""
197
transport.put_bytes('format', self.get_format_string())
200
def from_transport(self, transport):
201
"""Open a cache file present on a transport, or initialize one.
203
:param transport: Transport to use
204
:return: A BzrGitCache instance
207
format_name = transport.get_bytes('format')
208
format = formats.get(format_name)
209
except bzr_errors.NoSuchFile:
210
format = formats.get('default')
211
format.initialize(transport)
212
return format.open(transport)
215
def from_repository(cls, repository):
216
"""Open a cache file for a repository.
218
This will use the repository's transport to store the cache file, or
219
use the users global cache directory if the repository has no
220
transport associated with it.
222
:param repository: Repository to open the cache for
223
:return: A `BzrGitCache`
225
from ..transport.local import LocalTransport
226
repo_transport = getattr(repository, "_transport", None)
227
if (repo_transport is not None
228
and isinstance(repo_transport, LocalTransport)):
229
# Even if we don't write to this repo, we should be able
230
# to update its cache.
232
repo_transport = remove_readonly_transport_decorator(
234
except bzr_errors.ReadOnlyError:
238
repo_transport.mkdir('git')
239
except bzr_errors.FileExists:
241
transport = repo_transport.clone('git')
244
if transport is None:
245
transport = get_remote_cache_transport(repository)
246
return cls.from_transport(transport)
249
class CacheUpdater(object):
250
"""Base class for objects that can update a bzr-git cache."""
252
def add_object(self, obj, bzr_key_data, path):
255
:param obj: Object type ("commit", "blob" or "tree")
256
:param bzr_key_data: bzr key store data or testament_sha in case
258
:param path: Path of the object (optional)
260
raise NotImplementedError(self.add_object)
263
raise NotImplementedError(self.finish)
266
class BzrGitCache(object):
267
"""Caching backend."""
269
def __init__(self, idmap, cache_updater_klass):
271
self._cache_updater_klass = cache_updater_klass
273
def get_updater(self, rev):
274
"""Update an object that implements the CacheUpdater interface for
277
return self._cache_updater_klass(self, rev)
280
def DictBzrGitCache():
281
return BzrGitCache(DictGitShaMap(), DictCacheUpdater)
284
class DictCacheUpdater(CacheUpdater):
285
"""Cache updater for dict-based caches."""
287
def __init__(self, cache, rev):
289
self.revid = rev.revision_id
290
self.parent_revids = rev.parent_ids
294
def add_object(self, obj, bzr_key_data, path):
295
if isinstance(obj, tuple):
296
(type_name, hexsha) = obj
298
type_name = obj.type_name.decode('ascii')
300
if not isinstance(hexsha, bytes):
301
raise TypeError(hexsha)
302
if type_name == "commit":
304
if type(bzr_key_data) is not dict:
305
raise TypeError(bzr_key_data)
307
type_data = (self.revid, self._commit.tree, bzr_key_data)
308
self.cache.idmap._by_revid[self.revid] = hexsha
309
elif type_name in ("blob", "tree"):
310
if bzr_key_data is not None:
311
key = type_data = bzr_key_data
312
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[
313
type_data[0]] = hexsha
316
entry = (type_name, type_data)
317
self.cache.idmap._by_sha.setdefault(hexsha, {})[key] = entry
320
if self._commit is None:
321
raise AssertionError("No commit object added")
325
class DictGitShaMap(GitShaMap):
326
"""Git SHA map that uses a dictionary."""
333
def lookup_blob_id(self, fileid, revision):
334
return self._by_fileid[revision][fileid]
336
def lookup_git_sha(self, sha):
337
if not isinstance(sha, bytes):
339
for entry in viewvalues(self._by_sha[sha]):
342
def lookup_tree_id(self, fileid, revision):
343
return self._by_fileid[revision][fileid]
345
def lookup_commit(self, revid):
346
return self._by_revid[revid]
349
for key, entries in viewitems(self._by_sha):
350
for (type, type_data) in viewvalues(entries):
355
return viewkeys(self._by_sha)
358
class SqliteCacheUpdater(CacheUpdater):
360
def __init__(self, cache, rev):
362
self.db = self.cache.idmap.db
363
self.revid = rev.revision_id
368
def add_object(self, obj, bzr_key_data, path):
369
if isinstance(obj, tuple):
370
(type_name, hexsha) = obj
372
type_name = obj.type_name.decode('ascii')
374
if not isinstance(hexsha, bytes):
375
raise TypeError(hexsha)
376
if type_name == "commit":
378
if type(bzr_key_data) is not dict:
379
raise TypeError(bzr_key_data)
380
self._testament3_sha1 = bzr_key_data.get("testament3-sha1")
381
elif type_name == "tree":
382
if bzr_key_data is not None:
383
self._trees.append((hexsha, bzr_key_data[0], bzr_key_data[1]))
384
elif type_name == "blob":
385
if bzr_key_data is not None:
386
self._blobs.append((hexsha, bzr_key_data[0], bzr_key_data[1]))
391
if self._commit is None:
392
raise AssertionError("No commit object added")
394
"replace into trees (sha1, fileid, revid) values (?, ?, ?)",
397
"replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
400
"replace into commits (sha1, revid, tree_sha, testament3_sha1) "
401
"values (?, ?, ?, ?)",
402
(self._commit.id, self.revid, self._commit.tree,
403
self._testament3_sha1))
407
def SqliteBzrGitCache(p):
408
return BzrGitCache(SqliteGitShaMap(p), SqliteCacheUpdater)
411
class SqliteGitCacheFormat(BzrGitCacheFormat):
413
def get_format_string(self):
414
return b'bzr-git sha map version 1 using sqlite\n'
416
def open(self, transport):
418
basepath = transport.local_abspath(".")
419
except bzr_errors.NotLocalUrl:
420
basepath = get_cache_dir()
421
return SqliteBzrGitCache(os.path.join(basepath, "idmap.db"))
424
class SqliteGitShaMap(GitShaMap):
425
"""Bazaar GIT Sha map that uses a sqlite database for storage."""
427
def __init__(self, path=None):
430
self.db = sqlite3.connect(":memory:")
432
if path not in mapdbs():
433
mapdbs()[path] = sqlite3.connect(path)
434
self.db = mapdbs()[path]
435
self.db.text_factory = str
436
self.db.executescript("""
437
create table if not exists commits(
438
sha1 text not null check(length(sha1) == 40),
440
tree_sha text not null check(length(tree_sha) == 40)
442
create index if not exists commit_sha1 on commits(sha1);
443
create unique index if not exists commit_revid on commits(revid);
444
create table if not exists blobs(
445
sha1 text not null check(length(sha1) == 40),
446
fileid text not null,
449
create index if not exists blobs_sha1 on blobs(sha1);
450
create unique index if not exists blobs_fileid_revid on blobs(
452
create table if not exists trees(
453
sha1 text unique not null check(length(sha1) == 40),
454
fileid text not null,
457
create unique index if not exists trees_sha1 on trees(sha1);
458
create unique index if not exists trees_fileid_revid on trees(
462
self.db.executescript(
463
"ALTER TABLE commits ADD testament3_sha1 TEXT;")
464
except sqlite3.OperationalError:
465
pass # Column already exists.
468
return "%s(%r)" % (self.__class__.__name__, self.path)
470
def lookup_commit(self, revid):
471
cursor = self.db.execute("select sha1 from commits where revid = ?",
473
row = cursor.fetchone()
478
def commit_write_group(self):
481
def lookup_blob_id(self, fileid, revision):
482
row = self.db.execute(
483
"select sha1 from blobs where fileid = ? and revid = ?",
484
(fileid, revision)).fetchone()
487
raise KeyError(fileid)
489
def lookup_tree_id(self, fileid, revision):
490
row = self.db.execute(
491
"select sha1 from trees where fileid = ? and revid = ?",
492
(fileid, revision)).fetchone()
495
raise KeyError(fileid)
497
def lookup_git_sha(self, sha):
498
"""Lookup a Git sha in the database.
500
:param sha: Git object sha
501
:return: (type, type_data) with type_data:
502
commit: revid, tree sha, verifiers
507
cursor = self.db.execute(
508
"select revid, tree_sha, testament3_sha1 from commits where "
510
for row in cursor.fetchall():
512
if row[2] is not None:
513
verifiers = {"testament3-sha1": row[2]}
516
yield ("commit", (row[0], row[1], verifiers))
517
cursor = self.db.execute(
518
"select fileid, revid from blobs where sha1 = ?", (sha,))
519
for row in cursor.fetchall():
522
cursor = self.db.execute(
523
"select fileid, revid from trees where sha1 = ?", (sha,))
524
for row in cursor.fetchall():
531
"""List the revision ids known."""
532
return (row for (row,) in self.db.execute("select revid from commits"))
535
"""List the SHA1s."""
536
for table in ("blobs", "commits", "trees"):
537
for (sha,) in self.db.execute("select sha1 from %s" % table):
538
yield sha.encode('ascii')
541
class TdbCacheUpdater(CacheUpdater):
542
"""Cache updater for tdb-based caches."""
544
def __init__(self, cache, rev):
546
self.db = cache.idmap.db
547
self.revid = rev.revision_id
548
self.parent_revids = rev.parent_ids
552
def add_object(self, obj, bzr_key_data, path):
553
if isinstance(obj, tuple):
554
(type_name, hexsha) = obj
555
sha = hex_to_sha(hexsha)
557
type_name = obj.type_name.decode('ascii')
558
sha = obj.sha().digest()
559
if type_name == "commit":
560
self.db[b"commit\0" + self.revid] = b"\0".join((sha, obj.tree))
561
if type(bzr_key_data) is not dict:
562
raise TypeError(bzr_key_data)
563
type_data = (self.revid, obj.tree)
565
type_data += (bzr_key_data["testament3-sha1"],)
569
elif type_name == "blob":
570
if bzr_key_data is None:
573
(b"blob", bzr_key_data[0], bzr_key_data[1]))] = sha
574
type_data = bzr_key_data
575
elif type_name == "tree":
576
if bzr_key_data is None:
578
type_data = bzr_key_data
581
entry = b"\0".join((type_name.encode('ascii'), ) + type_data) + b"\n"
584
oldval = self.db[key]
588
if not oldval.endswith(b'\n'):
589
self.db[key] = b"".join([oldval, b"\n", entry])
591
self.db[key] = b"".join([oldval, entry])
594
if self._commit is None:
595
raise AssertionError("No commit object added")
599
def TdbBzrGitCache(p):
600
return BzrGitCache(TdbGitShaMap(p), TdbCacheUpdater)
603
class TdbGitCacheFormat(BzrGitCacheFormat):
604
"""Cache format for tdb-based caches."""
606
def get_format_string(self):
607
return b'bzr-git sha map version 3 using tdb\n'
609
def open(self, transport):
611
basepath = transport.local_abspath(".")
612
except bzr_errors.NotLocalUrl:
613
basepath = get_cache_dir()
615
return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
618
"Unable to open existing bzr-git cache because 'tdb' is not "
622
class TdbGitShaMap(GitShaMap):
623
"""SHA Map that uses a TDB database.
627
"git <sha1>" -> "<type> <type-data1> <type-data2>"
628
"commit revid" -> "<sha1> <tree-id>"
629
"tree fileid revid" -> "<sha1>"
630
"blob fileid revid" -> "<sha1>"
634
TDB_HASH_SIZE = 50000
636
def __init__(self, path=None):
642
if path not in mapdbs():
643
mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
644
os.O_RDWR | os.O_CREAT)
645
self.db = mapdbs()[path]
647
if int(self.db[b"version"]) not in (2, 3):
649
"SHA Map is incompatible (%s -> %d), rebuilding database.",
650
self.db[b"version"], self.TDB_MAP_VERSION)
654
self.db[b"version"] = b'%d' % self.TDB_MAP_VERSION
656
def start_write_group(self):
657
"""Start writing changes."""
658
self.db.transaction_start()
660
def commit_write_group(self):
661
"""Commit any pending changes."""
662
self.db.transaction_commit()
664
def abort_write_group(self):
665
"""Abort any pending changes."""
666
self.db.transaction_cancel()
669
return "%s(%r)" % (self.__class__.__name__, self.path)
671
def lookup_commit(self, revid):
673
return sha_to_hex(self.db[b"commit\0" + revid][:20])
675
raise KeyError("No cache entry for %r" % revid)
677
def lookup_blob_id(self, fileid, revision):
678
return sha_to_hex(self.db[b"\0".join((b"blob", fileid, revision))])
680
def lookup_git_sha(self, sha):
681
"""Lookup a Git sha in the database.
683
:param sha: Git object sha
684
:return: (type, type_data) with type_data:
685
commit: revid, tree sha
690
sha = hex_to_sha(sha)
691
value = self.db[b"git\0" + sha]
692
for data in value.splitlines():
693
data = data.split(b"\0")
694
type_name = data[0].decode('ascii')
695
if type_name == "commit":
697
yield (type_name, (data[1], data[2], {}))
699
yield (type_name, (data[1], data[2],
700
{"testament3-sha1": data[3]}))
701
elif type_name in ("tree", "blob"):
702
yield (type_name, tuple(data[1:]))
704
raise AssertionError("unknown type %r" % type_name)
706
def missing_revisions(self, revids):
709
if self.db.get(b"commit\0" + revid) is None:
715
return self.db.keys()
716
except AttributeError: # python < 3
717
return self.db.iterkeys()
720
"""List the revision ids known."""
721
for key in self._keys():
722
if key.startswith(b"commit\0"):
726
"""List the SHA1s."""
727
for key in self._keys():
728
if key.startswith(b"git\0"):
729
yield sha_to_hex(key[4:])
732
class VersionedFilesContentCache(ContentCache):
734
def __init__(self, vf):
738
self._vf.insert_record_stream(
739
[versionedfile.ChunkedContentFactory(
740
(obj.id,), [], None, obj.as_legacy_object_chunks())])
742
def __getitem__(self, sha):
743
stream = self._vf.get_record_stream([(sha,)], 'unordered', True)
745
if entry.storage_kind == 'absent':
747
return ShaFile._parse_legacy_object(entry.get_bytes_as('fulltext'))
750
class IndexCacheUpdater(CacheUpdater):
752
def __init__(self, cache, rev):
754
self.revid = rev.revision_id
755
self.parent_revids = rev.parent_ids
759
def add_object(self, obj, bzr_key_data, path):
760
if isinstance(obj, tuple):
761
(type_name, hexsha) = obj
763
type_name = obj.type_name.decode('ascii')
765
if type_name == "commit":
767
if type(bzr_key_data) is not dict:
768
raise TypeError(bzr_key_data)
769
self.cache.idmap._add_git_sha(hexsha, b"commit",
770
(self.revid, obj.tree, bzr_key_data))
771
self.cache.idmap._add_node((b"commit", self.revid, b"X"),
772
b" ".join((hexsha, obj.tree)))
773
elif type_name == "blob":
774
self.cache.idmap._add_git_sha(hexsha, b"blob", bzr_key_data)
775
self.cache.idmap._add_node((b"blob", bzr_key_data[0],
776
bzr_key_data[1]), hexsha)
777
elif type_name == "tree":
778
self.cache.idmap._add_git_sha(hexsha, b"tree", bzr_key_data)
786
class IndexBzrGitCache(BzrGitCache):
788
def __init__(self, transport=None):
789
shamap = IndexGitShaMap(transport.clone('index'))
790
super(IndexBzrGitCache, self).__init__(shamap, IndexCacheUpdater)
793
class IndexGitCacheFormat(BzrGitCacheFormat):
795
def get_format_string(self):
796
return b'bzr-git sha map with git object cache version 1\n'
798
def initialize(self, transport):
799
super(IndexGitCacheFormat, self).initialize(transport)
800
transport.mkdir('index')
801
transport.mkdir('objects')
802
from .transportgit import TransportObjectStore
803
TransportObjectStore.init(transport.clone('objects'))
805
def open(self, transport):
806
return IndexBzrGitCache(transport)
809
class IndexGitShaMap(GitShaMap):
810
"""SHA Map that uses the Bazaar APIs to store a cache.
812
BTree Index file with the following contents:
814
("git", <sha1>, "X") -> "<type> <type-data1> <type-data2>"
815
("commit", <revid>, "X") -> "<sha1> <tree-id>"
816
("blob", <fileid>, <revid>) -> <sha1>
820
def __init__(self, transport=None):
822
if transport is None:
823
self._transport = None
824
self._index = _mod_index.InMemoryGraphIndex(0, key_elements=3)
825
self._builder = self._index
828
self._transport = transport
829
self._index = _mod_index.CombinedGraphIndex([])
830
for name in self._transport.list_dir("."):
831
if not name.endswith(".rix"):
833
x = _mod_btree_index.BTreeGraphIndex(
834
self._transport, name, self._transport.stat(name).st_size)
835
self._index.insert_index(0, x)
838
def from_repository(cls, repository):
839
transport = getattr(repository, "_transport", None)
840
if transport is not None:
842
transport.mkdir('git')
843
except bzr_errors.FileExists:
845
return cls(transport.clone('git'))
846
return cls(get_transport_from_path(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)