75
75
return _mapdbs.cache
78
class InventorySHAMap(object):
79
"""Maps inventory file ids to Git SHAs."""
81
def lookup_blob(self, file_id, revision):
82
"""Retrieve a Git blob SHA by file id.
84
:param file_id: File id of the file/symlink
85
:param revision: revision in which the file was last changed.
87
raise NotImplementedError(self.lookup_blob)
89
def lookup_tree(self, file_id):
90
"""Retrieve a Git tree SHA by file id.
92
raise NotImplementedError(self.lookup_tree)
78
95
class GitShaMap(object):
79
96
"""Git<->Bzr revision id mapping database."""
81
def add_entry(self, sha, type, type_data):
98
def _add_entry(self, sha, type, type_data):
82
99
"""Add a new entry to the database.
84
raise NotImplementedError(self.add_entry)
101
raise NotImplementedError(self._add_entry)
86
def add_entries(self, entries):
103
def add_entries(self, revid, parent_revids, commit_sha, root_tree_sha,
87
105
"""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)
107
for (fileid, kind, hexsha, revision) in entries:
108
self._add_entry(hexsha, kind, (fileid, revision))
109
self._add_entry(commit_sha, "commit", (revid, root_tree_sha))
111
def get_inventory_sha_map(self, revid):
112
"""Return the inventory SHA map for a revision.
114
:param revid: Revision to fetch the map for
115
:return: A `InventorySHAMap`
117
raise NotImplementedError(self.get_inventory_sha_map)
100
119
def lookup_git_sha(self, sha):
101
120
"""Lookup a Git sha in the database.
103
121
:param sha: Git object sha
104
122
:return: (type, type_data) with type_data:
105
123
revision: revid, tree sha
134
152
class DictGitShaMap(GitShaMap):
136
154
def __init__(self):
139
def add_entry(self, sha, type, type_data):
140
self.dict[sha] = (type, type_data)
158
def _add_entry(self, sha, type, type_data):
159
self._by_sha[sha] = (type, type_data)
160
if type in ("blob", "tree"):
161
self._by_fileid.setdefault(type_data[1], {})[type_data[0]] = sha
163
def get_inventory_sha_map(self, revid):
165
class DictInventorySHAMap(InventorySHAMap):
167
def __init__(self, base, revid):
171
def lookup_blob(self, fileid, revision):
172
return self._base._by_fileid[revision][fileid]
174
def lookup_tree(self, fileid):
175
return self._base._by_fileid[self.revid][fileid]
177
return DictInventorySHAMap(self, revid)
142
179
def lookup_git_sha(self, sha):
143
return self.dict[sha]
145
def lookup_tree(self, fileid, revid):
146
for k, v in self.dict.iteritems():
147
if v == ("tree", (fileid, revid)):
149
raise KeyError((fileid, revid))
151
def lookup_blob(self, fileid, revid):
152
for k, v in self.dict.iteritems():
153
if v == ("blob", (fileid, revid)):
155
raise KeyError((fileid, revid))
180
return self._by_sha[sha]
157
182
def revids(self):
158
for key, (type, type_data) in self.dict.iteritems():
183
for key, (type, type_data) in self._by_sha.iteritems():
159
184
if type == "commit":
160
185
yield type_data[0]
163
return self.dict.iterkeys()
188
return self._by_sha.iterkeys()
166
191
class SqliteGitShaMap(GitShaMap):
190
215
create index if not exists blobs_sha1 on blobs(sha1);
191
216
create unique index if not exists blobs_fileid_revid on blobs(fileid, revid);
192
217
create table if not exists trees(
193
sha1 text not null check(length(sha1) == 40),
218
sha1 text unique not null check(length(sha1) == 40),
194
219
fileid text not null,
195
220
revid text not null
197
create index if not exists trees_sha1 on trees(sha1);
222
create unique index if not exists trees_sha1 on trees(sha1);
198
223
create unique index if not exists trees_fileid_revid on trees(fileid, revid);
227
return "%s(%r)" % (self.__class__.__name__, self.path)
202
230
def from_repository(cls, repository):
217
245
def commit_write_group(self):
220
def add_entries(self, entries):
248
def add_entries(self, revid, parent_revids, commit_sha, root_tree_sha,
223
for sha, type, type_data in entries:
224
assert isinstance(type_data[0], str)
225
assert isinstance(type_data[1], str)
226
entry = (sha, type_data[0], type_data[1])
252
for (fileid, kind, hexsha, revision) in entries:
256
trees.append((hexsha, fileid, revid))
258
blobs.append((hexsha, fileid, revision))
232
260
raise AssertionError
234
262
self.db.executemany("replace into trees (sha1, fileid, revid) values (?, ?, ?)", trees)
236
264
self.db.executemany("replace into blobs (sha1, fileid, revid) values (?, ?, ?)", blobs)
239
def add_entry(self, sha, type, type_data):
265
self._add_entry(commit_sha, "commit", (revid, root_tree_sha))
267
def _add_entry(self, sha, type, type_data):
240
268
"""Add a new entry to the database.
242
270
assert isinstance(type_data, tuple)
243
273
assert isinstance(sha, str), "type was %r" % sha
244
274
if type == "commit":
245
275
self.db.execute("replace into commits (sha1, revid, tree_sha) values (?, ?, ?)", (sha, type_data[0], type_data[1]))
249
279
raise AssertionError("Unknown type %s" % type)
251
def lookup_tree(self, fileid, revid):
252
row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid,revid)).fetchone()
254
raise KeyError((fileid, revid))
257
def lookup_blob(self, fileid, revid):
258
row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revid)).fetchone()
260
raise KeyError((fileid, revid))
281
def get_inventory_sha_map(self, revid):
282
class SqliteInventorySHAMap(InventorySHAMap):
284
def __init__(self, db, revid):
288
def lookup_blob(self, fileid, revision):
289
row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revision)).fetchone()
292
raise KeyError(fileid)
294
def lookup_tree(self, fileid):
295
row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid, self.revid)).fetchone()
298
raise KeyError(fileid)
300
return SqliteInventorySHAMap(self.db, revid)
263
302
def lookup_git_sha(self, sha):
264
303
"""Lookup a Git sha in the database.
283
322
def revids(self):
284
323
"""List the revision ids known."""
285
for row in self.db.execute("select revid from commits").fetchall():
324
return (row for (row,) in self.db.execute("select revid from commits"))
289
327
"""List the SHA1s."""
290
328
for table in ("blobs", "commits", "trees"):
291
for row in self.db.execute("select sha1 from %s" % table).fetchall():
330
for (row,) in self.db.execute("select sha1 from %s" % table):
296
335
TDB_HASH_SIZE = 50000
318
357
os.O_RDWR|os.O_CREAT)
319
358
self.db = mapdbs()[path]
321
if int(self.db["version"]) != TDB_MAP_VERSION:
360
if int(self.db["version"]) not in (2, 3):
322
361
trace.warning("SHA Map is incompatible (%s -> %d), rebuilding database.",
323
362
self.db["version"], TDB_MAP_VERSION)
325
self.db["version"] = str(TDB_MAP_VERSION)
327
self.db["version"] = str(TDB_MAP_VERSION)
366
self.db["version"] = str(TDB_MAP_VERSION)
368
def start_write_group(self):
369
"""Start writing changes."""
370
self.db.transaction_start()
372
def commit_write_group(self):
373
"""Commit any pending changes."""
374
self.db.transaction_commit()
376
def abort_write_group(self):
377
"""Abort any pending changes."""
378
self.db.transaction_cancel()
381
return "%s(%r)" % (self.__class__.__name__, self.path)
330
384
def from_repository(cls, repository):
339
393
def lookup_commit(self, revid):
340
394
return sha_to_hex(self.db["commit\0" + revid][:20])
342
def add_entry(self, hexsha, type, type_data):
396
def _add_entry(self, hexsha, type, type_data):
343
397
"""Add a new entry to the database.
345
399
if hexsha is None:
349
403
self.db["git\0" + sha] = "\0".join((type, type_data[0], type_data[1]))
350
404
if type == "commit":
351
405
self.db["commit\0" + type_data[0]] = "\0".join((sha, type_data[1]))
353
self.db["\0".join((type, type_data[0], type_data[1]))] = sha
355
def lookup_tree(self, fileid, revid):
356
sha = self.db["\0".join(("tree", fileid, revid))]
360
return sha_to_hex(sha)
362
def lookup_blob(self, fileid, revid):
363
return sha_to_hex(self.db["\0".join(("blob", fileid, revid))])
407
self.db["\0".join(("blob", type_data[0], type_data[1]))] = sha
409
def get_inventory_sha_map(self, revid):
411
class TdbInventorySHAMap(InventorySHAMap):
413
def __init__(self, db, revid):
417
def lookup_blob(self, fileid, revision):
418
return sha_to_hex(self.db["\0".join(("blob", fileid, revision))])
420
return TdbInventorySHAMap(self.db, revid)
365
422
def lookup_git_sha(self, sha):
366
423
"""Lookup a Git sha in the database.