32
from bzrlib.transport import get_transport
30
34
from bzrlib.plugins.git import (
36
44
class GitRepository(repository.Repository):
37
45
"""An adapter to git repositories for bzr."""
39
# To make bzrlib happy
42
49
def __init__(self, gitdir, lockfiles):
43
50
self.bzrdir = gitdir
44
51
self.control_files = lockfiles
45
52
gitdirectory = gitdir.transport.local_abspath('.')
53
self.base = gitdirectory
46
54
self._git = model.GitModel(gitdirectory)
47
55
self._revision_cache = {}
48
56
self._blob_cache = {}
49
self._entry_revision_cache = {}
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
def _init_cachedb(self):
67
self.cachedb.executescript("""
68
create table if not exists inventory (
70
create unique index if not exists inventory_revid
72
create table if not exists entry_revision (
78
create unique index if not exists entry_revision_revid_path
79
on entry_revision (inventory, path);
51
84
def _ancestor_revisions(self, revision_ids):
52
85
if revision_ids is not None:
104
137
def get_signature_text(self, revision_id):
105
138
raise errors.NoSuchRevision(self, revision_id)
107
def get_inventory_xml(self, revision_id):
108
"""See Repository.get_inventory_xml()."""
109
return bzrlib.xml5.serializer_v5.write_inventory_to_string(
110
self.get_inventory(revision_id))
112
def get_inventory_sha1(self, revision_id):
113
"""Get the sha1 for the XML representation of an inventory.
115
:param revision_id: Revision id of the inventory for which to return
120
return osutils.sha_string(self.get_inventory_xml(revision_id))
122
def get_revision_xml(self, revision_id):
123
"""Return the XML representation of a revision.
125
:param revision_id: Revision for which to return the XML.
128
return bzrlib.xml5.serializer_v5.write_revision_to_string(
129
self.get_revision(revision_id))
131
141
def get_revision(self, revision_id):
132
142
if revision_id in self._revision_cache:
317
354
max_count=1, topo_order=True, paths=[path])
319
356
result = ids.convert_revision_id_git_to_bzr(line[:-1])
320
# print "fetched file revision", line[:-1], path
357
print "fetched file revision", line[:-1], path
360
def _get_entry_revision_from_db(self, revid, path, git_id, executable):
361
result = self.cachedb.execute(
362
"select revision from entry_revision where"
363
" inventory=? and path=? and gitid=? and executable=?",
364
(revid, path, git_id, executable)).fetchone()
370
def _set_entry_revision_in_db(self, revid, path, git_id, executable, revision):
371
self.cachedb.execute(
372
"insert into entry_revision"
373
" (inventory, path, gitid, executable, revision)"
374
" values (?, ?, ?, ?, ?)",
375
(revid, path, git_id, executable, revision))
377
def _all_inventories_in_db(self, revids):
379
result = self.cachedb.execute(
380
"select count(*) from inventory where revid = ?",
323
386
def _set_entry_revision(self, entry, revid, path, git_id):
324
387
# If a revision is in the cache, we assume it contains entries for the
325
388
# whole inventory. So if all parent revisions are in the cache, but no
326
389
# parent entry is present, then the entry revision is the current
327
390
# revision. That amortizes the number of _get_file_revision calls for
328
391
# large pulls to a "small number".
329
cached = self._entry_revision_cache.get(revid, {}).get(
330
(path, git_id, entry.executable))
331
if cached is not None:
332
entry.revision = cached
392
entry_rev = self._get_entry_revision_from_db(
393
revid, path, git_id, entry.executable)
394
if entry_rev is not None:
395
entry.revision = entry_rev
334
398
revision = self.get_revision(revid)
335
all_parents_in_cache = True
336
399
for parent_id in revision.parent_ids:
337
if parent_id not in self._entry_revision_cache:
338
all_parents_in_cache = False
340
entry_rev = self._entry_revision_cache[parent_id].get(
341
(path, git_id, entry.executable))
400
entry_rev = self._get_entry_revision_from_db(
401
parent_id, path, git_id, entry.executable)
342
402
if entry_rev is not None:
345
if all_parents_in_cache:
405
if self._all_inventories_in_db(revision.parent_ids):
346
406
entry_rev = revid
348
408
entry_rev = self._get_file_revision(revid, path)
349
self._entry_revision_cache.setdefault(
350
revid, {})[(path, git_id, entry.executable)] = entry_rev
409
self._set_entry_revision_in_db(
410
revid, path, git_id, entry.executable, entry_rev)
411
#self.cachedb.commit()
351
412
entry.revision = entry_rev
354
415
def escape_file_id(file_id):
355
416
return file_id.replace('_', '__').replace(' ', '_s')
419
def escape_for_xml(message):
420
"""Replace xml-incompatible control characters."""
421
# Copied from _escape_commit_message from bzr-svn.
422
# -- David Allouche 2007-12-29.
426
# FIXME: RBC 20060419 this should be done by the revision
427
# serialiser not by commit. Then we can also add an unescaper
428
# in the deserializer and start roundtripping revision messages
429
# precisely. See repository_implementations/test_repository.py
431
# Python strings can include characters that can't be
432
# represented in well-formed XML; escape characters that
433
# aren't listed in the XML specification
434
# (http://www.w3.org/TR/REC-xml/#NT-Char).
435
message, _ = re.subn(
436
u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
437
lambda match: match.group(0).encode('unicode_escape'),
357
442
class GitRevisionTree(revisiontree.RevisionTree):
359
444
def __init__(self, repository, revision_id):