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 (
55
from xdg.BaseDirectory import xdg_cache_home
57
from ..config import config_dir
58
ret = os.path.join(config_dir(), "git")
60
ret = os.path.join(xdg_cache_home, "breezy", "git")
61
if not os.path.isdir(ret):
66
def get_remote_cache_transport(repository):
67
"""Retrieve the transport to use when accessing (unwritable) remote
70
uuid = getattr(repository, "uuid", None)
72
path = get_cache_dir()
74
path = os.path.join(get_cache_dir(), uuid)
75
if not os.path.isdir(path):
77
return get_transport(path)
80
def check_pysqlite_version(sqlite3):
81
"""Check that sqlite library is compatible.
84
if (sqlite3.sqlite_version_info[0] < 3
85
or (sqlite3.sqlite_version_info[0] == 3 and
86
sqlite3.sqlite_version_info[1] < 3)):
87
trace.warning('Needs at least sqlite 3.3.x')
88
raise bzr_errors.BzrError("incompatible sqlite library")
94
check_pysqlite_version(sqlite3)
95
except (ImportError, bzr_errors.BzrError):
96
from pysqlite2 import dbapi2 as sqlite3
97
check_pysqlite_version(sqlite3)
99
trace.warning('Needs at least Python2.5 or Python2.4 with the pysqlite2 '
101
raise bzr_errors.BzrError("missing sqlite library")
104
_mapdbs = threading.local()
108
"""Get a cache for this thread's db connections."""
111
except AttributeError:
116
class GitShaMap(object):
117
"""Git<->Bzr revision id mapping database."""
119
def lookup_git_sha(self, sha):
120
"""Lookup a Git sha in the database.
121
:param sha: Git object sha
122
:return: list with (type, type_data) tuples with type_data:
123
commit: revid, tree_sha, verifiers
127
raise NotImplementedError(self.lookup_git_sha)
129
def lookup_blob_id(self, file_id, revision):
130
"""Retrieve a Git blob SHA by file id.
132
:param file_id: File id of the file/symlink
133
:param revision: revision in which the file was last changed.
135
raise NotImplementedError(self.lookup_blob_id)
137
def lookup_tree_id(self, file_id, revision):
138
"""Retrieve a Git tree SHA by file id.
140
raise NotImplementedError(self.lookup_tree_id)
142
def lookup_commit(self, revid):
143
"""Retrieve a Git commit SHA by Bazaar revision id.
145
raise NotImplementedError(self.lookup_commit)
148
"""List the revision ids known."""
149
raise NotImplementedError(self.revids)
151
def missing_revisions(self, revids):
152
"""Return set of all the revisions that are not present."""
153
present_revids = set(self.revids())
154
if not isinstance(revids, set):
156
return revids - present_revids
159
"""List the SHA1s."""
160
raise NotImplementedError(self.sha1s)
162
def start_write_group(self):
163
"""Start writing changes."""
165
def commit_write_group(self):
166
"""Commit any pending changes."""
168
def abort_write_group(self):
169
"""Abort any pending changes."""
172
class ContentCache(object):
173
"""Object that can cache Git objects."""
175
def add(self, object):
177
raise NotImplementedError(self.add)
179
def add_multi(self, objects):
180
"""Add multiple objects."""
184
def __getitem__(self, sha):
185
"""Retrieve an item, by SHA."""
186
raise NotImplementedError(self.__getitem__)
189
class BzrGitCacheFormat(object):
190
"""Bazaar-Git Cache Format."""
192
def get_format_string(self):
193
"""Return a single-line unique format string for this cache format."""
194
raise NotImplementedError(self.get_format_string)
196
def open(self, transport):
197
"""Open this format on a transport."""
198
raise NotImplementedError(self.open)
200
def initialize(self, transport):
201
"""Create a new instance of this cache format at transport."""
202
transport.put_bytes('format', self.get_format_string())
205
def from_transport(self, transport):
206
"""Open a cache file present on a transport, or initialize one.
208
:param transport: Transport to use
209
:return: A BzrGitCache instance
212
format_name = transport.get_bytes('format')
213
format = formats.get(format_name)
214
except bzr_errors.NoSuchFile:
215
format = formats.get('default')
216
format.initialize(transport)
217
return format.open(transport)
220
def from_repository(cls, repository):
221
"""Open a cache file for a repository.
223
This will use the repository's transport to store the cache file, or
224
use the users global cache directory if the repository has no
225
transport associated with it.
227
:param repository: Repository to open the cache for
228
:return: A `BzrGitCache`
230
from ..transport.local import LocalTransport
231
repo_transport = getattr(repository, "_transport", None)
232
if (repo_transport is not None
233
and isinstance(repo_transport, LocalTransport)):
234
# Even if we don't write to this repo, we should be able
235
# to update its cache.
237
repo_transport = remove_readonly_transport_decorator(
239
except bzr_errors.ReadOnlyError:
243
repo_transport.mkdir('git')
244
except bzr_errors.FileExists:
246
transport = repo_transport.clone('git')
249
if transport is None:
250
transport = get_remote_cache_transport(repository)
251
return cls.from_transport(transport)
254
class CacheUpdater(object):
255
"""Base class for objects that can update a bzr-git cache."""
257
def add_object(self, obj, bzr_key_data, path):
260
:param obj: Object type ("commit", "blob" or "tree")
261
:param bzr_key_data: bzr key store data or testament_sha in case
263
:param path: Path of the object (optional)
265
raise NotImplementedError(self.add_object)
268
raise NotImplementedError(self.finish)
271
class BzrGitCache(object):
272
"""Caching backend."""
274
def __init__(self, idmap, cache_updater_klass):
276
self._cache_updater_klass = cache_updater_klass
278
def get_updater(self, rev):
279
"""Update an object that implements the CacheUpdater interface for
282
return self._cache_updater_klass(self, rev)
285
def DictBzrGitCache():
286
return BzrGitCache(DictGitShaMap(), DictCacheUpdater)
289
class DictCacheUpdater(CacheUpdater):
290
"""Cache updater for dict-based caches."""
292
def __init__(self, cache, rev):
294
self.revid = rev.revision_id
295
self.parent_revids = rev.parent_ids
299
def add_object(self, obj, bzr_key_data, path):
300
if isinstance(obj, tuple):
301
(type_name, hexsha) = obj
303
type_name = obj.type_name.decode('ascii')
305
if not isinstance(hexsha, bytes):
306
raise TypeError(hexsha)
307
if type_name == "commit":
309
if type(bzr_key_data) is not dict:
310
raise TypeError(bzr_key_data)
312
type_data = (self.revid, self._commit.tree, bzr_key_data)
313
self.cache.idmap._by_revid[self.revid] = hexsha
314
elif type_name in ("blob", "tree"):
315
if bzr_key_data is not None:
316
key = type_data = bzr_key_data
317
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[
318
type_data[0]] = hexsha
321
entry = (type_name, type_data)
322
self.cache.idmap._by_sha.setdefault(hexsha, {})[key] = entry
325
if self._commit is None:
326
raise AssertionError("No commit object added")
330
class DictGitShaMap(GitShaMap):
331
"""Git SHA map that uses a dictionary."""
338
def lookup_blob_id(self, fileid, revision):
339
return self._by_fileid[revision][fileid]
341
def lookup_git_sha(self, sha):
342
if not isinstance(sha, bytes):
344
for entry in viewvalues(self._by_sha[sha]):
347
def lookup_tree_id(self, fileid, revision):
348
return self._by_fileid[revision][fileid]
350
def lookup_commit(self, revid):
351
return self._by_revid[revid]
354
for key, entries in viewitems(self._by_sha):
355
for (type, type_data) in viewvalues(entries):
360
return viewkeys(self._by_sha)
363
class SqliteCacheUpdater(CacheUpdater):
365
def __init__(self, cache, rev):
367
self.db = self.cache.idmap.db
368
self.revid = rev.revision_id
373
def add_object(self, obj, bzr_key_data, path):
374
if isinstance(obj, tuple):
375
(type_name, hexsha) = obj
377
type_name = obj.type_name.decode('ascii')
379
if not isinstance(hexsha, bytes):
380
raise TypeError(hexsha)
381
if type_name == "commit":
383
if type(bzr_key_data) is not dict:
384
raise TypeError(bzr_key_data)
385
self._testament3_sha1 = bzr_key_data.get("testament3-sha1")
386
elif type_name == "tree":
387
if bzr_key_data is not None:
388
self._trees.append((hexsha, bzr_key_data[0], bzr_key_data[1]))
389
elif type_name == "blob":
390
if bzr_key_data is not None:
391
self._blobs.append((hexsha, bzr_key_data[0], bzr_key_data[1]))
396
if self._commit is None:
397
raise AssertionError("No commit object added")
399
"replace into trees (sha1, fileid, revid) values (?, ?, ?)",
402
"replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
405
"replace into commits (sha1, revid, tree_sha, testament3_sha1) "
406
"values (?, ?, ?, ?)",
407
(self._commit.id, self.revid, self._commit.tree,
408
self._testament3_sha1))
412
def SqliteBzrGitCache(p):
413
return BzrGitCache(SqliteGitShaMap(p), SqliteCacheUpdater)
416
class SqliteGitCacheFormat(BzrGitCacheFormat):
418
def get_format_string(self):
419
return b'bzr-git sha map version 1 using sqlite\n'
421
def open(self, transport):
423
basepath = transport.local_abspath(".")
424
except bzr_errors.NotLocalUrl:
425
basepath = get_cache_dir()
426
return SqliteBzrGitCache(os.path.join(basepath, "idmap.db"))
429
class SqliteGitShaMap(GitShaMap):
430
"""Bazaar GIT Sha map that uses a sqlite database for storage."""
432
def __init__(self, path=None):
435
self.db = sqlite3.connect(":memory:")
437
if path not in mapdbs():
438
mapdbs()[path] = sqlite3.connect(path)
439
self.db = mapdbs()[path]
440
self.db.text_factory = str
441
self.db.executescript("""
442
create table if not exists commits(
443
sha1 text not null check(length(sha1) == 40),
445
tree_sha text not null check(length(tree_sha) == 40)
447
create index if not exists commit_sha1 on commits(sha1);
448
create unique index if not exists commit_revid on commits(revid);
449
create table if not exists blobs(
450
sha1 text not null check(length(sha1) == 40),
451
fileid text not null,
454
create index if not exists blobs_sha1 on blobs(sha1);
455
create unique index if not exists blobs_fileid_revid on blobs(
457
create table if not exists trees(
458
sha1 text unique not null check(length(sha1) == 40),
459
fileid text not null,
462
create unique index if not exists trees_sha1 on trees(sha1);
463
create unique index if not exists trees_fileid_revid on trees(
467
self.db.executescript(
468
"ALTER TABLE commits ADD testament3_sha1 TEXT;")
469
except sqlite3.OperationalError:
470
pass # Column already exists.
473
return "%s(%r)" % (self.__class__.__name__, self.path)
475
def lookup_commit(self, revid):
476
cursor = self.db.execute("select sha1 from commits where revid = ?",
478
row = cursor.fetchone()
483
def commit_write_group(self):
486
def lookup_blob_id(self, fileid, revision):
487
row = self.db.execute(
488
"select sha1 from blobs where fileid = ? and revid = ?",
489
(fileid, revision)).fetchone()
492
raise KeyError(fileid)
494
def lookup_tree_id(self, fileid, revision):
495
row = self.db.execute(
496
"select sha1 from trees where fileid = ? and revid = ?",
497
(fileid, revision)).fetchone()
500
raise KeyError(fileid)
502
def lookup_git_sha(self, sha):
503
"""Lookup a Git sha in the database.
505
:param sha: Git object sha
506
:return: (type, type_data) with type_data:
507
commit: revid, tree sha, verifiers
512
cursor = self.db.execute(
513
"select revid, tree_sha, testament3_sha1 from commits where "
515
for row in cursor.fetchall():
517
if row[2] is not None:
518
verifiers = {"testament3-sha1": row[2]}
521
yield ("commit", (row[0], row[1], verifiers))
522
cursor = self.db.execute(
523
"select fileid, revid from blobs where sha1 = ?", (sha,))
524
for row in cursor.fetchall():
527
cursor = self.db.execute(
528
"select fileid, revid from trees where sha1 = ?", (sha,))
529
for row in cursor.fetchall():
536
"""List the revision ids known."""
537
return (row for (row,) in self.db.execute("select revid from commits"))
540
"""List the SHA1s."""
541
for table in ("blobs", "commits", "trees"):
542
for (sha,) in self.db.execute("select sha1 from %s" % table):
543
yield sha.encode('ascii')
546
class TdbCacheUpdater(CacheUpdater):
547
"""Cache updater for tdb-based caches."""
549
def __init__(self, cache, rev):
551
self.db = cache.idmap.db
552
self.revid = rev.revision_id
553
self.parent_revids = rev.parent_ids
557
def add_object(self, obj, bzr_key_data, path):
558
if isinstance(obj, tuple):
559
(type_name, hexsha) = obj
560
sha = hex_to_sha(hexsha)
562
type_name = obj.type_name.decode('ascii')
563
sha = obj.sha().digest()
564
if type_name == "commit":
565
self.db[b"commit\0" + self.revid] = b"\0".join((sha, obj.tree))
566
if type(bzr_key_data) is not dict:
567
raise TypeError(bzr_key_data)
568
type_data = (self.revid, obj.tree)
570
type_data += (bzr_key_data["testament3-sha1"],)
574
elif type_name == "blob":
575
if bzr_key_data is None:
578
(b"blob", bzr_key_data[0], bzr_key_data[1]))] = sha
579
type_data = bzr_key_data
580
elif type_name == "tree":
581
if bzr_key_data is None:
583
type_data = bzr_key_data
586
entry = b"\0".join((type_name.encode('ascii'), ) + type_data) + b"\n"
589
oldval = self.db[key]
593
if not oldval.endswith(b'\n'):
594
self.db[key] = b"".join([oldval, b"\n", entry])
596
self.db[key] = b"".join([oldval, entry])
599
if self._commit is None:
600
raise AssertionError("No commit object added")
604
def TdbBzrGitCache(p):
605
return BzrGitCache(TdbGitShaMap(p), TdbCacheUpdater)
608
class TdbGitCacheFormat(BzrGitCacheFormat):
609
"""Cache format for tdb-based caches."""
611
def get_format_string(self):
612
return b'bzr-git sha map version 3 using tdb\n'
614
def open(self, transport):
616
basepath = transport.local_abspath(".").encode(osutils._fs_enc)
617
except bzr_errors.NotLocalUrl:
618
basepath = get_cache_dir()
619
if not isinstance(basepath, str):
620
raise TypeError(basepath)
622
return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
625
"Unable to open existing bzr-git cache because 'tdb' is not "
629
class TdbGitShaMap(GitShaMap):
630
"""SHA Map that uses a TDB database.
634
"git <sha1>" -> "<type> <type-data1> <type-data2>"
635
"commit revid" -> "<sha1> <tree-id>"
636
"tree fileid revid" -> "<sha1>"
637
"blob fileid revid" -> "<sha1>"
641
TDB_HASH_SIZE = 50000
643
def __init__(self, path=None):
649
if path not in mapdbs():
650
mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
651
os.O_RDWR | os.O_CREAT)
652
self.db = mapdbs()[path]
654
if int(self.db[b"version"]) not in (2, 3):
656
"SHA Map is incompatible (%s -> %d), rebuilding database.",
657
self.db[b"version"], self.TDB_MAP_VERSION)
661
self.db[b"version"] = b'%d' % self.TDB_MAP_VERSION
663
def start_write_group(self):
664
"""Start writing changes."""
665
self.db.transaction_start()
667
def commit_write_group(self):
668
"""Commit any pending changes."""
669
self.db.transaction_commit()
671
def abort_write_group(self):
672
"""Abort any pending changes."""
673
self.db.transaction_cancel()
676
return "%s(%r)" % (self.__class__.__name__, self.path)
678
def lookup_commit(self, revid):
680
return sha_to_hex(self.db[b"commit\0" + revid][:20])
682
raise KeyError("No cache entry for %r" % revid)
684
def lookup_blob_id(self, fileid, revision):
685
return sha_to_hex(self.db[b"\0".join((b"blob", fileid, revision))])
687
def lookup_git_sha(self, sha):
688
"""Lookup a Git sha in the database.
690
:param sha: Git object sha
691
:return: (type, type_data) with type_data:
692
commit: revid, tree sha
697
sha = hex_to_sha(sha)
698
value = self.db[b"git\0" + sha]
699
for data in value.splitlines():
700
data = data.split(b"\0")
701
type_name = data[0].decode('ascii')
702
if type_name == "commit":
704
yield (type_name, (data[1], data[2], {}))
706
yield (type_name, (data[1], data[2],
707
{"testament3-sha1": data[3]}))
708
elif type_name in ("tree", "blob"):
709
yield (type_name, tuple(data[1:]))
711
raise AssertionError("unknown type %r" % type_name)
713
def missing_revisions(self, revids):
716
if self.db.get(b"commit\0" + revid) is None:
722
return self.db.keys()
723
except AttributeError: # python < 3
724
return self.db.iterkeys()
727
"""List the revision ids known."""
728
for key in self._keys():
729
if key.startswith(b"commit\0"):
733
"""List the SHA1s."""
734
for key in self._keys():
735
if key.startswith(b"git\0"):
736
yield sha_to_hex(key[4:])
739
class VersionedFilesContentCache(ContentCache):
741
def __init__(self, vf):
745
self._vf.insert_record_stream(
746
[versionedfile.ChunkedContentFactory(
747
(obj.id,), [], None, obj.as_legacy_object_chunks())])
749
def __getitem__(self, sha):
750
stream = self._vf.get_record_stream([(sha,)], 'unordered', True)
752
if entry.storage_kind == 'absent':
754
return ShaFile._parse_legacy_object(entry.get_bytes_as('fulltext'))
757
class IndexCacheUpdater(CacheUpdater):
759
def __init__(self, cache, rev):
761
self.revid = rev.revision_id
762
self.parent_revids = rev.parent_ids
766
def add_object(self, obj, bzr_key_data, path):
767
if isinstance(obj, tuple):
768
(type_name, hexsha) = obj
770
type_name = obj.type_name.decode('ascii')
772
if type_name == "commit":
774
if type(bzr_key_data) is not dict:
775
raise TypeError(bzr_key_data)
776
self.cache.idmap._add_git_sha(hexsha, b"commit",
777
(self.revid, obj.tree, bzr_key_data))
778
self.cache.idmap._add_node((b"commit", self.revid, b"X"),
779
b" ".join((hexsha, obj.tree)))
780
elif type_name == "blob":
781
self.cache.idmap._add_git_sha(hexsha, b"blob", bzr_key_data)
782
self.cache.idmap._add_node((b"blob", bzr_key_data[0],
783
bzr_key_data[1]), hexsha)
784
elif type_name == "tree":
785
self.cache.idmap._add_git_sha(hexsha, b"tree", bzr_key_data)
793
class IndexBzrGitCache(BzrGitCache):
795
def __init__(self, transport=None):
796
shamap = IndexGitShaMap(transport.clone('index'))
797
super(IndexBzrGitCache, self).__init__(shamap, IndexCacheUpdater)
800
class IndexGitCacheFormat(BzrGitCacheFormat):
802
def get_format_string(self):
803
return b'bzr-git sha map with git object cache version 1\n'
805
def initialize(self, transport):
806
super(IndexGitCacheFormat, self).initialize(transport)
807
transport.mkdir('index')
808
transport.mkdir('objects')
809
from .transportgit import TransportObjectStore
810
TransportObjectStore.init(transport.clone('objects'))
812
def open(self, transport):
813
return IndexBzrGitCache(transport)
816
class IndexGitShaMap(GitShaMap):
817
"""SHA Map that uses the Bazaar APIs to store a cache.
819
BTree Index file with the following contents:
821
("git", <sha1>, "X") -> "<type> <type-data1> <type-data2>"
822
("commit", <revid>, "X") -> "<sha1> <tree-id>"
823
("blob", <fileid>, <revid>) -> <sha1>
827
def __init__(self, transport=None):
829
if transport is None:
830
self._transport = None
831
self._index = _mod_index.InMemoryGraphIndex(0, key_elements=3)
832
self._builder = self._index
835
self._transport = transport
836
self._index = _mod_index.CombinedGraphIndex([])
837
for name in self._transport.list_dir("."):
838
if not name.endswith(".rix"):
840
x = _mod_btree_index.BTreeGraphIndex(
841
self._transport, name, self._transport.stat(name).st_size)
842
self._index.insert_index(0, x)
845
def from_repository(cls, repository):
846
transport = getattr(repository, "_transport", None)
847
if transport is not None:
849
transport.mkdir('git')
850
except bzr_errors.FileExists:
852
return cls(transport.clone('git'))
853
from ..transport import get_transport
854
return cls(get_transport(get_cache_dir()))
857
if self._transport is not None:
858
return "%s(%r)" % (self.__class__.__name__, self._transport.base)
860
return "%s()" % (self.__class__.__name__)
863
if self._builder is not None:
864
raise bzr_errors.BzrError('builder already open')
865
self.start_write_group()
866
self._builder.add_nodes(
867
((key, value) for (_, key, value) in
868
self._index.iter_all_entries()))
870
for name in self._transport.list_dir('.'):
871
if name.endswith('.rix'):
872
to_remove.append(name)
873
self.commit_write_group()
874
del self._index.indices[1:]
875
for name in to_remove:
876
self._transport.rename(name, name + '.old')
878
def start_write_group(self):
879
if self._builder is not None:
880
raise bzr_errors.BzrError('builder already open')
881
self._builder = _mod_btree_index.BTreeBuilder(0, key_elements=3)
882
self._name = osutils.sha()
884
def commit_write_group(self):
885
if self._builder is None:
886
raise bzr_errors.BzrError('builder not open')
887
stream = self._builder.finish()
888
name = self._name.hexdigest() + ".rix"
889
size = self._transport.put_file(name, stream)
890
index = _mod_btree_index.BTreeGraphIndex(self._transport, name, size)
891
self._index.insert_index(0, index)
895
def abort_write_group(self):
896
if self._builder is None:
897
raise bzr_errors.BzrError('builder not open')
901
def _add_node(self, key, value):
905
self._builder.add_node(key, value)
910
def _get_entry(self, key):
911
entries = self._index.iter_entries([key])
913
return next(entries)[2]
914
except StopIteration:
915
if self._builder is None:
917
entries = self._builder.iter_entries([key])
919
return next(entries)[2]
920
except StopIteration:
923
def _iter_entries_prefix(self, prefix):
924
for entry in self._index.iter_entries_prefix([prefix]):
925
yield (entry[1], entry[2])
926
if self._builder is not None:
927
for entry in self._builder.iter_entries_prefix([prefix]):
928
yield (entry[1], entry[2])
930
def lookup_commit(self, revid):
931
return self._get_entry((b"commit", revid, b"X"))[:40]
933
def _add_git_sha(self, hexsha, type, type_data):
934
if hexsha is not None:
935
self._name.update(hexsha)
936
if type == b"commit":
937
td = (type_data[0], type_data[1])
939
td += (type_data[2]["testament3-sha1"],)
944
self._add_node((b"git", hexsha, b"X"), b" ".join((type,) + td))
946
# This object is not represented in Git - perhaps an empty
948
self._name.update(type + b" ".join(type_data))
950
def lookup_blob_id(self, fileid, revision):
951
return self._get_entry((b"blob", fileid, revision))
953
def lookup_git_sha(self, sha):
955
sha = sha_to_hex(sha)
956
value = self._get_entry((b"git", sha, b"X"))
957
data = value.split(b" ", 3)
958
if data[0] == b"commit":
961
verifiers = {"testament3-sha1": data[3]}
966
yield ("commit", (data[1], data[2], verifiers))
968
yield (data[0].decode('ascii'), tuple(data[1:]))
971
"""List the revision ids known."""
972
for key, value in self._iter_entries_prefix((b"commit", None, None)):
975
def missing_revisions(self, revids):
976
"""Return set of all the revisions that are not present."""
977
missing_revids = set(revids)
978
for _, key, value in self._index.iter_entries((
979
(b"commit", revid, b"X") for revid in revids)):
980
missing_revids.remove(key[1])
981
return missing_revids
984
"""List the SHA1s."""
985
for key, value in self._iter_entries_prefix((b"git", None, None)):
989
formats = registry.Registry()
990
formats.register(TdbGitCacheFormat().get_format_string(),
992
formats.register(SqliteGitCacheFormat().get_format_string(),
993
SqliteGitCacheFormat())
994
formats.register(IndexGitCacheFormat().get_format_string(),
995
IndexGitCacheFormat())
996
# In the future, this will become the default:
997
formats.register('default', IndexGitCacheFormat())
1000
def migrate_ancient_formats(repo_transport):
1001
# Migrate older cache formats
1002
repo_transport = remove_readonly_transport_decorator(repo_transport)
1003
has_sqlite = repo_transport.has("git.db")
1004
has_tdb = repo_transport.has("git.tdb")
1005
if not has_sqlite or has_tdb:
1008
repo_transport.mkdir("git")
1009
except bzr_errors.FileExists:
1011
# Prefer migrating git.db over git.tdb, since the latter may not
1012
# be openable on some platforms.
1014
SqliteGitCacheFormat().initialize(repo_transport.clone("git"))
1015
repo_transport.rename("git.db", "git/idmap.db")
1017
TdbGitCacheFormat().initialize(repo_transport.clone("git"))
1018
repo_transport.rename("git.tdb", "git/idmap.tdb")
1021
def remove_readonly_transport_decorator(transport):
1022
if transport.is_readonly():
1024
return transport._decorated
1025
except AttributeError:
1026
raise bzr_errors.ReadOnlyError(transport)
1030
def from_repository(repository):
1031
"""Open a cache file for a repository.
1033
If the repository is remote and there is no transport available from it
1034
this will use a local file in the users cache directory
1035
(typically ~/.cache/bazaar/git/)
1037
:param repository: A repository object
1039
repo_transport = getattr(repository, "_transport", None)
1040
if repo_transport is not None:
1042
migrate_ancient_formats(repo_transport)
1043
except bzr_errors.ReadOnlyError:
1044
pass # Not much we can do
1045
return BzrGitCacheFormat.from_repository(repository)