52
63
self.base = gitdir.root_transport.base
53
64
self.bzrdir = gitdir
54
65
self.control_files = lockfiles
55
self._git = git.repo.Repo(gitdir.root_transport.local_abspath("."))
57
self._blob_info_cache = {}
58
cache_dir = cache.create_cache_dir()
59
cachedir_transport = get_transport(cache_dir)
60
cache_file = os.path.join(cache_dir, 'cache-%s' % ids.NAMESPACE)
61
if not cachedbs.has_key(cache_file):
62
cachedbs[cache_file] = cache.sqlite3.connect(cache_file)
63
self.cachedb = cachedbs[cache_file]
66
self._git = gitdir._git
66
self.signatures = versionedfile.VirtualSignatureTexts(self)
68
self.signatures = versionedfiles.VirtualSignatureTexts(self)
69
self.revisions = versionedfiles.VirtualRevisionTexts(self)
68
70
self._format = GitFormat()
69
71
self._fallback_repositories = []
72
self.tags = GitTags(self._git.get_tags())
71
def _init_cachedb(self):
72
self.cachedb.executescript("""
73
create table if not exists inventory (
75
create unique index if not exists inventory_revid
77
create table if not exists entry_revision (
83
create unique index if not exists entry_revision_revid_path
84
on entry_revision (inventory, path);
74
def _all_revision_ids(self):
75
if self._git.heads == []:
82
cms = self._git.commits("--all", max_count=max_count, skip=skip)
84
ret.update([default_mapping.revision_id_foreign_to_bzr(cm.id) for cm in cms])
88
87
def is_shared(self):
113
117
def get_parent_map(self, revision_ids):
115
119
for revid in revision_ids:
116
commit = self._git.commit(ids.convert_revision_id_bzr_to_git(revid))
117
ret[revid] = tuple([ids.convert_revision_id_git_to_bzr(p.id) for p in commit.parents])
120
if revid == revision.NULL_REVISION:
123
commit = self._git.commit(self.lookup_git_revid(revid, default_mapping))
124
ret[revid] = tuple([default_mapping.revision_id_foreign_to_bzr(p.id) for p in commit.parents])
127
def lookup_git_revid(self, bzr_revid, mapping):
129
return mapping.revision_id_bzr_to_foreign(bzr_revid)
130
except errors.InvalidRevisionId:
131
raise errors.NoSuchRevision(bzr_revid, self)
120
133
def get_revision(self, revision_id):
121
git_commit_id = ids.convert_revision_id_bzr_to_git(revision_id)
134
git_commit_id = self.lookup_git_revid(revision_id, default_mapping)
122
135
commit = self._git.commit(git_commit_id)
123
136
# print "fetched revision:", git_commit_id
124
revision = self._parse_rev(commit)
137
revision = self._parse_rev(commit, default_mapping)
127
140
def has_revision(self, revision_id):
166
180
return GitRevisionTree(self, revision_id)
168
def _fetch_blob(self, git_id):
169
lines = self._git.cat_file('blob', git_id)
170
# print "fetched blob:", git_id
171
if self._building_inventory is not None:
172
self._building_inventory.git_file_data[git_id] = lines
175
def _get_blob(self, git_id):
177
return self._blob_cache[git_id]
179
return self._fetch_blob(git_id)
181
def _get_blob_caching(self, git_id):
183
return self._blob_cache[git_id]
185
lines = self._fetch_blob(git_id)
186
self._blob_cache[git_id] = lines
189
def _get_blob_info(self, git_id):
191
return self._blob_info_cache[git_id]
193
lines = self._get_blob(git_id)
194
size = sum(len(line) for line in lines)
195
sha1 = osutils.sha_strings(lines)
196
self._blob_info_cache[git_id] = (size, sha1)
199
182
def get_inventory(self, revision_id):
200
183
assert revision_id != None
201
184
return self.revision_tree(revision_id).inventory
203
def _set_entry_text_info(self, inv, entry, git_id):
204
if entry.kind == 'directory':
206
size, sha1 = self._get_blob_info(git_id)
207
entry.text_size = size
208
entry.text_sha1 = sha1
209
if entry.kind == 'symlink':
210
lines = self._get_blob_caching(git_id)
211
entry.symlink_target = ''.join(lines)
213
def _get_file_revision(self, revision_id, path):
214
lines = self._git.rev_list(
215
[ids.convert_revision_id_bzr_to_git(revision_id)],
216
max_count=1, topo_order=True, paths=[path])
218
result = ids.convert_revision_id_git_to_bzr(line[:-1])
219
# print "fetched file revision", line[:-1], path
222
def _get_entry_revision_from_db(self, revid, path, git_id, executable):
223
result = self.cachedb.execute(
224
"select revision from entry_revision where"
225
" inventory=? and path=? and gitid=? and executable=?",
226
(revid, path, git_id, executable)).fetchone()
232
def _set_entry_revision_in_db(self, revid, path, git_id, executable, revision):
233
self.cachedb.execute(
234
"insert into entry_revision"
235
" (inventory, path, gitid, executable, revision)"
236
" values (?, ?, ?, ?, ?)",
237
(revid, path, git_id, executable, revision))
239
def _all_inventories_in_db(self, revids):
241
result = self.cachedb.execute(
242
"select count(*) from inventory where revid = ?",
248
def _set_entry_revision(self, entry, revid, path, git_id):
249
# If a revision is in the cache, we assume it contains entries for the
250
# whole inventory. So if all parent revisions are in the cache, but no
251
# parent entry is present, then the entry revision is the current
252
# revision. That amortizes the number of _get_file_revision calls for
253
# large pulls to a "small number".
254
entry_rev = self._get_entry_revision_from_db(
255
revid, path, git_id, entry.executable)
256
if entry_rev is not None:
257
entry.revision = entry_rev
260
revision = self.get_revision(revid)
261
for parent_id in revision.parent_ids:
262
entry_rev = self._get_entry_revision_from_db(
263
parent_id, path, git_id, entry.executable)
264
if entry_rev is not None:
267
if self._all_inventories_in_db(revision.parent_ids):
270
entry_rev = self._get_file_revision(revid, path)
271
self._set_entry_revision_in_db(
272
revid, path, git_id, entry.executable, entry_rev)
273
#self.cachedb.commit()
274
entry.revision = entry_rev
186
def set_make_working_trees(self, trees):
277
190
def escape_file_id(file_id):
278
191
return file_id.replace('_', '__').replace(' ', '_s')
194
def unescape_file_id(file_id):
195
return file_id.replace("_s", " ").replace("__", "_")
281
198
class GitRevisionTree(revisiontree.RevisionTree):
283
200
def __init__(self, repository, revision_id):
284
201
self._repository = repository
285
202
self.revision_id = revision_id
286
git_id = ids.convert_revision_id_bzr_to_git(revision_id)
203
git_id = repository.lookup_git_revid(revision_id, default_mapping)
287
204
self.tree = repository._git.commit(git_id).tree
288
205
self._inventory = inventory.Inventory(revision_id=revision_id)
289
206
self._inventory.root.revision = revision_id
290
207
self._build_inventory(self.tree, self._inventory.root, "")
292
def get_file_lines(self, file_id):
209
def get_revision_id(self):
210
return self.revision_id
212
def get_file_text(self, file_id):
293
213
entry = self._inventory[file_id]
294
if entry.kind == 'directory': return []
295
git_id = self._inventory.git_ids[file_id]
296
if git_id in self._inventory.git_file_data:
297
return self._inventory.git_file_data[git_id]
298
return self._repository._get_blob(git_id)
214
if entry.kind == 'directory': return ""
215
return self._repository._git.blob(entry.text_id).data
300
217
def _build_inventory(self, tree, ie, path):
301
218
assert isinstance(path, str)