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.
224
repo_transport = remove_readonly_transport_decorator(repo_transport)
226
repo_transport.mkdir('git')
227
except bzrlib.errors.FileExists:
229
transport = repo_transport.clone('git')
231
transport = get_remote_cache_transport(repository)
232
return cls.from_transport(transport)
235
class CacheUpdater(object):
236
"""Base class for objects that can update a bzr-git cache."""
238
def add_object(self, obj, ie, path):
241
:param obj: Object type ("commit", "blob" or "tree")
242
:param ie: Inventory entry (for blob/tree) or testament_sha in case
244
:param path: Path of the object (optional)
246
raise NotImplementedError(self.add_object)
249
raise NotImplementedError(self.finish)
252
class BzrGitCache(object):
253
"""Caching backend."""
255
def __init__(self, idmap, content_cache, cache_updater_klass):
257
self.content_cache = content_cache
258
self._cache_updater_klass = cache_updater_klass
260
def get_updater(self, rev):
261
"""Update an object that implements the CacheUpdater interface for
264
return self._cache_updater_klass(self, rev)
267
DictBzrGitCache = lambda: BzrGitCache(DictGitShaMap(), None, DictCacheUpdater)
270
class DictCacheUpdater(CacheUpdater):
271
"""Cache updater for dict-based caches."""
273
def __init__(self, cache, rev):
275
self.revid = rev.revision_id
276
self.parent_revids = rev.parent_ids
280
def add_object(self, obj, ie, path):
281
if obj.type_name == "commit":
283
assert type(ie) is dict
285
type_data = (self.revid, self._commit.tree, ie)
286
self.cache.idmap._by_revid[self.revid] = obj.id
287
elif obj.type_name in ("blob", "tree"):
289
if obj.type_name == "blob":
290
revision = ie.revision
292
revision = self.revid
293
key = type_data = (ie.file_id, revision)
294
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] = obj.id
297
entry = (obj.type_name, type_data)
298
self.cache.idmap._by_sha.setdefault(obj.id, {})[key] = entry
301
if self._commit is None:
302
raise AssertionError("No commit object added")
306
class DictGitShaMap(GitShaMap):
307
"""Git SHA map that uses a dictionary."""
314
def lookup_blob_id(self, fileid, revision):
315
return self._by_fileid[revision][fileid]
317
def lookup_git_sha(self, sha):
318
for entry in self._by_sha[sha].itervalues():
321
def lookup_tree_id(self, fileid, revision):
322
return self._by_fileid[revision][fileid]
324
def lookup_commit(self, revid):
325
return self._by_revid[revid]
328
for key, entries in self._by_sha.iteritems():
329
for (type, type_data) in entries.values():
334
return self._by_sha.iterkeys()
337
class SqliteCacheUpdater(CacheUpdater):
339
def __init__(self, cache, rev):
341
self.db = self.cache.idmap.db
342
self.revid = rev.revision_id
347
def add_object(self, obj, ie, path):
348
if obj.type_name == "commit":
350
self._testament3_sha1 = ie.get("testament3-sha1")
351
assert type(ie) is dict
352
elif obj.type_name == "tree":
354
self._trees.append((obj.id, ie.file_id, self.revid))
355
elif obj.type_name == "blob":
357
self._blobs.append((obj.id, ie.file_id, ie.revision))
362
if self._commit is None:
363
raise AssertionError("No commit object added")
365
"replace into trees (sha1, fileid, revid) values (?, ?, ?)",
368
"replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
371
"replace into commits (sha1, revid, tree_sha, testament3_sha1) values (?, ?, ?, ?)",
372
(self._commit.id, self.revid, self._commit.tree, self._testament3_sha1))
376
SqliteBzrGitCache = lambda p: BzrGitCache(SqliteGitShaMap(p), None, SqliteCacheUpdater)
379
class SqliteGitCacheFormat(BzrGitCacheFormat):
381
def get_format_string(self):
382
return 'bzr-git sha map version 1 using sqlite\n'
384
def open(self, transport):
386
basepath = transport.local_abspath(".")
387
except bzrlib.errors.NotLocalUrl:
388
basepath = get_cache_dir()
389
return SqliteBzrGitCache(os.path.join(basepath, "idmap.db"))
392
class SqliteGitShaMap(GitShaMap):
393
"""Bazaar GIT Sha map that uses a sqlite database for storage."""
395
def __init__(self, path=None):
398
self.db = sqlite3.connect(":memory:")
400
if not mapdbs().has_key(path):
401
mapdbs()[path] = sqlite3.connect(path)
402
self.db = mapdbs()[path]
403
self.db.text_factory = str
404
self.db.executescript("""
405
create table if not exists commits(
406
sha1 text not null check(length(sha1) == 40),
408
tree_sha text not null check(length(tree_sha) == 40)
410
create index if not exists commit_sha1 on commits(sha1);
411
create unique index if not exists commit_revid on commits(revid);
412
create table if not exists blobs(
413
sha1 text not null check(length(sha1) == 40),
414
fileid text not null,
417
create index if not exists blobs_sha1 on blobs(sha1);
418
create unique index if not exists blobs_fileid_revid on blobs(fileid, revid);
419
create table if not exists trees(
420
sha1 text unique not null check(length(sha1) == 40),
421
fileid text not null,
424
create unique index if not exists trees_sha1 on trees(sha1);
425
create unique index if not exists trees_fileid_revid on trees(fileid, revid);
428
self.db.executescript(
429
"ALTER TABLE commits ADD testament3_sha1 TEXT;")
430
except sqlite3.OperationalError:
431
pass # Column already exists.
434
return "%s(%r)" % (self.__class__.__name__, self.path)
436
def lookup_commit(self, revid):
437
cursor = self.db.execute("select sha1 from commits where revid = ?",
439
row = cursor.fetchone()
444
def commit_write_group(self):
447
def lookup_blob_id(self, fileid, revision):
448
row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revision)).fetchone()
451
raise KeyError(fileid)
453
def lookup_tree_id(self, fileid, revision):
454
row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid, revision)).fetchone()
457
raise KeyError(fileid)
459
def lookup_git_sha(self, sha):
460
"""Lookup a Git sha in the database.
462
:param sha: Git object sha
463
:return: (type, type_data) with type_data:
464
commit: revid, tree sha, verifiers
469
cursor = self.db.execute("select revid, tree_sha, testament3_sha1 from commits where sha1 = ?", (sha,))
470
for row in cursor.fetchall():
472
if row[2] is not None:
473
verifiers = {"testament3-sha1": row[2]}
476
yield ("commit", (row[0], row[1], verifiers))
477
cursor = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,))
478
for row in cursor.fetchall():
481
cursor = self.db.execute("select fileid, revid from trees where sha1 = ?", (sha,))
482
for row in cursor.fetchall():
489
"""List the revision ids known."""
490
return (row for (row,) in self.db.execute("select revid from commits"))
493
"""List the SHA1s."""
494
for table in ("blobs", "commits", "trees"):
495
for (sha,) in self.db.execute("select sha1 from %s" % table):
499
class TdbCacheUpdater(CacheUpdater):
500
"""Cache updater for tdb-based caches."""
502
def __init__(self, cache, rev):
504
self.db = cache.idmap.db
505
self.revid = rev.revision_id
506
self.parent_revids = rev.parent_ids
510
def add_object(self, obj, ie, path):
511
sha = obj.sha().digest()
512
if obj.type_name == "commit":
513
self.db["commit\0" + self.revid] = "\0".join((sha, obj.tree))
514
assert type(ie) is dict, "was %r" % ie
515
type_data = (self.revid, obj.tree)
517
type_data += (ie["testament3-sha1"],)
521
elif obj.type_name == "blob":
524
self.db["\0".join(("blob", ie.file_id, ie.revision))] = sha
525
type_data = (ie.file_id, ie.revision)
526
elif obj.type_name == "tree":
529
type_data = (ie.file_id, self.revid)
532
entry = "\0".join((obj.type_name, ) + type_data) + "\n"
535
oldval = self.db[key]
539
if oldval[-1] != "\n":
540
self.db[key] = "".join([oldval, "\n", entry])
542
self.db[key] = "".join([oldval, entry])
545
if self._commit is None:
546
raise AssertionError("No commit object added")
550
TdbBzrGitCache = lambda p: BzrGitCache(TdbGitShaMap(p), None, TdbCacheUpdater)
553
class TdbGitCacheFormat(BzrGitCacheFormat):
554
"""Cache format for tdb-based caches."""
556
def get_format_string(self):
557
return 'bzr-git sha map version 3 using tdb\n'
559
def open(self, transport):
561
basepath = transport.local_abspath(".").encode(osutils._fs_enc)
562
except bzrlib.errors.NotLocalUrl:
563
basepath = get_cache_dir()
564
assert isinstance(basepath, str)
566
return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
569
"Unable to open existing bzr-git cache because 'tdb' is not "
573
class TdbGitShaMap(GitShaMap):
574
"""SHA Map that uses a TDB database.
578
"git <sha1>" -> "<type> <type-data1> <type-data2>"
579
"commit revid" -> "<sha1> <tree-id>"
580
"tree fileid revid" -> "<sha1>"
581
"blob fileid revid" -> "<sha1>"
585
TDB_HASH_SIZE = 50000
587
def __init__(self, path=None):
593
assert isinstance(path, str)
594
if not mapdbs().has_key(path):
595
mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
596
os.O_RDWR|os.O_CREAT)
597
self.db = mapdbs()[path]
599
if int(self.db["version"]) not in (2, 3):
600
trace.warning("SHA Map is incompatible (%s -> %d), rebuilding database.",
601
self.db["version"], self.TDB_MAP_VERSION)
605
self.db["version"] = str(self.TDB_MAP_VERSION)
607
def start_write_group(self):
608
"""Start writing changes."""
609
self.db.transaction_start()
611
def commit_write_group(self):
612
"""Commit any pending changes."""
613
self.db.transaction_commit()
615
def abort_write_group(self):
616
"""Abort any pending changes."""
617
self.db.transaction_cancel()
620
return "%s(%r)" % (self.__class__.__name__, self.path)
622
def lookup_commit(self, revid):
624
return sha_to_hex(self.db["commit\0" + revid][:20])
626
raise KeyError("No cache entry for %r" % revid)
628
def lookup_blob_id(self, fileid, revision):
629
return sha_to_hex(self.db["\0".join(("blob", fileid, revision))])
631
def lookup_git_sha(self, sha):
632
"""Lookup a Git sha in the database.
634
:param sha: Git object sha
635
:return: (type, type_data) with type_data:
636
commit: revid, tree sha
641
sha = hex_to_sha(sha)
642
value = self.db["git\0" + sha]
643
for data in value.splitlines():
644
data = data.split("\0")
645
if data[0] == "commit":
647
yield (data[0], (data[1], data[2], {}))
649
yield (data[0], (data[1], data[2], {"testament3-sha1": data[3]}))
650
elif data[0] in ("tree", "blob"):
651
yield (data[0], tuple(data[1:]))
653
raise AssertionError("unknown type %r" % data[0])
655
def missing_revisions(self, revids):
658
if self.db.get("commit\0" + revid) is None:
663
"""List the revision ids known."""
664
for key in self.db.iterkeys():
665
if key.startswith("commit\0"):
669
"""List the SHA1s."""
670
for key in self.db.iterkeys():
671
if key.startswith("git\0"):
672
yield sha_to_hex(key[4:])
675
class VersionedFilesContentCache(ContentCache):
677
def __init__(self, vf):
681
self._vf.insert_record_stream(
682
[versionedfile.ChunkedContentFactory((obj.id,), [], None,
683
obj.as_legacy_object_chunks())])
685
def __getitem__(self, sha):
686
stream = self._vf.get_record_stream([(sha,)], 'unordered', True)
687
entry = stream.next()
688
if entry.storage_kind == 'absent':
690
return ShaFile._parse_legacy_object(entry.get_bytes_as('fulltext'))
693
class GitObjectStoreContentCache(ContentCache):
695
def __init__(self, store):
698
def add_multi(self, objs):
699
self.store.add_objects(objs)
701
def add(self, obj, path):
702
self.store.add_object(obj)
704
def __getitem__(self, sha):
705
return self.store[sha]
708
class IndexCacheUpdater(CacheUpdater):
710
def __init__(self, cache, rev):
712
self.revid = rev.revision_id
713
self.parent_revids = rev.parent_ids
716
self._cache_objs = set()
718
def add_object(self, obj, ie, path):
719
if obj.type_name == "commit":
721
assert type(ie) is dict
722
self.cache.idmap._add_git_sha(obj.id, "commit",
723
(self.revid, obj.tree, ie))
724
self.cache.idmap._add_node(("commit", self.revid, "X"),
725
" ".join((obj.id, obj.tree)))
726
self._cache_objs.add((obj, path))
727
elif obj.type_name == "blob":
728
self.cache.idmap._add_git_sha(obj.id, "blob",
729
(ie.file_id, ie.revision))
730
self.cache.idmap._add_node(("blob", ie.file_id, ie.revision), obj.id)
731
if ie.kind == "symlink":
732
self._cache_objs.add((obj, path))
733
elif obj.type_name == "tree":
734
self.cache.idmap._add_git_sha(obj.id, "tree",
735
(ie.file_id, self.revid))
736
self._cache_objs.add((obj, path))
741
self.cache.content_cache.add_multi(self._cache_objs)
745
class IndexBzrGitCache(BzrGitCache):
747
def __init__(self, transport=None):
748
mapper = versionedfile.ConstantMapper("trees")
749
shamap = IndexGitShaMap(transport.clone('index'))
750
#trees_store = knit.make_file_factory(True, mapper)(transport)
751
#content_cache = VersionedFilesContentCache(trees_store)
752
from bzrlib.plugins.git.transportgit import TransportObjectStore
753
store = TransportObjectStore(transport.clone('objects'))
754
content_cache = GitObjectStoreContentCache(store)
755
super(IndexBzrGitCache, self).__init__(shamap, content_cache,
759
class IndexGitCacheFormat(BzrGitCacheFormat):
761
def get_format_string(self):
762
return 'bzr-git sha map with git object cache version 1\n'
764
def initialize(self, transport):
765
super(IndexGitCacheFormat, self).initialize(transport)
766
transport.mkdir('index')
767
transport.mkdir('objects')
768
from bzrlib.plugins.git.transportgit import TransportObjectStore
769
TransportObjectStore.init(transport.clone('objects'))
771
def open(self, transport):
772
return IndexBzrGitCache(transport)
775
class IndexGitShaMap(GitShaMap):
776
"""SHA Map that uses the Bazaar APIs to store a cache.
778
BTree Index file with the following contents:
780
("git", <sha1>) -> "<type> <type-data1> <type-data2>"
781
("commit", <revid>) -> "<sha1> <tree-id>"
782
("blob", <fileid>, <revid>) -> <sha1>
786
def __init__(self, transport=None):
787
if transport is None:
788
self._transport = None
789
self._index = _mod_index.InMemoryGraphIndex(0, key_elements=3)
790
self._builder = self._index
793
self._transport = transport
794
self._index = _mod_index.CombinedGraphIndex([])
795
for name in self._transport.list_dir("."):
796
if not name.endswith(".rix"):
798
x = _mod_btree_index.BTreeGraphIndex(self._transport, name,
799
self._transport.stat(name).st_size)
800
self._index.insert_index(0, x)
803
def from_repository(cls, repository):
804
transport = getattr(repository, "_transport", None)
805
if transport is not None:
807
transport.mkdir('git')
808
except bzrlib.errors.FileExists:
810
return cls(transport.clone('git'))
811
from bzrlib.transport import get_transport
812
return cls(get_transport(get_cache_dir()))
815
if self._transport is not None:
816
return "%s(%r)" % (self.__class__.__name__, self._transport.base)
818
return "%s()" % (self.__class__.__name__)
821
assert self._builder is None
822
self.start_write_group()
823
for _, key, value in self._index.iter_all_entries():
824
self._builder.add_node(key, value)
826
for name in self._transport.list_dir('.'):
827
if name.endswith('.rix'):
828
to_remove.append(name)
829
self.commit_write_group()
830
del self._index.indices[1:]
831
for name in to_remove:
832
self._transport.rename(name, name + '.old')
834
def start_write_group(self):
835
assert self._builder is None
836
self._builder = _mod_btree_index.BTreeBuilder(0, key_elements=3)
837
self._name = osutils.sha()
839
def commit_write_group(self):
840
assert self._builder is not None
841
stream = self._builder.finish()
842
name = self._name.hexdigest() + ".rix"
843
size = self._transport.put_file(name, stream)
844
index = _mod_btree_index.BTreeGraphIndex(self._transport, name, size)
845
self._index.insert_index(0, index)
849
def abort_write_group(self):
850
assert self._builder is not None
854
def _add_node(self, key, value):
856
self._builder.add_node(key, value)
857
except bzrlib.errors.BadIndexDuplicateKey:
858
# Multiple bzr objects can have the same contents
863
def _get_entry(self, key):
864
entries = self._index.iter_entries([key])
866
return entries.next()[2]
867
except StopIteration:
868
if self._builder is None:
870
entries = self._builder.iter_entries([key])
872
return entries.next()[2]
873
except StopIteration:
876
def _iter_entries_prefix(self, prefix):
877
for entry in self._index.iter_entries_prefix([prefix]):
878
yield (entry[1], entry[2])
879
if self._builder is not None:
880
for entry in self._builder.iter_entries_prefix([prefix]):
881
yield (entry[1], entry[2])
883
def lookup_commit(self, revid):
884
return self._get_entry(("commit", revid, "X"))[:40]
886
def _add_git_sha(self, hexsha, type, type_data):
887
if hexsha is not None:
888
self._name.update(hexsha)
890
td = (type_data[0], type_data[1])
892
td += (type_data[2]["testament3-sha1"],)
897
self._add_node(("git", hexsha, "X"), " ".join((type,) + td))
899
# This object is not represented in Git - perhaps an empty
901
self._name.update(type + " ".join(type_data))
903
def lookup_blob_id(self, fileid, revision):
904
return self._get_entry(("blob", fileid, revision))
906
def lookup_git_sha(self, sha):
908
sha = sha_to_hex(sha)
910
for key, value in self._iter_entries_prefix(("git", sha, None)):
912
data = value.split(" ", 3)
913
if data[0] == "commit":
915
verifiers = {"testament3-sha1": data[3]}
918
yield ("commit", (data[1], data[2], verifiers))
920
yield (data[0], tuple(data[1:]))
925
"""List the revision ids known."""
926
for key, value in self._iter_entries_prefix(("commit", None, None)):
929
def missing_revisions(self, revids):
930
"""Return set of all the revisions that are not present."""
931
missing_revids = set(revids)
932
for _, key, value in self._index.iter_entries((
933
("commit", revid, "X") for revid in revids)):
934
missing_revids.remove(key[1])
935
return missing_revids
938
"""List the SHA1s."""
939
for key, value in self._iter_entries_prefix(("git", None, None)):
943
formats = registry.Registry()
944
formats.register(TdbGitCacheFormat().get_format_string(),
946
formats.register(SqliteGitCacheFormat().get_format_string(),
947
SqliteGitCacheFormat())
948
formats.register(IndexGitCacheFormat().get_format_string(),
949
IndexGitCacheFormat())
950
# In the future, this will become the default:
951
# formats.register('default', IndexGitCacheFormat())
955
formats.register('default', SqliteGitCacheFormat())
957
formats.register('default', TdbGitCacheFormat())
961
def migrate_ancient_formats(repo_transport):
962
# Migrate older cache formats
963
repo_transport = remove_readonly_transport_decorator(repo_transport)
964
has_sqlite = repo_transport.has("git.db")
965
has_tdb = repo_transport.has("git.tdb")
966
if not has_sqlite or has_tdb:
969
repo_transport.mkdir("git")
970
except bzrlib.errors.FileExists:
972
# Prefer migrating git.db over git.tdb, since the latter may not
973
# be openable on some platforms.
975
SqliteGitCacheFormat().initialize(repo_transport.clone("git"))
976
repo_transport.rename("git.db", "git/idmap.db")
978
TdbGitCacheFormat().initialize(repo_transport.clone("git"))
979
repo_transport.rename("git.tdb", "git/idmap.tdb")
982
def remove_readonly_transport_decorator(transport):
983
if transport.is_readonly():
984
return transport._decorated
988
def from_repository(repository):
989
"""Open a cache file for a repository.
991
If the repository is remote and there is no transport available from it
992
this will use a local file in the users cache directory
993
(typically ~/.cache/bazaar/git/)
995
:param repository: A repository object
997
repo_transport = getattr(repository, "_transport", None)
998
if repo_transport is not None:
999
migrate_ancient_formats(repo_transport)
1000
return BzrGitCacheFormat.from_repository(repository)