13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Map from Git sha's to Bazaar objects."""
21
from bzrlib.errors import NoSuchRevision
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 ..transport import (
45
get_transport_from_path,
50
path = os.path.join(bedding.cache_dir(), "git")
51
if not os.path.isdir(path):
56
def get_remote_cache_transport(repository):
57
"""Retrieve the transport to use when accessing (unwritable) remote
60
uuid = getattr(repository, "uuid", None)
62
path = get_cache_dir()
64
path = os.path.join(get_cache_dir(), uuid)
65
if not os.path.isdir(path):
67
return get_transport_from_path(path)
26
70
def check_pysqlite_version(sqlite3):
27
71
"""Check that sqlite library is compatible.
30
if (sqlite3.sqlite_version_info[0] < 3 or
31
(sqlite3.sqlite_version_info[0] == 3 and
32
sqlite3.sqlite_version_info[1] < 3)):
33
warning('Needs at least sqlite 3.3.x')
34
raise bzrlib.errors.BzrError("incompatible sqlite library")
74
if (sqlite3.sqlite_version_info[0] < 3
75
or (sqlite3.sqlite_version_info[0] == 3 and
76
sqlite3.sqlite_version_info[1] < 3)):
77
trace.warning('Needs at least sqlite 3.3.x')
78
raise bzr_errors.BzrError("incompatible sqlite library")
39
84
check_pysqlite_version(sqlite3)
40
except (ImportError, bzrlib.errors.BzrError), e:
85
except (ImportError, bzr_errors.BzrError):
41
86
from pysqlite2 import dbapi2 as sqlite3
42
87
check_pysqlite_version(sqlite3)
44
warning('Needs at least Python2.5 or Python2.4 with the pysqlite2 '
46
raise bzrlib.errors.BzrError("missing sqlite library")
89
trace.warning('Needs at least Python2.5 or Python2.4 with the pysqlite2 '
91
raise bzr_errors.BzrError("missing sqlite library")
94
_mapdbs = threading.local()
98
"""Get a cache for this thread's db connections."""
101
except AttributeError:
49
106
class GitShaMap(object):
51
def __init__(self, transport):
52
self.transport = transport
53
self.db = sqlite3.connect(
54
os.path.join(self.transport.local_abspath("."), "git.db"))
107
"""Git<->Bzr revision id mapping database."""
109
def lookup_git_sha(self, sha):
110
"""Lookup a Git sha in the database.
111
:param sha: Git object sha
112
:return: list with (type, type_data) tuples with type_data:
113
commit: revid, tree_sha, verifiers
117
raise NotImplementedError(self.lookup_git_sha)
119
def lookup_blob_id(self, file_id, revision):
120
"""Retrieve a Git blob SHA by file id.
122
:param file_id: File id of the file/symlink
123
:param revision: revision in which the file was last changed.
125
raise NotImplementedError(self.lookup_blob_id)
127
def lookup_tree_id(self, file_id, revision):
128
"""Retrieve a Git tree SHA by file id.
130
raise NotImplementedError(self.lookup_tree_id)
132
def lookup_commit(self, revid):
133
"""Retrieve a Git commit SHA by Bazaar revision id.
135
raise NotImplementedError(self.lookup_commit)
138
"""List the revision ids known."""
139
raise NotImplementedError(self.revids)
141
def missing_revisions(self, revids):
142
"""Return set of all the revisions that are not present."""
143
present_revids = set(self.revids())
144
if not isinstance(revids, set):
146
return revids - present_revids
149
"""List the SHA1s."""
150
raise NotImplementedError(self.sha1s)
152
def start_write_group(self):
153
"""Start writing changes."""
155
def commit_write_group(self):
156
"""Commit any pending changes."""
158
def abort_write_group(self):
159
"""Abort any pending changes."""
162
class ContentCache(object):
163
"""Object that can cache Git objects."""
165
def add(self, object):
167
raise NotImplementedError(self.add)
169
def add_multi(self, objects):
170
"""Add multiple objects."""
174
def __getitem__(self, sha):
175
"""Retrieve an item, by SHA."""
176
raise NotImplementedError(self.__getitem__)
179
class BzrGitCacheFormat(object):
180
"""Bazaar-Git Cache Format."""
182
def get_format_string(self):
183
"""Return a single-line unique format string for this cache format."""
184
raise NotImplementedError(self.get_format_string)
186
def open(self, transport):
187
"""Open this format on a transport."""
188
raise NotImplementedError(self.open)
190
def initialize(self, transport):
191
"""Create a new instance of this cache format at transport."""
192
transport.put_bytes('format', self.get_format_string())
195
def from_transport(self, transport):
196
"""Open a cache file present on a transport, or initialize one.
198
:param transport: Transport to use
199
:return: A BzrGitCache instance
202
format_name = transport.get_bytes('format')
203
format = formats.get(format_name)
204
except bzr_errors.NoSuchFile:
205
format = formats.get('default')
206
format.initialize(transport)
207
return format.open(transport)
210
def from_repository(cls, repository):
211
"""Open a cache file for a repository.
213
This will use the repository's transport to store the cache file, or
214
use the users global cache directory if the repository has no
215
transport associated with it.
217
:param repository: Repository to open the cache for
218
:return: A `BzrGitCache`
220
from ..transport.local import LocalTransport
221
repo_transport = getattr(repository, "_transport", None)
222
if (repo_transport is not None
223
and isinstance(repo_transport, LocalTransport)):
224
# Even if we don't write to this repo, we should be able
225
# to update its cache.
227
repo_transport = remove_readonly_transport_decorator(
229
except bzr_errors.ReadOnlyError:
233
repo_transport.mkdir('git')
234
except bzr_errors.FileExists:
236
transport = repo_transport.clone('git')
239
if transport is None:
240
transport = get_remote_cache_transport(repository)
241
return cls.from_transport(transport)
244
class CacheUpdater(object):
245
"""Base class for objects that can update a bzr-git cache."""
247
def add_object(self, obj, bzr_key_data, path):
250
:param obj: Object type ("commit", "blob" or "tree")
251
:param bzr_key_data: bzr key store data or testament_sha in case
253
:param path: Path of the object (optional)
255
raise NotImplementedError(self.add_object)
258
raise NotImplementedError(self.finish)
261
class BzrGitCache(object):
262
"""Caching backend."""
264
def __init__(self, idmap, cache_updater_klass):
266
self._cache_updater_klass = cache_updater_klass
268
def get_updater(self, rev):
269
"""Update an object that implements the CacheUpdater interface for
272
return self._cache_updater_klass(self, rev)
275
def DictBzrGitCache():
276
return BzrGitCache(DictGitShaMap(), DictCacheUpdater)
279
class DictCacheUpdater(CacheUpdater):
280
"""Cache updater for dict-based caches."""
282
def __init__(self, cache, rev):
284
self.revid = rev.revision_id
285
self.parent_revids = rev.parent_ids
289
def add_object(self, obj, bzr_key_data, path):
290
if isinstance(obj, tuple):
291
(type_name, hexsha) = obj
293
type_name = obj.type_name.decode('ascii')
295
if not isinstance(hexsha, bytes):
296
raise TypeError(hexsha)
297
if type_name == "commit":
299
if type(bzr_key_data) is not dict:
300
raise TypeError(bzr_key_data)
302
type_data = (self.revid, self._commit.tree, bzr_key_data)
303
self.cache.idmap._by_revid[self.revid] = hexsha
304
elif type_name in ("blob", "tree"):
305
if bzr_key_data is not None:
306
key = type_data = bzr_key_data
307
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[
308
type_data[0]] = hexsha
311
entry = (type_name, type_data)
312
self.cache.idmap._by_sha.setdefault(hexsha, {})[key] = entry
315
if self._commit is None:
316
raise AssertionError("No commit object added")
320
class DictGitShaMap(GitShaMap):
321
"""Git SHA map that uses a dictionary."""
328
def lookup_blob_id(self, fileid, revision):
329
return self._by_fileid[revision][fileid]
331
def lookup_git_sha(self, sha):
332
if not isinstance(sha, bytes):
334
for entry in self._by_sha[sha].values():
337
def lookup_tree_id(self, fileid, revision):
338
return self._by_fileid[revision][fileid]
340
def lookup_commit(self, revid):
341
return self._by_revid[revid]
344
for key, entries in self._by_sha.items():
345
for (type, type_data) in entries.values():
350
return self._by_sha.keys()
353
class SqliteCacheUpdater(CacheUpdater):
355
def __init__(self, cache, rev):
357
self.db = self.cache.idmap.db
358
self.revid = rev.revision_id
363
def add_object(self, obj, bzr_key_data, path):
364
if isinstance(obj, tuple):
365
(type_name, hexsha) = obj
367
type_name = obj.type_name.decode('ascii')
369
if not isinstance(hexsha, bytes):
370
raise TypeError(hexsha)
371
if type_name == "commit":
373
if type(bzr_key_data) is not dict:
374
raise TypeError(bzr_key_data)
375
self._testament3_sha1 = bzr_key_data.get("testament3-sha1")
376
elif type_name == "tree":
377
if bzr_key_data is not None:
378
self._trees.append((hexsha, bzr_key_data[0], bzr_key_data[1]))
379
elif type_name == "blob":
380
if bzr_key_data is not None:
381
self._blobs.append((hexsha, bzr_key_data[0], bzr_key_data[1]))
386
if self._commit is None:
387
raise AssertionError("No commit object added")
389
"replace into trees (sha1, fileid, revid) values (?, ?, ?)",
392
"replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
395
"replace into commits (sha1, revid, tree_sha, testament3_sha1) "
396
"values (?, ?, ?, ?)",
397
(self._commit.id, self.revid, self._commit.tree,
398
self._testament3_sha1))
402
def SqliteBzrGitCache(p):
403
return BzrGitCache(SqliteGitShaMap(p), SqliteCacheUpdater)
406
class SqliteGitCacheFormat(BzrGitCacheFormat):
408
def get_format_string(self):
409
return b'bzr-git sha map version 1 using sqlite\n'
411
def open(self, transport):
413
basepath = transport.local_abspath(".")
414
except bzr_errors.NotLocalUrl:
415
basepath = get_cache_dir()
416
return SqliteBzrGitCache(os.path.join(basepath, "idmap.db"))
419
class SqliteGitShaMap(GitShaMap):
420
"""Bazaar GIT Sha map that uses a sqlite database for storage."""
422
def __init__(self, path=None):
425
self.db = sqlite3.connect(":memory:")
427
if path not in mapdbs():
428
mapdbs()[path] = sqlite3.connect(path)
429
self.db = mapdbs()[path]
430
self.db.text_factory = str
55
431
self.db.executescript("""
56
create table if not exists commits(sha1 text, revid text, tree_sha text);
432
create table if not exists commits(
433
sha1 text not null check(length(sha1) == 40),
435
tree_sha text not null check(length(tree_sha) == 40)
57
437
create index if not exists commit_sha1 on commits(sha1);
58
create table if not exists blobs(sha1 text, fileid text, revid text);
438
create unique index if not exists commit_revid on commits(revid);
439
create table if not exists blobs(
440
sha1 text not null check(length(sha1) == 40),
441
fileid text not null,
59
444
create index if not exists blobs_sha1 on blobs(sha1);
60
create table if not exists trees(sha1 text, fileid text, revid text);
61
create index if not exists trees_sha1 on trees(sha1);
445
create unique index if not exists blobs_fileid_revid on blobs(
447
create table if not exists trees(
448
sha1 text unique not null check(length(sha1) == 40),
449
fileid text not null,
452
create unique index if not exists trees_sha1 on trees(sha1);
453
create unique index if not exists trees_fileid_revid on trees(
64
def _parent_lookup(self, revid):
65
return self.db.execute("select sha1 from commits where revid = ?", (revid,)).fetchone()[0].encode("utf-8")
67
def add_entry(self, sha, type, type_data):
68
"""Add a new entry to the database.
70
assert isinstance(type_data, tuple)
71
assert isinstance(sha, str), "type was %r" % sha
73
self.db.execute("replace into commits (sha1, revid, tree_sha) values (?, ?, ?)", (sha, type_data[0], type_data[1]))
75
self.db.execute("replace into blobs (sha1, fileid, revid) values (?, ?, ?)", (sha, type_data[0], type_data[1]))
77
self.db.execute("replace into trees (sha1, fileid, revid) values (?, ?, ?)", (sha, type_data[0], type_data[1]))
79
raise AssertionError("Unknown type %s" % type)
81
def lookup_git_sha(self, sha):
82
"""Lookup a Git sha in the database.
84
:param sha: Git object sha
85
:return: (type, type_data) with type_data:
86
revision: revid, tree sha
88
row = self.db.execute("select revid, tree_sha from commits where sha1 = ?", (sha,)).fetchone()
90
return ("commit", row)
91
row = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,)).fetchone()
94
row = self.db.execute("select fileid, revid from trees where sha1 = ?", (sha,)).fetchone()
100
for row in self.db.execute("select revid from commits").fetchall():
457
self.db.executescript(
458
"ALTER TABLE commits ADD testament3_sha1 TEXT;")
459
except sqlite3.OperationalError:
460
pass # Column already exists.
463
return "%s(%r)" % (self.__class__.__name__, self.path)
465
def lookup_commit(self, revid):
466
cursor = self.db.execute("select sha1 from commits where revid = ?",
468
row = cursor.fetchone()
473
def commit_write_group(self):
476
def lookup_blob_id(self, fileid, revision):
477
row = self.db.execute(
478
"select sha1 from blobs where fileid = ? and revid = ?",
479
(fileid, revision)).fetchone()
482
raise KeyError(fileid)
484
def lookup_tree_id(self, fileid, revision):
485
row = self.db.execute(
486
"select sha1 from trees where fileid = ? and revid = ?",
487
(fileid, revision)).fetchone()
490
raise KeyError(fileid)
492
def lookup_git_sha(self, sha):
493
"""Lookup a Git sha in the database.
495
:param sha: Git object sha
496
:return: (type, type_data) with type_data:
497
commit: revid, tree sha, verifiers
502
cursor = self.db.execute(
503
"select revid, tree_sha, testament3_sha1 from commits where "
505
for row in cursor.fetchall():
507
if row[2] is not None:
508
verifiers = {"testament3-sha1": row[2]}
511
yield ("commit", (row[0], row[1], verifiers))
512
cursor = self.db.execute(
513
"select fileid, revid from blobs where sha1 = ?", (sha,))
514
for row in cursor.fetchall():
517
cursor = self.db.execute(
518
"select fileid, revid from trees where sha1 = ?", (sha,))
519
for row in cursor.fetchall():
526
"""List the revision ids known."""
527
return (row for (row,) in self.db.execute("select revid from commits"))
530
"""List the SHA1s."""
531
for table in ("blobs", "commits", "trees"):
532
for (sha,) in self.db.execute("select sha1 from %s" % table):
533
yield sha.encode('ascii')
536
class TdbCacheUpdater(CacheUpdater):
537
"""Cache updater for tdb-based caches."""
539
def __init__(self, cache, rev):
541
self.db = cache.idmap.db
542
self.revid = rev.revision_id
543
self.parent_revids = rev.parent_ids
547
def add_object(self, obj, bzr_key_data, path):
548
if isinstance(obj, tuple):
549
(type_name, hexsha) = obj
550
sha = hex_to_sha(hexsha)
552
type_name = obj.type_name.decode('ascii')
553
sha = obj.sha().digest()
554
if type_name == "commit":
555
self.db[b"commit\0" + self.revid] = b"\0".join((sha, obj.tree))
556
if type(bzr_key_data) is not dict:
557
raise TypeError(bzr_key_data)
558
type_data = (self.revid, obj.tree)
560
type_data += (bzr_key_data["testament3-sha1"],)
564
elif type_name == "blob":
565
if bzr_key_data is None:
568
(b"blob", bzr_key_data[0], bzr_key_data[1]))] = sha
569
type_data = bzr_key_data
570
elif type_name == "tree":
571
if bzr_key_data is None:
573
type_data = bzr_key_data
576
entry = b"\0".join((type_name.encode('ascii'), ) + type_data) + b"\n"
579
oldval = self.db[key]
583
if not oldval.endswith(b'\n'):
584
self.db[key] = b"".join([oldval, b"\n", entry])
586
self.db[key] = b"".join([oldval, entry])
589
if self._commit is None:
590
raise AssertionError("No commit object added")
594
def TdbBzrGitCache(p):
595
return BzrGitCache(TdbGitShaMap(p), TdbCacheUpdater)
598
class TdbGitCacheFormat(BzrGitCacheFormat):
599
"""Cache format for tdb-based caches."""
601
def get_format_string(self):
602
return b'bzr-git sha map version 3 using tdb\n'
604
def open(self, transport):
606
basepath = transport.local_abspath(".")
607
except bzr_errors.NotLocalUrl:
608
basepath = get_cache_dir()
610
return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
613
"Unable to open existing bzr-git cache because 'tdb' is not "
617
class TdbGitShaMap(GitShaMap):
618
"""SHA Map that uses a TDB database.
622
"git <sha1>" -> "<type> <type-data1> <type-data2>"
623
"commit revid" -> "<sha1> <tree-id>"
624
"tree fileid revid" -> "<sha1>"
625
"blob fileid revid" -> "<sha1>"
629
TDB_HASH_SIZE = 50000
631
def __init__(self, path=None):
637
if path not in mapdbs():
638
mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
639
os.O_RDWR | os.O_CREAT)
640
self.db = mapdbs()[path]
642
if int(self.db[b"version"]) not in (2, 3):
644
"SHA Map is incompatible (%s -> %d), rebuilding database.",
645
self.db[b"version"], self.TDB_MAP_VERSION)
649
self.db[b"version"] = b'%d' % self.TDB_MAP_VERSION
651
def start_write_group(self):
652
"""Start writing changes."""
653
self.db.transaction_start()
655
def commit_write_group(self):
656
"""Commit any pending changes."""
657
self.db.transaction_commit()
659
def abort_write_group(self):
660
"""Abort any pending changes."""
661
self.db.transaction_cancel()
664
return "%s(%r)" % (self.__class__.__name__, self.path)
666
def lookup_commit(self, revid):
668
return sha_to_hex(self.db[b"commit\0" + revid][:20])
670
raise KeyError("No cache entry for %r" % revid)
672
def lookup_blob_id(self, fileid, revision):
673
return sha_to_hex(self.db[b"\0".join((b"blob", fileid, revision))])
675
def lookup_git_sha(self, sha):
676
"""Lookup a Git sha in the database.
678
:param sha: Git object sha
679
:return: (type, type_data) with type_data:
680
commit: revid, tree sha
685
sha = hex_to_sha(sha)
686
value = self.db[b"git\0" + sha]
687
for data in value.splitlines():
688
data = data.split(b"\0")
689
type_name = data[0].decode('ascii')
690
if type_name == "commit":
692
yield (type_name, (data[1], data[2], {}))
694
yield (type_name, (data[1], data[2],
695
{"testament3-sha1": data[3]}))
696
elif type_name in ("tree", "blob"):
697
yield (type_name, tuple(data[1:]))
699
raise AssertionError("unknown type %r" % type_name)
701
def missing_revisions(self, revids):
704
if self.db.get(b"commit\0" + revid) is None:
709
return self.db.keys()
712
"""List the revision ids known."""
713
for key in self._keys():
714
if key.startswith(b"commit\0"):
718
"""List the SHA1s."""
719
for key in self._keys():
720
if key.startswith(b"git\0"):
721
yield sha_to_hex(key[4:])
724
class VersionedFilesContentCache(ContentCache):
726
def __init__(self, vf):
730
self._vf.insert_record_stream(
731
[versionedfile.ChunkedContentFactory(
732
(obj.id,), [], None, obj.as_legacy_object_chunks())])
734
def __getitem__(self, sha):
735
stream = self._vf.get_record_stream([(sha,)], 'unordered', True)
737
if entry.storage_kind == 'absent':
739
return ShaFile._parse_legacy_object(entry.get_bytes_as('fulltext'))
742
class IndexCacheUpdater(CacheUpdater):
744
def __init__(self, cache, rev):
746
self.revid = rev.revision_id
747
self.parent_revids = rev.parent_ids
751
def add_object(self, obj, bzr_key_data, path):
752
if isinstance(obj, tuple):
753
(type_name, hexsha) = obj
755
type_name = obj.type_name.decode('ascii')
757
if type_name == "commit":
759
if type(bzr_key_data) is not dict:
760
raise TypeError(bzr_key_data)
761
self.cache.idmap._add_git_sha(hexsha, b"commit",
762
(self.revid, obj.tree, bzr_key_data))
763
self.cache.idmap._add_node((b"commit", self.revid, b"X"),
764
b" ".join((hexsha, obj.tree)))
765
elif type_name == "blob":
766
self.cache.idmap._add_git_sha(hexsha, b"blob", bzr_key_data)
767
self.cache.idmap._add_node((b"blob", bzr_key_data[0],
768
bzr_key_data[1]), hexsha)
769
elif type_name == "tree":
770
self.cache.idmap._add_git_sha(hexsha, b"tree", bzr_key_data)
778
class IndexBzrGitCache(BzrGitCache):
780
def __init__(self, transport=None):
781
shamap = IndexGitShaMap(transport.clone('index'))
782
super(IndexBzrGitCache, self).__init__(shamap, IndexCacheUpdater)
785
class IndexGitCacheFormat(BzrGitCacheFormat):
787
def get_format_string(self):
788
return b'bzr-git sha map with git object cache version 1\n'
790
def initialize(self, transport):
791
super(IndexGitCacheFormat, self).initialize(transport)
792
transport.mkdir('index')
793
transport.mkdir('objects')
794
from .transportgit import TransportObjectStore
795
TransportObjectStore.init(transport.clone('objects'))
797
def open(self, transport):
798
return IndexBzrGitCache(transport)
801
class IndexGitShaMap(GitShaMap):
802
"""SHA Map that uses the Bazaar APIs to store a cache.
804
BTree Index file with the following contents:
806
("git", <sha1>, "X") -> "<type> <type-data1> <type-data2>"
807
("commit", <revid>, "X") -> "<sha1> <tree-id>"
808
("blob", <fileid>, <revid>) -> <sha1>
812
def __init__(self, transport=None):
814
if transport is None:
815
self._transport = None
816
self._index = _mod_index.InMemoryGraphIndex(0, key_elements=3)
817
self._builder = self._index
820
self._transport = transport
821
self._index = _mod_index.CombinedGraphIndex([])
822
for name in self._transport.list_dir("."):
823
if not name.endswith(".rix"):
825
x = _mod_btree_index.BTreeGraphIndex(
826
self._transport, name, self._transport.stat(name).st_size)
827
self._index.insert_index(0, x)
830
def from_repository(cls, repository):
831
transport = getattr(repository, "_transport", None)
832
if transport is not None:
834
transport.mkdir('git')
835
except bzr_errors.FileExists:
837
return cls(transport.clone('git'))
838
return cls(get_transport_from_path(get_cache_dir()))
841
if self._transport is not None:
842
return "%s(%r)" % (self.__class__.__name__, self._transport.base)
844
return "%s()" % (self.__class__.__name__)
847
if self._builder is not None:
848
raise bzr_errors.BzrError('builder already open')
849
self.start_write_group()
850
self._builder.add_nodes(
851
((key, value) for (_, key, value) in
852
self._index.iter_all_entries()))
854
for name in self._transport.list_dir('.'):
855
if name.endswith('.rix'):
856
to_remove.append(name)
857
self.commit_write_group()
858
del self._index.indices[1:]
859
for name in to_remove:
860
self._transport.rename(name, name + '.old')
862
def start_write_group(self):
863
if self._builder is not None:
864
raise bzr_errors.BzrError('builder already open')
865
self._builder = _mod_btree_index.BTreeBuilder(0, key_elements=3)
866
self._name = osutils.sha()
868
def commit_write_group(self):
869
if self._builder is None:
870
raise bzr_errors.BzrError('builder not open')
871
stream = self._builder.finish()
872
name = self._name.hexdigest() + ".rix"
873
size = self._transport.put_file(name, stream)
874
index = _mod_btree_index.BTreeGraphIndex(self._transport, name, size)
875
self._index.insert_index(0, index)
879
def abort_write_group(self):
880
if self._builder is None:
881
raise bzr_errors.BzrError('builder not open')
885
def _add_node(self, key, value):
889
self._builder.add_node(key, value)
894
def _get_entry(self, key):
895
entries = self._index.iter_entries([key])
897
return next(entries)[2]
898
except StopIteration:
899
if self._builder is None:
901
entries = self._builder.iter_entries([key])
903
return next(entries)[2]
904
except StopIteration:
907
def _iter_entries_prefix(self, prefix):
908
for entry in self._index.iter_entries_prefix([prefix]):
909
yield (entry[1], entry[2])
910
if self._builder is not None:
911
for entry in self._builder.iter_entries_prefix([prefix]):
912
yield (entry[1], entry[2])
914
def lookup_commit(self, revid):
915
return self._get_entry((b"commit", revid, b"X"))[:40]
917
def _add_git_sha(self, hexsha, type, type_data):
918
if hexsha is not None:
919
self._name.update(hexsha)
920
if type == b"commit":
921
td = (type_data[0], type_data[1])
923
td += (type_data[2]["testament3-sha1"],)
928
self._add_node((b"git", hexsha, b"X"), b" ".join((type,) + td))
930
# This object is not represented in Git - perhaps an empty
932
self._name.update(type + b" ".join(type_data))
934
def lookup_blob_id(self, fileid, revision):
935
return self._get_entry((b"blob", fileid, revision))
937
def lookup_git_sha(self, sha):
939
sha = sha_to_hex(sha)
940
value = self._get_entry((b"git", sha, b"X"))
941
data = value.split(b" ", 3)
942
if data[0] == b"commit":
945
verifiers = {"testament3-sha1": data[3]}
950
yield ("commit", (data[1], data[2], verifiers))
952
yield (data[0].decode('ascii'), tuple(data[1:]))
955
"""List the revision ids known."""
956
for key, value in self._iter_entries_prefix((b"commit", None, None)):
959
def missing_revisions(self, revids):
960
"""Return set of all the revisions that are not present."""
961
missing_revids = set(revids)
962
for _, key, value in self._index.iter_entries((
963
(b"commit", revid, b"X") for revid in revids)):
964
missing_revids.remove(key[1])
965
return missing_revids
968
"""List the SHA1s."""
969
for key, value in self._iter_entries_prefix((b"git", None, None)):
973
formats = registry.Registry()
974
formats.register(TdbGitCacheFormat().get_format_string(),
976
formats.register(SqliteGitCacheFormat().get_format_string(),
977
SqliteGitCacheFormat())
978
formats.register(IndexGitCacheFormat().get_format_string(),
979
IndexGitCacheFormat())
980
# In the future, this will become the default:
981
formats.register('default', IndexGitCacheFormat())
984
def migrate_ancient_formats(repo_transport):
985
# Migrate older cache formats
986
repo_transport = remove_readonly_transport_decorator(repo_transport)
987
has_sqlite = repo_transport.has("git.db")
988
has_tdb = repo_transport.has("git.tdb")
989
if not has_sqlite or has_tdb:
992
repo_transport.mkdir("git")
993
except bzr_errors.FileExists:
995
# Prefer migrating git.db over git.tdb, since the latter may not
996
# be openable on some platforms.
998
SqliteGitCacheFormat().initialize(repo_transport.clone("git"))
999
repo_transport.rename("git.db", "git/idmap.db")
1001
TdbGitCacheFormat().initialize(repo_transport.clone("git"))
1002
repo_transport.rename("git.tdb", "git/idmap.tdb")
1005
def remove_readonly_transport_decorator(transport):
1006
if transport.is_readonly():
1008
return transport._decorated
1009
except AttributeError:
1010
raise bzr_errors.ReadOnlyError(transport)
1014
def from_repository(repository):
1015
"""Open a cache file for a repository.
1017
If the repository is remote and there is no transport available from it
1018
this will use a local file in the users cache directory
1019
(typically ~/.cache/bazaar/git/)
1021
:param repository: A repository object
1023
repo_transport = getattr(repository, "_transport", None)
1024
if repo_transport is not None:
1026
migrate_ancient_formats(repo_transport)
1027
except bzr_errors.ReadOnlyError:
1028
pass # Not much we can do
1029
return BzrGitCacheFormat.from_repository(repository)