97
78
class GitShaMap(object):
98
79
"""Git<->Bzr revision id mapping database."""
81
def add_entry(self, sha, type, type_data):
82
"""Add a new entry to the database.
84
raise NotImplementedError(self.add_entry)
86
def add_entries(self, entries):
87
"""Add multiple new entries to the database.
92
def lookup_tree(self, fileid, revid):
93
"""Lookup the SHA of a git tree."""
94
raise NotImplementedError(self.lookup_tree)
96
def lookup_blob(self, fileid, revid):
97
"""Lookup a blob by the fileid it has in a bzr revision."""
98
raise NotImplementedError(self.lookup_blob)
100
100
def lookup_git_sha(self, sha):
101
101
"""Lookup a Git sha in the database.
102
103
:param sha: Git object sha
103
104
:return: (type, type_data) with type_data:
104
commit: revid, tree_sha, verifiers
105
revision: revid, tree sha
108
107
raise NotImplementedError(self.lookup_git_sha)
110
def lookup_blob_id(self, file_id, revision):
111
"""Retrieve a Git blob SHA by file id.
113
:param file_id: File id of the file/symlink
114
:param revision: revision in which the file was last changed.
116
raise NotImplementedError(self.lookup_blob_id)
118
def lookup_tree_id(self, file_id, revision):
119
"""Retrieve a Git tree SHA by file id.
121
raise NotImplementedError(self.lookup_tree_id)
123
109
def revids(self):
124
110
"""List the revision ids known."""
125
111
raise NotImplementedError(self.revids)
127
def missing_revisions(self, revids):
128
"""Return set of all the revisions that are not present."""
129
present_revids = set(self.revids())
130
if not isinstance(revids, set):
132
return revids - present_revids
135
114
"""List the SHA1s."""
136
115
raise NotImplementedError(self.sha1s)
138
def start_write_group(self):
139
"""Start writing changes."""
141
def commit_write_group(self):
142
118
"""Commit any pending changes."""
144
def abort_write_group(self):
145
"""Abort any pending changes."""
148
class ContentCache(object):
149
"""Object that can cache Git objects."""
151
def add(self, object):
153
raise NotImplementedError(self.add)
155
def add_multi(self, objects):
156
"""Add multiple objects."""
160
def __getitem__(self, sha):
161
"""Retrieve an item, by SHA."""
162
raise NotImplementedError(self.__getitem__)
165
class BzrGitCacheFormat(object):
166
"""Bazaar-Git Cache Format."""
168
def get_format_string(self):
169
"""Return a single-line unique format string for this cache format."""
170
raise NotImplementedError(self.get_format_string)
172
def open(self, transport):
173
"""Open this format on a transport."""
174
raise NotImplementedError(self.open)
176
def initialize(self, transport):
177
"""Create a new instance of this cache format at transport."""
178
transport.put_bytes('format', self.get_format_string())
181
def from_transport(self, transport):
182
"""Open a cache file present on a transport, or initialize one.
184
:param transport: Transport to use
185
:return: A BzrGitCache instance
188
format_name = transport.get_bytes('format')
189
format = formats.get(format_name)
190
except bzrlib.errors.NoSuchFile:
191
format = formats.get('default')
192
format.initialize(transport)
193
return format.open(transport)
196
def from_repository(cls, repository):
197
"""Open a cache file for a repository.
199
This will use the repository's transport to store the cache file, or
200
use the users global cache directory if the repository has no
201
transport associated with it.
203
:param repository: Repository to open the cache for
204
:return: A `BzrGitCache`
206
repo_transport = getattr(repository, "_transport", None)
207
if repo_transport is not None:
208
# Even if we don't write to this repo, we should be able
209
# to update its cache.
210
repo_transport = remove_readonly_transport_decorator(repo_transport)
212
repo_transport.mkdir('git')
213
except bzrlib.errors.FileExists:
215
transport = repo_transport.clone('git')
217
transport = get_remote_cache_transport()
218
return cls.from_transport(transport)
221
class CacheUpdater(object):
222
"""Base class for objects that can update a bzr-git cache."""
224
def add_object(self, obj, ie, path):
227
:param obj: Object type ("commit", "blob" or "tree")
228
:param ie: Inventory entry (for blob/tree) or testament_sha in case
230
:param path: Path of the object (optional)
232
raise NotImplementedError(self.add_object)
235
raise NotImplementedError(self.finish)
238
class BzrGitCache(object):
239
"""Caching backend."""
241
def __init__(self, idmap, content_cache, cache_updater_klass):
243
self.content_cache = content_cache
244
self._cache_updater_klass = cache_updater_klass
246
def get_updater(self, rev):
247
"""Update an object that implements the CacheUpdater interface for
250
return self._cache_updater_klass(self, rev)
253
DictBzrGitCache = lambda: BzrGitCache(DictGitShaMap(), None, DictCacheUpdater)
256
class DictCacheUpdater(CacheUpdater):
257
"""Cache updater for dict-based caches."""
259
def __init__(self, cache, rev):
261
self.revid = rev.revision_id
262
self.parent_revids = rev.parent_ids
266
def add_object(self, obj, ie, path):
267
if obj.type_name == "commit":
269
assert type(ie) is dict
270
type_data = (self.revid, self._commit.tree, ie)
271
self.cache.idmap._by_revid[self.revid] = obj.id
272
elif obj.type_name in ("blob", "tree"):
274
if obj.type_name == "blob":
275
revision = ie.revision
277
revision = self.revid
278
type_data = (ie.file_id, revision)
279
self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] = obj.id
282
self.cache.idmap._by_sha[obj.id] = (obj.type_name, type_data)
285
if self._commit is None:
286
raise AssertionError("No commit object added")
290
121
class DictGitShaMap(GitShaMap):
291
"""Git SHA map that uses a dictionary."""
293
123
def __init__(self):
298
def lookup_blob_id(self, fileid, revision):
299
return self._by_fileid[revision][fileid]
126
def add_entry(self, sha, type, type_data):
127
self.dict[sha] = (type, type_data)
301
129
def lookup_git_sha(self, sha):
302
return self._by_sha[sha]
304
def lookup_tree_id(self, fileid, revision):
305
return self._by_fileid[revision][fileid]
307
def lookup_commit(self, revid):
308
return self._by_revid[revid]
130
return self.dict[sha]
132
def lookup_tree(self, fileid, revid):
133
for k, v in self.dict.iteritems():
134
if v == ("tree", (fileid, revid)):
136
raise KeyError((fileid, revid))
138
def lookup_blob(self, fileid, revid):
139
for k, v in self.dict.iteritems():
140
if v == ("blob", (fileid, revid)):
142
raise KeyError((fileid, revid))
310
144
def revids(self):
311
for key, (type, type_data) in self._by_sha.iteritems():
145
for key, (type, type_data) in self.dict.iteritems():
312
146
if type == "commit":
313
147
yield type_data[0]
316
return self._by_sha.iterkeys()
319
class SqliteCacheUpdater(CacheUpdater):
321
def __init__(self, cache, rev):
323
self.db = self.cache.idmap.db
324
self.revid = rev.revision_id
329
def add_object(self, obj, ie, path):
330
if obj.type_name == "commit":
332
self._testament3_sha1 = ie["testament3-sha1"]
333
assert type(ie) is dict
334
elif obj.type_name == "tree":
336
self._trees.append((obj.id, ie.file_id, self.revid))
337
elif obj.type_name == "blob":
339
self._blobs.append((obj.id, ie.file_id, ie.revision))
344
if self._commit is None:
345
raise AssertionError("No commit object added")
347
"replace into trees (sha1, fileid, revid) values (?, ?, ?)",
350
"replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
353
"replace into commits (sha1, revid, tree_sha, testament3_sha1) values (?, ?, ?, ?)",
354
(self._commit.id, self.revid, self._commit.tree, self._testament3_sha1))
358
SqliteBzrGitCache = lambda p: BzrGitCache(SqliteGitShaMap(p), None, SqliteCacheUpdater)
361
class SqliteGitCacheFormat(BzrGitCacheFormat):
363
def get_format_string(self):
364
return 'bzr-git sha map version 1 using sqlite\n'
366
def open(self, transport):
368
basepath = transport.local_abspath(".")
369
except bzrlib.errors.NotLocalUrl:
370
basepath = get_cache_dir()
371
return SqliteBzrGitCache(os.path.join(basepath, "idmap.db"))
150
return self.dict.iterkeys()
374
153
class SqliteGitShaMap(GitShaMap):
375
"""Bazaar GIT Sha map that uses a sqlite database for storage."""
377
155
def __init__(self, path=None):
382
160
if not mapdbs().has_key(path):
383
161
mapdbs()[path] = sqlite3.connect(path)
384
self.db = mapdbs()[path]
385
self.db.text_factory = str
162
self.db = mapdbs()[path]
386
163
self.db.executescript("""
387
create table if not exists commits(
388
sha1 text not null check(length(sha1) == 40),
390
tree_sha text not null check(length(tree_sha) == 40)
164
create table if not exists commits(sha1 text, revid text, tree_sha text);
392
165
create index if not exists commit_sha1 on commits(sha1);
393
166
create unique index if not exists commit_revid on commits(revid);
394
create table if not exists blobs(
395
sha1 text not null check(length(sha1) == 40),
396
fileid text not null,
167
create table if not exists blobs(sha1 text, fileid text, revid text);
399
168
create index if not exists blobs_sha1 on blobs(sha1);
400
169
create unique index if not exists blobs_fileid_revid on blobs(fileid, revid);
401
create table if not exists trees(
402
sha1 text unique not null check(length(sha1) == 40),
403
fileid text not null,
406
create unique index if not exists trees_sha1 on trees(sha1);
170
create table if not exists trees(sha1 text, fileid text, revid text);
171
create index if not exists trees_sha1 on trees(sha1);
407
172
create unique index if not exists trees_fileid_revid on trees(fileid, revid);
176
def from_repository(cls, repository):
410
self.db.executescript(
411
"ALTER TABLE commits ADD testament3_sha1 TEXT;")
412
except sqlite3.OperationalError:
413
pass # Column already exists.
416
return "%s(%r)" % (self.__class__.__name__, self.path)
178
transport = getattr(repository, "_transport", None)
179
if transport is not None:
180
return cls(os.path.join(transport.local_abspath("."), "git.db"))
181
except bzrlib.errors.NotLocalUrl:
183
return cls(os.path.join(get_cache_dir(), "remote.db"))
418
185
def lookup_commit(self, revid):
419
cursor = self.db.execute("select sha1 from commits where revid = ?",
421
row = cursor.fetchone()
186
row = self.db.execute("select sha1 from commits where revid = ?", (revid,)).fetchone()
422
187
if row is not None:
188
return row[0].encode("utf-8")
426
def commit_write_group(self):
429
def lookup_blob_id(self, fileid, revision):
430
row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revision)).fetchone()
433
raise KeyError(fileid)
435
def lookup_tree_id(self, fileid, revision):
436
row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid, revision)).fetchone()
439
raise KeyError(fileid)
194
def add_entries(self, entries):
197
for sha, type, type_data in entries:
198
assert isinstance(type_data[0], str)
199
assert isinstance(type_data[1], str)
200
entry = (sha.decode("utf-8"), type_data[0].decode("utf-8"),
201
type_data[1].decode("utf-8"))
209
self.db.executemany("replace into trees (sha1, fileid, revid) values (?, ?, ?)", trees)
211
self.db.executemany("replace into blobs (sha1, fileid, revid) values (?, ?, ?)", blobs)
214
def add_entry(self, sha, type, type_data):
215
"""Add a new entry to the database.
217
assert isinstance(type_data, tuple)
218
assert isinstance(sha, str), "type was %r" % sha
220
self.db.execute("replace into commits (sha1, revid, tree_sha) values (?, ?, ?)", (sha, type_data[0], type_data[1]))
221
elif type in ("blob", "tree"):
222
self.db.execute("replace into %ss (sha1, fileid, revid) values (?, ?, ?)" % type, (sha, type_data[0], type_data[1]))
224
raise AssertionError("Unknown type %s" % type)
226
def lookup_tree(self, fileid, revid):
227
row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid,revid)).fetchone()
229
raise KeyError((fileid, revid))
230
return row[0].encode("utf-8")
232
def lookup_blob(self, fileid, revid):
233
row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revid)).fetchone()
235
raise KeyError((fileid, revid))
236
return row[0].encode("utf-8")
441
238
def lookup_git_sha(self, sha):
442
239
"""Lookup a Git sha in the database.
444
241
:param sha: Git object sha
445
242
:return: (type, type_data) with type_data:
446
commit: revid, tree sha, verifiers
243
revision: revid, tree sha
450
row = self.db.execute("select revid, tree_sha, testament3_sha1 from commits where sha1 = ?", (sha,)).fetchone()
245
def format(type, row):
246
return (type, (row[0].encode("utf-8"), row[1].encode("utf-8")))
247
row = self.db.execute("select revid, tree_sha from commits where sha1 = ?", (sha,)).fetchone()
451
248
if row is not None:
452
return ("commit", (row[0], row[1], {"testament3-sha1": row[2]}))
249
return format("commit", row)
453
250
row = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,)).fetchone()
454
251
if row is not None:
252
return format("blob", row)
456
253
row = self.db.execute("select fileid, revid from trees where sha1 = ?", (sha,)).fetchone()
457
254
if row is not None:
255
return format("tree", row)
459
256
raise KeyError(sha)
461
258
def revids(self):
462
259
"""List the revision ids known."""
463
return (row for (row,) in self.db.execute("select revid from commits"))
260
for row in self.db.execute("select revid from commits").fetchall():
261
yield row[0].encode("utf-8")
466
264
"""List the SHA1s."""
467
265
for table in ("blobs", "commits", "trees"):
468
for (sha,) in self.db.execute("select sha1 from %s" % table):
472
class TdbCacheUpdater(CacheUpdater):
473
"""Cache updater for tdb-based caches."""
475
def __init__(self, cache, rev):
477
self.db = cache.idmap.db
478
self.revid = rev.revision_id
479
self.parent_revids = rev.parent_ids
483
def add_object(self, obj, ie, path):
484
sha = obj.sha().digest()
485
if obj.type_name == "commit":
486
self.db["commit\0" + self.revid] = "\0".join((sha, obj.tree))
487
assert type(ie) is dict, "was %r" % ie
488
type_data = (self.revid, obj.tree, ie["testament3-sha1"])
490
elif obj.type_name == "blob":
493
self.db["\0".join(("blob", ie.file_id, ie.revision))] = sha
494
type_data = (ie.file_id, ie.revision)
495
elif obj.type_name == "tree":
498
type_data = (ie.file_id, self.revid)
501
self.db["git\0" + sha] = "\0".join((obj.type_name, ) + type_data)
504
if self._commit is None:
505
raise AssertionError("No commit object added")
509
TdbBzrGitCache = lambda p: BzrGitCache(TdbGitShaMap(p), None, TdbCacheUpdater)
511
class TdbGitCacheFormat(BzrGitCacheFormat):
512
"""Cache format for tdb-based caches."""
514
def get_format_string(self):
515
return 'bzr-git sha map version 3 using tdb\n'
517
def open(self, transport):
519
basepath = transport.local_abspath(".")
520
except bzrlib.errors.NotLocalUrl:
521
basepath = get_cache_dir()
523
return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
526
"Unable to open existing bzr-git cache because 'tdb' is not "
266
for row in self.db.execute("select sha1 from %s" % table).fetchall():
267
yield row[0].encode("utf-8")
271
TDB_HASH_SIZE = 50000
530
274
class TdbGitShaMap(GitShaMap):
550
291
if not mapdbs().has_key(path):
551
mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
292
mapdbs()[path] = tdb.Tdb(path, TDB_HASH_SIZE, tdb.DEFAULT,
552
293
os.O_RDWR|os.O_CREAT)
553
self.db = mapdbs()[path]
555
if int(self.db["version"]) not in (2, 3):
294
self.db = mapdbs()[path]
295
if not "version" in self.db:
296
self.db["version"] = str(TDB_MAP_VERSION)
298
if int(self.db["version"]) != TDB_MAP_VERSION:
556
299
trace.warning("SHA Map is incompatible (%s -> %d), rebuilding database.",
557
self.db["version"], self.TDB_MAP_VERSION)
300
self.db["version"], TDB_MAP_VERSION)
302
self.db["version"] = str(TDB_MAP_VERSION)
305
def from_repository(cls, repository):
307
transport = getattr(repository, "_transport", None)
308
if transport is not None:
309
return cls(os.path.join(transport.local_abspath("."), "git.tdb"))
310
except bzrlib.errors.NotLocalUrl:
561
self.db["version"] = str(self.TDB_MAP_VERSION)
563
def start_write_group(self):
564
"""Start writing changes."""
565
self.db.transaction_start()
567
def commit_write_group(self):
568
"""Commit any pending changes."""
569
self.db.transaction_commit()
571
def abort_write_group(self):
572
"""Abort any pending changes."""
573
self.db.transaction_cancel()
576
return "%s(%r)" % (self.__class__.__name__, self.path)
312
return cls(os.path.join(get_cache_dir(), "remote.tdb"))
578
314
def lookup_commit(self, revid):
579
315
return sha_to_hex(self.db["commit\0" + revid][:20])
581
def lookup_blob_id(self, fileid, revision):
582
return sha_to_hex(self.db["\0".join(("blob", fileid, revision))])
320
def add_entry(self, hexsha, type, type_data):
321
"""Add a new entry to the database.
326
sha = hex_to_sha(hexsha)
327
self.db["git\0" + sha] = "\0".join((type, type_data[0], type_data[1]))
329
self.db["commit\0" + type_data[0]] = "\0".join((sha, type_data[1]))
331
self.db["\0".join((type, type_data[0], type_data[1]))] = sha
333
def lookup_tree(self, fileid, revid):
334
sha = self.db["\0".join(("tree", fileid, revid))]
338
return sha_to_hex(sha)
340
def lookup_blob(self, fileid, revid):
341
return sha_to_hex(self.db["\0".join(("blob", fileid, revid))])
584
343
def lookup_git_sha(self, sha):
585
344
"""Lookup a Git sha in the database.
587
346
:param sha: Git object sha
588
347
:return: (type, type_data) with type_data:
589
commit: revid, tree sha
348
revision: revid, tree sha
593
350
if len(sha) == 40:
594
351
sha = hex_to_sha(sha)
595
352
data = self.db["git\0" + sha].split("\0")
596
if data[0] == "commit":
598
return (data[0], (data[1], data[2], {}))
600
return (data[0], (data[1], data[2], {"testament3-sha1": data[3]}))
602
return (data[0], tuple(data[1:]))
604
def missing_revisions(self, revids):
607
if self.db.get("commit\0" + revid) is None:
353
return (data[0], (data[1], data[2]))
611
355
def revids(self):
612
356
"""List the revision ids known."""
619
363
for key in self.db.iterkeys():
620
364
if key.startswith("git\0"):
621
365
yield sha_to_hex(key[4:])
624
class VersionedFilesContentCache(ContentCache):
626
def __init__(self, vf):
630
self._vf.insert_record_stream(
631
[versionedfile.ChunkedContentFactory((obj.id,), [], None,
632
obj.as_legacy_object_chunks())])
634
def __getitem__(self, sha):
635
stream = self._vf.get_record_stream([(sha,)], 'unordered', True)
636
entry = stream.next()
637
if entry.storage_kind == 'absent':
639
return ShaFile._parse_legacy_object(entry.get_bytes_as('fulltext'))
642
class GitObjectStoreContentCache(ContentCache):
644
def __init__(self, store):
647
def add_multi(self, objs):
648
self.store.add_objects(objs)
650
def add(self, obj, path):
651
self.store.add_object(obj)
653
def __getitem__(self, sha):
654
return self.store[sha]
657
class IndexCacheUpdater(CacheUpdater):
659
def __init__(self, cache, rev):
661
self.revid = rev.revision_id
662
self.parent_revids = rev.parent_ids
665
self._cache_objs = set()
667
def add_object(self, obj, ie, path):
668
if obj.type_name == "commit":
670
assert type(ie) is dict
671
self.cache.idmap._add_git_sha(obj.id, "commit",
672
(self.revid, obj.tree, ie))
673
self.cache.idmap._add_node(("commit", self.revid, "X"),
674
" ".join((obj.id, obj.tree)))
675
self._cache_objs.add((obj, path))
676
elif obj.type_name == "blob":
677
self.cache.idmap._add_git_sha(obj.id, "blob",
678
(ie.file_id, ie.revision))
679
self.cache.idmap._add_node(("blob", ie.file_id, ie.revision), obj.id)
680
if ie.kind == "symlink":
681
self._cache_objs.add((obj, path))
682
elif obj.type_name == "tree":
683
self.cache.idmap._add_git_sha(obj.id, "tree",
684
(ie.file_id, self.revid))
685
self._cache_objs.add((obj, path))
690
self.cache.content_cache.add_multi(self._cache_objs)
694
class IndexBzrGitCache(BzrGitCache):
696
def __init__(self, transport=None):
697
mapper = versionedfile.ConstantMapper("trees")
698
shamap = IndexGitShaMap(transport.clone('index'))
699
#trees_store = knit.make_file_factory(True, mapper)(transport)
700
#content_cache = VersionedFilesContentCache(trees_store)
701
from bzrlib.plugins.git.transportgit import TransportObjectStore
702
store = TransportObjectStore(transport.clone('objects'))
703
content_cache = GitObjectStoreContentCache(store)
704
super(IndexBzrGitCache, self).__init__(shamap, content_cache,
708
class IndexGitCacheFormat(BzrGitCacheFormat):
710
def get_format_string(self):
711
return 'bzr-git sha map with git object cache version 1\n'
713
def initialize(self, transport):
714
super(IndexGitCacheFormat, self).initialize(transport)
715
transport.mkdir('index')
716
transport.mkdir('objects')
717
from bzrlib.plugins.git.transportgit import TransportObjectStore
718
TransportObjectStore.init(transport.clone('objects'))
720
def open(self, transport):
721
return IndexBzrGitCache(transport)
724
class IndexGitShaMap(GitShaMap):
725
"""SHA Map that uses the Bazaar APIs to store a cache.
727
BTree Index file with the following contents:
729
("git", <sha1>) -> "<type> <type-data1> <type-data2>"
730
("commit", <revid>) -> "<sha1> <tree-id>"
731
("blob", <fileid>, <revid>) -> <sha1>
735
def __init__(self, transport=None):
736
if transport is None:
737
self._transport = None
738
self._index = _mod_index.InMemoryGraphIndex(0, key_elements=3)
739
self._builder = self._index
742
self._transport = transport
743
self._index = _mod_index.CombinedGraphIndex([])
744
for name in self._transport.list_dir("."):
745
if not name.endswith(".rix"):
747
x = _mod_btree_index.BTreeGraphIndex(self._transport, name,
748
self._transport.stat(name).st_size)
749
self._index.insert_index(0, x)
752
def from_repository(cls, repository):
753
transport = getattr(repository, "_transport", None)
754
if transport is not None:
756
transport.mkdir('git')
757
except bzrlib.errors.FileExists:
759
return cls(transport.clone('git'))
760
from bzrlib.transport import get_transport
761
return cls(get_transport(get_cache_dir()))
764
if self._transport is not None:
765
return "%s(%r)" % (self.__class__.__name__, self._transport.base)
767
return "%s()" % (self.__class__.__name__)
770
assert self._builder is None
771
self.start_write_group()
772
for _, key, value in self._index.iter_all_entries():
773
self._builder.add_node(key, value)
775
for name in self._transport.list_dir('.'):
776
if name.endswith('.rix'):
777
to_remove.append(name)
778
self.commit_write_group()
779
del self._index.indices[1:]
780
for name in to_remove:
781
self._transport.rename(name, name + '.old')
783
def start_write_group(self):
784
assert self._builder is None
785
self._builder = _mod_btree_index.BTreeBuilder(0, key_elements=3)
786
self._name = osutils.sha()
788
def commit_write_group(self):
789
assert self._builder is not None
790
stream = self._builder.finish()
791
name = self._name.hexdigest() + ".rix"
792
size = self._transport.put_file(name, stream)
793
index = _mod_btree_index.BTreeGraphIndex(self._transport, name, size)
794
self._index.insert_index(0, index)
798
def abort_write_group(self):
799
assert self._builder is not None
803
def _add_node(self, key, value):
805
self._builder.add_node(key, value)
806
except bzrlib.errors.BadIndexDuplicateKey:
807
# Multiple bzr objects can have the same contents
812
def _get_entry(self, key):
813
entries = self._index.iter_entries([key])
815
return entries.next()[2]
816
except StopIteration:
817
if self._builder is None:
819
entries = self._builder.iter_entries([key])
821
return entries.next()[2]
822
except StopIteration:
825
def _iter_keys_prefix(self, prefix):
826
for entry in self._index.iter_entries_prefix([prefix]):
828
if self._builder is not None:
829
for entry in self._builder.iter_entries_prefix([prefix]):
832
def lookup_commit(self, revid):
833
return self._get_entry(("commit", revid, "X"))[:40]
835
def _add_git_sha(self, hexsha, type, type_data):
836
if hexsha is not None:
837
self._name.update(hexsha)
839
td = (type_data[0], type_data[1], type_data[2]["testament3-sha1"])
842
self._add_node(("git", hexsha, "X"), " ".join((type,) + td))
844
# This object is not represented in Git - perhaps an empty
846
self._name.update(type + " ".join(type_data))
848
def lookup_blob_id(self, fileid, revision):
849
return self._get_entry(("blob", fileid, revision))
851
def lookup_git_sha(self, sha):
853
sha = sha_to_hex(sha)
854
data = self._get_entry(("git", sha, "X")).split(" ", 3)
855
if data[0] == "commit":
856
return ("commit", (data[1], data[2], {"testament3-sha1": data[3]}))
858
return (data[0], tuple(data[1:]))
861
"""List the revision ids known."""
862
for key in self._iter_keys_prefix(("commit", None, None)):
865
def missing_revisions(self, revids):
866
"""Return set of all the revisions that are not present."""
867
missing_revids = set(revids)
868
for _, key, value in self._index.iter_entries((
869
("commit", revid, "X") for revid in revids)):
870
missing_revids.remove(key[1])
871
return missing_revids
874
"""List the SHA1s."""
875
for key in self._iter_keys_prefix(("git", None, None)):
879
formats = registry.Registry()
880
formats.register(TdbGitCacheFormat().get_format_string(),
882
formats.register(SqliteGitCacheFormat().get_format_string(),
883
SqliteGitCacheFormat())
884
formats.register(IndexGitCacheFormat().get_format_string(),
885
IndexGitCacheFormat())
886
# In the future, this will become the default:
887
# formats.register('default', IndexGitCacheFormat())
891
formats.register('default', SqliteGitCacheFormat())
893
formats.register('default', TdbGitCacheFormat())
897
def migrate_ancient_formats(repo_transport):
898
# Prefer migrating git.db over git.tdb, since the latter may not
899
# be openable on some platforms.
900
if repo_transport.has("git.db"):
901
SqliteGitCacheFormat().initialize(repo_transport.clone("git"))
902
repo_transport.rename("git.db", "git/idmap.db")
903
elif repo_transport.has("git.tdb"):
904
TdbGitCacheFormat().initialize(repo_transport.clone("git"))
905
repo_transport.rename("git.tdb", "git/idmap.tdb")
908
def remove_readonly_transport_decorator(transport):
909
if transport.is_readonly():
910
return transport._decorated
914
def from_repository(repository):
915
"""Open a cache file for a repository.
917
If the repository is remote and there is no transport available from it
918
this will use a local file in the users cache directory
919
(typically ~/.cache/bazaar/git/)
921
:param repository: A repository object
923
repo_transport = getattr(repository, "_transport", None)
924
if repo_transport is not None:
925
# Migrate older cache formats
926
repo_transport = remove_readonly_transport_decorator(repo_transport)
928
repo_transport.mkdir("git")
929
except bzrlib.errors.FileExists:
932
migrate_ancient_formats(repo_transport)
933
return BzrGitCacheFormat.from_repository(repository)