1
# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
"""Map from Git sha's to Bazaar objects."""
19
from dulwich.objects import (
26
from dulwich.objects import (
32
btree_index as _mod_btree_index,
39
from bzrlib.transport import (
46
from xdg.BaseDirectory import xdg_cache_home
48
from bzrlib.config import config_dir
49
ret = os.path.join(config_dir(), "git")
51
ret = os.path.join(xdg_cache_home, "bazaar", "git")
52
if not os.path.isdir(ret):
57
def get_remote_cache_transport(repository):
58
"""Retrieve the transport to use when accessing (unwritable) remote
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)
71
def check_pysqlite_version(sqlite3):
72
"""Check that sqlite library is compatible.
75
if (sqlite3.sqlite_version_info[0] < 3 or
76
(sqlite3.sqlite_version_info[0] == 3 and
77
sqlite3.sqlite_version_info[1] < 3)):
78
trace.warning('Needs at least sqlite 3.3.x')
79
raise bzrlib.errors.BzrError("incompatible sqlite library")
84
check_pysqlite_version(sqlite3)
85
except (ImportError, bzrlib.errors.BzrError), e:
86
from pysqlite2 import dbapi2 as sqlite3
87
check_pysqlite_version(sqlite3)
89
trace.warning('Needs at least Python2.5 or Python2.4 with the pysqlite2 '
91
raise bzrlib.errors.BzrError("missing sqlite library")
94
_mapdbs = threading.local()
96
"""Get a cache for this thread's db connections."""
99
except AttributeError:
104
class GitShaMap(object):
105
"""Git<->Bzr revision id mapping database."""
107
def lookup_git_sha(self, sha):
108
"""Lookup a Git sha in the database.
109
:param sha: Git object sha
110
:return: list with (type, type_data) tuples with type_data:
111
commit: revid, tree_sha, verifiers
115
raise NotImplementedError(self.lookup_git_sha)
117
def lookup_blob_id(self, file_id, revision):
118
"""Retrieve a Git blob SHA by file id.
120
:param file_id: File id of the file/symlink
121
:param revision: revision in which the file was last changed.
123
raise NotImplementedError(self.lookup_blob_id)
125
def lookup_tree_id(self, file_id, revision):
126
"""Retrieve a Git tree SHA by file id.
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)
136
"""List the revision ids known."""
137
raise NotImplementedError(self.revids)
139
def missing_revisions(self, revids):
140
"""Return set of all the revisions that are not present."""
141
present_revids = set(self.revids())
142
if not isinstance(revids, set):
144
return revids - present_revids
147
"""List the SHA1s."""
148
raise NotImplementedError(self.sha1s)
150
def start_write_group(self):
151
"""Start writing changes."""
153
def commit_write_group(self):
154
"""Commit any pending changes."""
156
def abort_write_group(self):
157
"""Abort any pending changes."""
160
class ContentCache(object):
161
"""Object that can cache Git objects."""
163
def add(self, object):
165
raise NotImplementedError(self.add)
167
def add_multi(self, objects):
168
"""Add multiple objects."""
172
def __getitem__(self, sha):
173
"""Retrieve an item, by SHA."""
174
raise NotImplementedError(self.__getitem__)
177
class BzrGitCacheFormat(object):
178
"""Bazaar-Git Cache Format."""
180
def get_format_string(self):
181
"""Return a single-line unique format string for this cache format."""
182
raise NotImplementedError(self.get_format_string)
184
def open(self, transport):
185
"""Open this format on a transport."""
186
raise NotImplementedError(self.open)
188
def initialize(self, transport):
189
"""Create a new instance of this cache format at transport."""
190
transport.put_bytes('format', self.get_format_string())
193
def from_transport(self, transport):
194
"""Open a cache file present on a transport, or initialize one.
196
:param transport: Transport to use
197
:return: A BzrGitCache instance
200
format_name = transport.get_bytes('format')
201
format = formats.get(format_name)
202
except bzrlib.errors.NoSuchFile:
203
format = formats.get('default')
204
format.initialize(transport)
205
return format.open(transport)
208
def from_repository(cls, repository):
209
"""Open a cache file for a repository.
211
This will use the repository's transport to store the cache file, or
212
use the users global cache directory if the repository has no
213
transport associated with it.
215
:param repository: Repository to open the cache for
216
:return: A `BzrGitCache`
218
from bzrlib.transport.local import LocalTransport
219
repo_transport = getattr(repository, "_transport", None)
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
223
# to update its cache.
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')
236
if transport is None:
237
transport = get_remote_cache_transport(repository)
238
return cls.from_transport(transport)
241
class CacheUpdater(object):
242
"""Base class for objects that can update a bzr-git cache."""
244
def add_object(self, obj, ie, path):
247
:param obj: Object type ("commit", "blob" or "tree")
248
:param ie: Inventory entry (for blob/tree) or testament_sha in case
250
:param path: Path of the object (optional)
252
raise NotImplementedError(self.add_object)
255
raise NotImplementedError(self.finish)
258
class BzrGitCache(object):
259
"""Caching backend."""
261
def __init__(self, idmap, content_cache, cache_updater_klass):
263
self.content_cache = content_cache
264
self._cache_updater_klass = cache_updater_klass
266
def get_updater(self, rev):
267
"""Update an object that implements the CacheUpdater interface for
270
return self._cache_updater_klass(self, rev)
273
DictBzrGitCache = lambda: BzrGitCache(DictGitShaMap(), None, DictCacheUpdater)
276
class DictCacheUpdater(CacheUpdater):
277
"""Cache updater for dict-based caches."""
279
def __init__(self, cache, rev):
281
self.revid = rev.revision_id
282
self.parent_revids = rev.parent_ids
286
def add_object(self, obj, ie, path):
287
if obj.type_name == "commit":
289
assert type(ie) is dict
291
type_data = (self.revid, self._commit.tree, ie)
292
self.cache.idmap._by_revid[self.revid] = obj.id
293
elif obj.type_name in ("blob", "tree"):
295
if obj.type_name == "blob":
296
revision = ie.revision
298
revision = self.revid
299
key = type_data = (ie.file_id, revision)
300
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] = obj.id
303
entry = (obj.type_name, type_data)
304
self.cache.idmap._by_sha.setdefault(obj.id, {})[key] = entry
307
if self._commit is None:
308
raise AssertionError("No commit object added")
312
class DictGitShaMap(GitShaMap):
313
"""Git SHA map that uses a dictionary."""
320
def lookup_blob_id(self, fileid, revision):
321
return self._by_fileid[revision][fileid]
323
def lookup_git_sha(self, sha):
324
for entry in self._by_sha[sha].itervalues():
327
def lookup_tree_id(self, fileid, revision):
328
return self._by_fileid[revision][fileid]
330
def lookup_commit(self, revid):
331
return self._by_revid[revid]
334
for key, entries in self._by_sha.iteritems():
335
for (type, type_data) in entries.values():
340
return self._by_sha.iterkeys()
343
class SqliteCacheUpdater(CacheUpdater):
345
def __init__(self, cache, rev):
347
self.db = self.cache.idmap.db
348
self.revid = rev.revision_id
353
def add_object(self, obj, ie, path):
354
if obj.type_name == "commit":
356
self._testament3_sha1 = ie.get("testament3-sha1")
357
assert type(ie) is dict
358
elif obj.type_name == "tree":
360
self._trees.append((obj.id, ie.file_id, self.revid))
361
elif obj.type_name == "blob":
363
self._blobs.append((obj.id, ie.file_id, ie.revision))
368
if self._commit is None:
369
raise AssertionError("No commit object added")
371
"replace into trees (sha1, fileid, revid) values (?, ?, ?)",
374
"replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
377
"replace into commits (sha1, revid, tree_sha, testament3_sha1) values (?, ?, ?, ?)",
378
(self._commit.id, self.revid, self._commit.tree, self._testament3_sha1))
382
SqliteBzrGitCache = lambda p: BzrGitCache(SqliteGitShaMap(p), None, SqliteCacheUpdater)
385
class SqliteGitCacheFormat(BzrGitCacheFormat):
387
def get_format_string(self):
388
return 'bzr-git sha map version 1 using sqlite\n'
390
def open(self, transport):
392
basepath = transport.local_abspath(".")
393
except bzrlib.errors.NotLocalUrl:
394
basepath = get_cache_dir()
395
return SqliteBzrGitCache(os.path.join(basepath, "idmap.db"))
398
class SqliteGitShaMap(GitShaMap):
399
"""Bazaar GIT Sha map that uses a sqlite database for storage."""
401
def __init__(self, path=None):
404
self.db = sqlite3.connect(":memory:")
406
if not mapdbs().has_key(path):
407
mapdbs()[path] = sqlite3.connect(path)
408
self.db = mapdbs()[path]
409
self.db.text_factory = str
410
self.db.executescript("""
411
create table if not exists commits(
412
sha1 text not null check(length(sha1) == 40),
414
tree_sha text not null check(length(tree_sha) == 40)
416
create index if not exists commit_sha1 on commits(sha1);
417
create unique index if not exists commit_revid on commits(revid);
418
create table if not exists blobs(
419
sha1 text not null check(length(sha1) == 40),
420
fileid text not null,
423
create index if not exists blobs_sha1 on blobs(sha1);
424
create unique index if not exists blobs_fileid_revid on blobs(fileid, revid);
425
create table if not exists trees(
426
sha1 text unique not null check(length(sha1) == 40),
427
fileid text not null,
430
create unique index if not exists trees_sha1 on trees(sha1);
431
create unique index if not exists trees_fileid_revid on trees(fileid, revid);
434
self.db.executescript(
435
"ALTER TABLE commits ADD testament3_sha1 TEXT;")
436
except sqlite3.OperationalError:
437
pass # Column already exists.
440
return "%s(%r)" % (self.__class__.__name__, self.path)
442
def lookup_commit(self, revid):
443
cursor = self.db.execute("select sha1 from commits where revid = ?",
445
row = cursor.fetchone()
450
def commit_write_group(self):
453
def lookup_blob_id(self, fileid, revision):
454
row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revision)).fetchone()
457
raise KeyError(fileid)
459
def lookup_tree_id(self, fileid, revision):
460
row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid, revision)).fetchone()
463
raise KeyError(fileid)
465
def lookup_git_sha(self, sha):
466
"""Lookup a Git sha in the database.
468
:param sha: Git object sha
469
:return: (type, type_data) with type_data:
470
commit: revid, tree sha, verifiers
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():
495
"""List the revision ids known."""
496
return (row for (row,) in self.db.execute("select revid from commits"))
499
"""List the SHA1s."""
500
for table in ("blobs", "commits", "trees"):
501
for (sha,) in self.db.execute("select sha1 from %s" % table):
505
class TdbCacheUpdater(CacheUpdater):
506
"""Cache updater for tdb-based caches."""
508
def __init__(self, cache, rev):
510
self.db = cache.idmap.db
511
self.revid = rev.revision_id
512
self.parent_revids = rev.parent_ids
516
def add_object(self, obj, ie, path):
517
sha = obj.sha().digest()
518
if obj.type_name == "commit":
519
self.db["commit\0" + self.revid] = "\0".join((sha, obj.tree))
520
assert type(ie) is dict, "was %r" % ie
521
type_data = (self.revid, obj.tree)
523
type_data += (ie["testament3-sha1"],)
527
elif obj.type_name == "blob":
530
self.db["\0".join(("blob", ie.file_id, ie.revision))] = sha
531
type_data = (ie.file_id, ie.revision)
532
elif obj.type_name == "tree":
535
type_data = (ie.file_id, self.revid)
538
entry = "\0".join((obj.type_name, ) + type_data) + "\n"
541
oldval = self.db[key]
545
if oldval[-1] != "\n":
546
self.db[key] = "".join([oldval, "\n", entry])
548
self.db[key] = "".join([oldval, entry])
551
if self._commit is None:
552
raise AssertionError("No commit object added")
556
TdbBzrGitCache = lambda p: BzrGitCache(TdbGitShaMap(p), None, TdbCacheUpdater)
559
class TdbGitCacheFormat(BzrGitCacheFormat):
560
"""Cache format for tdb-based caches."""
562
def get_format_string(self):
563
return 'bzr-git sha map version 3 using tdb\n'
565
def open(self, transport):
567
basepath = transport.local_abspath(".").encode(osutils._fs_enc)
568
except bzrlib.errors.NotLocalUrl:
569
basepath = get_cache_dir()
570
assert isinstance(basepath, str)
572
return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
575
"Unable to open existing bzr-git cache because 'tdb' is not "
579
class TdbGitShaMap(GitShaMap):
580
"""SHA Map that uses a TDB database.
584
"git <sha1>" -> "<type> <type-data1> <type-data2>"
585
"commit revid" -> "<sha1> <tree-id>"
586
"tree fileid revid" -> "<sha1>"
587
"blob fileid revid" -> "<sha1>"
591
TDB_HASH_SIZE = 50000
593
def __init__(self, path=None):
599
assert isinstance(path, str)
600
if not mapdbs().has_key(path):
601
mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
602
os.O_RDWR|os.O_CREAT)
603
self.db = mapdbs()[path]
605
if int(self.db["version"]) not in (2, 3):
606
trace.warning("SHA Map is incompatible (%s -> %d), rebuilding database.",
607
self.db["version"], self.TDB_MAP_VERSION)
611
self.db["version"] = str(self.TDB_MAP_VERSION)
613
def start_write_group(self):
614
"""Start writing changes."""
615
self.db.transaction_start()
617
def commit_write_group(self):
618
"""Commit any pending changes."""
619
self.db.transaction_commit()
621
def abort_write_group(self):
622
"""Abort any pending changes."""
623
self.db.transaction_cancel()
626
return "%s(%r)" % (self.__class__.__name__, self.path)
628
def lookup_commit(self, revid):
630
return sha_to_hex(self.db["commit\0" + revid][:20])
632
raise KeyError("No cache entry for %r" % revid)
634
def lookup_blob_id(self, fileid, revision):
635
return sha_to_hex(self.db["\0".join(("blob", fileid, revision))])
637
def lookup_git_sha(self, sha):
638
"""Lookup a Git sha in the database.
640
:param sha: Git object sha
641
:return: (type, type_data) with type_data:
642
commit: revid, tree sha
647
sha = hex_to_sha(sha)
648
value = self.db["git\0" + sha]
649
for data in value.splitlines():
650
data = data.split("\0")
651
if data[0] == "commit":
653
yield (data[0], (data[1], data[2], {}))
655
yield (data[0], (data[1], data[2], {"testament3-sha1": data[3]}))
656
elif data[0] in ("tree", "blob"):
657
yield (data[0], tuple(data[1:]))
659
raise AssertionError("unknown type %r" % data[0])
661
def missing_revisions(self, revids):
664
if self.db.get("commit\0" + revid) is None:
669
"""List the revision ids known."""
670
for key in self.db.iterkeys():
671
if key.startswith("commit\0"):
675
"""List the SHA1s."""
676
for key in self.db.iterkeys():
677
if key.startswith("git\0"):
678
yield sha_to_hex(key[4:])
681
class VersionedFilesContentCache(ContentCache):
683
def __init__(self, vf):
687
self._vf.insert_record_stream(
688
[versionedfile.ChunkedContentFactory((obj.id,), [], None,
689
obj.as_legacy_object_chunks())])
691
def __getitem__(self, sha):
692
stream = self._vf.get_record_stream([(sha,)], 'unordered', True)
693
entry = stream.next()
694
if entry.storage_kind == 'absent':
696
return ShaFile._parse_legacy_object(entry.get_bytes_as('fulltext'))
699
class GitObjectStoreContentCache(ContentCache):
701
def __init__(self, store):
704
def add_multi(self, objs):
705
self.store.add_objects(objs)
707
def add(self, obj, path):
708
self.store.add_object(obj)
710
def __getitem__(self, sha):
711
return self.store[sha]
714
class IndexCacheUpdater(CacheUpdater):
716
def __init__(self, cache, rev):
718
self.revid = rev.revision_id
719
self.parent_revids = rev.parent_ids
722
self._cache_objs = set()
724
def add_object(self, obj, ie, path):
725
if obj.type_name == "commit":
727
assert type(ie) is dict
728
self.cache.idmap._add_git_sha(obj.id, "commit",
729
(self.revid, obj.tree, ie))
730
self.cache.idmap._add_node(("commit", self.revid, "X"),
731
" ".join((obj.id, obj.tree)))
732
self._cache_objs.add((obj, path))
733
elif obj.type_name == "blob":
734
self.cache.idmap._add_git_sha(obj.id, "blob",
735
(ie.file_id, ie.revision))
736
self.cache.idmap._add_node(("blob", ie.file_id, ie.revision), obj.id)
737
if ie.kind == "symlink":
738
self._cache_objs.add((obj, path))
739
elif obj.type_name == "tree":
740
self.cache.idmap._add_git_sha(obj.id, "tree",
741
(ie.file_id, self.revid))
742
self._cache_objs.add((obj, path))
747
self.cache.content_cache.add_multi(self._cache_objs)
751
class IndexBzrGitCache(BzrGitCache):
753
def __init__(self, transport=None):
754
mapper = versionedfile.ConstantMapper("trees")
755
shamap = IndexGitShaMap(transport.clone('index'))
756
#trees_store = knit.make_file_factory(True, mapper)(transport)
757
#content_cache = VersionedFilesContentCache(trees_store)
758
from bzrlib.plugins.git.transportgit import TransportObjectStore
759
store = TransportObjectStore(transport.clone('objects'))
760
content_cache = GitObjectStoreContentCache(store)
761
super(IndexBzrGitCache, self).__init__(shamap, content_cache,
765
class IndexGitCacheFormat(BzrGitCacheFormat):
767
def get_format_string(self):
768
return 'bzr-git sha map with git object cache version 1\n'
770
def initialize(self, transport):
771
super(IndexGitCacheFormat, self).initialize(transport)
772
transport.mkdir('index')
773
transport.mkdir('objects')
774
from bzrlib.plugins.git.transportgit import TransportObjectStore
775
TransportObjectStore.init(transport.clone('objects'))
777
def open(self, transport):
778
return IndexBzrGitCache(transport)
781
class IndexGitShaMap(GitShaMap):
782
"""SHA Map that uses the Bazaar APIs to store a cache.
784
BTree Index file with the following contents:
786
("git", <sha1>) -> "<type> <type-data1> <type-data2>"
787
("commit", <revid>) -> "<sha1> <tree-id>"
788
("blob", <fileid>, <revid>) -> <sha1>
792
def __init__(self, transport=None):
793
if transport is None:
794
self._transport = None
795
self._index = _mod_index.InMemoryGraphIndex(0, key_elements=3)
796
self._builder = self._index
799
self._transport = transport
800
self._index = _mod_index.CombinedGraphIndex([])
801
for name in self._transport.list_dir("."):
802
if not name.endswith(".rix"):
804
x = _mod_btree_index.BTreeGraphIndex(self._transport, name,
805
self._transport.stat(name).st_size)
806
self._index.insert_index(0, x)
809
def from_repository(cls, repository):
810
transport = getattr(repository, "_transport", None)
811
if transport is not None:
813
transport.mkdir('git')
814
except bzrlib.errors.FileExists:
816
return cls(transport.clone('git'))
817
from bzrlib.transport import get_transport
818
return cls(get_transport(get_cache_dir()))
821
if self._transport is not None:
822
return "%s(%r)" % (self.__class__.__name__, self._transport.base)
824
return "%s()" % (self.__class__.__name__)
827
assert self._builder is None
828
self.start_write_group()
829
for _, key, value in self._index.iter_all_entries():
830
self._builder.add_node(key, value)
832
for name in self._transport.list_dir('.'):
833
if name.endswith('.rix'):
834
to_remove.append(name)
835
self.commit_write_group()
836
del self._index.indices[1:]
837
for name in to_remove:
838
self._transport.rename(name, name + '.old')
840
def start_write_group(self):
841
assert self._builder is None
842
self._builder = _mod_btree_index.BTreeBuilder(0, key_elements=3)
843
self._name = osutils.sha()
845
def commit_write_group(self):
846
assert self._builder is not None
847
stream = self._builder.finish()
848
name = self._name.hexdigest() + ".rix"
849
size = self._transport.put_file(name, stream)
850
index = _mod_btree_index.BTreeGraphIndex(self._transport, name, size)
851
self._index.insert_index(0, index)
855
def abort_write_group(self):
856
assert self._builder is not None
860
def _add_node(self, key, value):
862
self._builder.add_node(key, value)
863
except bzrlib.errors.BadIndexDuplicateKey:
864
# Multiple bzr objects can have the same contents
869
def _get_entry(self, key):
870
entries = self._index.iter_entries([key])
872
return entries.next()[2]
873
except StopIteration:
874
if self._builder is None:
876
entries = self._builder.iter_entries([key])
878
return entries.next()[2]
879
except StopIteration:
882
def _iter_entries_prefix(self, prefix):
883
for entry in self._index.iter_entries_prefix([prefix]):
884
yield (entry[1], entry[2])
885
if self._builder is not None:
886
for entry in self._builder.iter_entries_prefix([prefix]):
887
yield (entry[1], entry[2])
889
def lookup_commit(self, revid):
890
return self._get_entry(("commit", revid, "X"))[:40]
892
def _add_git_sha(self, hexsha, type, type_data):
893
if hexsha is not None:
894
self._name.update(hexsha)
896
td = (type_data[0], type_data[1])
898
td += (type_data[2]["testament3-sha1"],)
903
self._add_node(("git", hexsha, "X"), " ".join((type,) + td))
905
# This object is not represented in Git - perhaps an empty
907
self._name.update(type + " ".join(type_data))
909
def lookup_blob_id(self, fileid, revision):
910
return self._get_entry(("blob", fileid, revision))
912
def lookup_git_sha(self, sha):
914
sha = sha_to_hex(sha)
916
for key, value in self._iter_entries_prefix(("git", sha, None)):
918
data = value.split(" ", 3)
919
if data[0] == "commit":
921
verifiers = {"testament3-sha1": data[3]}
924
yield ("commit", (data[1], data[2], verifiers))
926
yield (data[0], tuple(data[1:]))
931
"""List the revision ids known."""
932
for key, value in self._iter_entries_prefix(("commit", None, None)):
935
def missing_revisions(self, revids):
936
"""Return set of all the revisions that are not present."""
937
missing_revids = set(revids)
938
for _, key, value in self._index.iter_entries((
939
("commit", revid, "X") for revid in revids)):
940
missing_revids.remove(key[1])
941
return missing_revids
944
"""List the SHA1s."""
945
for key, value in self._iter_entries_prefix(("git", None, None)):
949
formats = registry.Registry()
950
formats.register(TdbGitCacheFormat().get_format_string(),
952
formats.register(SqliteGitCacheFormat().get_format_string(),
953
SqliteGitCacheFormat())
954
formats.register(IndexGitCacheFormat().get_format_string(),
955
IndexGitCacheFormat())
956
# In the future, this will become the default:
957
# formats.register('default', IndexGitCacheFormat())
961
formats.register('default', SqliteGitCacheFormat())
963
formats.register('default', TdbGitCacheFormat())
967
def migrate_ancient_formats(repo_transport):
968
# Migrate older cache formats
969
repo_transport = remove_readonly_transport_decorator(repo_transport)
970
has_sqlite = repo_transport.has("git.db")
971
has_tdb = repo_transport.has("git.tdb")
972
if not has_sqlite or has_tdb:
975
repo_transport.mkdir("git")
976
except bzrlib.errors.FileExists:
978
# Prefer migrating git.db over git.tdb, since the latter may not
979
# be openable on some platforms.
981
SqliteGitCacheFormat().initialize(repo_transport.clone("git"))
982
repo_transport.rename("git.db", "git/idmap.db")
984
TdbGitCacheFormat().initialize(repo_transport.clone("git"))
985
repo_transport.rename("git.tdb", "git/idmap.tdb")
988
def remove_readonly_transport_decorator(transport):
989
if transport.is_readonly():
991
return transport._decorated
992
except AttributeError:
993
raise bzrlib.errors.ReadOnlyError(transport)
997
def from_repository(repository):
998
"""Open a cache file for a repository.
1000
If the repository is remote and there is no transport available from it
1001
this will use a local file in the users cache directory
1002
(typically ~/.cache/bazaar/git/)
1004
:param repository: A repository object
1006
repo_transport = getattr(repository, "_transport", None)
1007
if repo_transport is not None:
1009
migrate_ancient_formats(repo_transport)
1010
except bzrlib.errors.ReadOnlyError:
1011
pass # Not much we can do
1012
return BzrGitCacheFormat.from_repository(repository)