/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.200.252 by Jelmer Vernooij
Clarify history, copyright.
1
# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
0.200.226 by Jelmer Vernooij
Merge thin-pack work.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
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
16
17
"""Map from Git sha's to Bazaar objects."""
18
0.235.1 by Jelmer Vernooij
Store sha map more efficiently.
19
from dulwich.objects import (
20
    sha_to_hex,
21
    hex_to_sha,
22
    )
0.200.292 by Jelmer Vernooij
Fix formatting.
23
import os
0.200.365 by Jelmer Vernooij
Share sha map cache connections inside threads.
24
import threading
0.200.292 by Jelmer Vernooij
Fix formatting.
25
0.200.228 by Jelmer Vernooij
Split out map.
26
import bzrlib
0.200.528 by Jelmer Vernooij
Fix import.
27
from bzrlib import (
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
28
    registry,
0.200.528 by Jelmer Vernooij
Fix import.
29
    trace,
30
    )
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
31
from bzrlib.transport import (
32
    get_transport,
33
    )
0.200.230 by Jelmer Vernooij
Implement sha cache.
34
0.200.226 by Jelmer Vernooij
Merge thin-pack work.
35
0.200.534 by Jelmer Vernooij
Use XDG cache directory if the python xdg module is available.
36
def get_cache_dir():
37
    try:
38
        from xdg.BaseDirectory import xdg_cache_home
39
    except ImportError:
0.200.558 by Jelmer Vernooij
Create cache dir if it doesn't exist yet.
40
        from bzrlib.config import config_dir
41
        ret = os.path.join(config_dir(), "git")
0.200.534 by Jelmer Vernooij
Use XDG cache directory if the python xdg module is available.
42
    else:
0.200.558 by Jelmer Vernooij
Create cache dir if it doesn't exist yet.
43
        ret = os.path.join(xdg_cache_home, "bazaar", "git")
44
    if not os.path.isdir(ret):
45
        os.makedirs(ret)
46
    return ret
0.200.534 by Jelmer Vernooij
Use XDG cache directory if the python xdg module is available.
47
48
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
49
def get_remote_cache_transport():
50
    return get_transport(get_cache_dir())
51
52
0.200.228 by Jelmer Vernooij
Split out map.
53
def check_pysqlite_version(sqlite3):
54
    """Check that sqlite library is compatible.
55
56
    """
0.200.675 by Jelmer Vernooij
Fix formatting.
57
    if (sqlite3.sqlite_version_info[0] < 3 or
58
            (sqlite3.sqlite_version_info[0] == 3 and
0.200.228 by Jelmer Vernooij
Split out map.
59
             sqlite3.sqlite_version_info[1] < 3)):
0.200.586 by Jelmer Vernooij
Fix issues pointed out by pyflakes.
60
        trace.warning('Needs at least sqlite 3.3.x')
0.200.228 by Jelmer Vernooij
Split out map.
61
        raise bzrlib.errors.BzrError("incompatible sqlite library")
62
63
try:
64
    try:
65
        import sqlite3
66
        check_pysqlite_version(sqlite3)
0.200.675 by Jelmer Vernooij
Fix formatting.
67
    except (ImportError, bzrlib.errors.BzrError), e:
0.200.228 by Jelmer Vernooij
Split out map.
68
        from pysqlite2 import dbapi2 as sqlite3
69
        check_pysqlite_version(sqlite3)
70
except:
0.200.586 by Jelmer Vernooij
Fix issues pointed out by pyflakes.
71
    trace.warning('Needs at least Python2.5 or Python2.4 with the pysqlite2 '
0.200.228 by Jelmer Vernooij
Split out map.
72
            'module')
73
    raise bzrlib.errors.BzrError("missing sqlite library")
74
0.200.226 by Jelmer Vernooij
Merge thin-pack work.
75
0.200.365 by Jelmer Vernooij
Share sha map cache connections inside threads.
76
_mapdbs = threading.local()
77
def mapdbs():
78
    """Get a cache for this thread's db connections."""
79
    try:
80
        return _mapdbs.cache
81
    except AttributeError:
82
        _mapdbs.cache = {}
83
        return _mapdbs.cache
84
85
0.200.841 by Jelmer Vernooij
Eliminate InventorySHAMap.
86
class GitShaMap(object):
87
    """Git<->Bzr revision id mapping database."""
88
89
    def lookup_git_sha(self, sha):
90
        """Lookup a Git sha in the database.
91
        :param sha: Git object sha
92
        :return: (type, type_data) with type_data:
93
            revision: revid, tree sha
94
        """
95
        raise NotImplementedError(self.lookup_git_sha)
96
0.200.835 by Jelmer Vernooij
Rename lookup_{tree,blob} -> lookup_{tree,blob}_id.
97
    def lookup_blob_id(self, file_id, revision):
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
98
        """Retrieve a Git blob SHA by file id.
99
100
        :param file_id: File id of the file/symlink
0.200.806 by Jelmer Vernooij
Make revision_hint mandatory.
101
        :param revision: revision in which the file was last changed.
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
102
        """
0.200.835 by Jelmer Vernooij
Rename lookup_{tree,blob} -> lookup_{tree,blob}_id.
103
        raise NotImplementedError(self.lookup_blob_id)
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
104
0.200.841 by Jelmer Vernooij
Eliminate InventorySHAMap.
105
    def lookup_tree_id(self, file_id, revision):
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
106
        """Retrieve a Git tree SHA by file id.
107
        """
0.200.835 by Jelmer Vernooij
Rename lookup_{tree,blob} -> lookup_{tree,blob}_id.
108
        raise NotImplementedError(self.lookup_tree_id)
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
109
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
110
    def revids(self):
111
        """List the revision ids known."""
112
        raise NotImplementedError(self.revids)
113
0.200.677 by Jelmer Vernooij
Implement TdbCache.missing_revisions().
114
    def missing_revisions(self, revids):
115
        """Return set of all the revisions that are not present."""
116
        present_revids = set(self.revids())
117
        if not isinstance(revids, set):
118
            revids = set(revids)
119
        return revids - present_revids
120
0.200.586 by Jelmer Vernooij
Fix issues pointed out by pyflakes.
121
    def sha1s(self):
0.200.422 by Jelmer Vernooij
'bzr git-object' without arguments now prints the available git objects.
122
        """List the SHA1s."""
123
        raise NotImplementedError(self.sha1s)
124
0.200.687 by Jelmer Vernooij
Use start_write_group() / commit_write_group() mechanism when creating git SHA maps.
125
    def start_write_group(self):
126
        """Start writing changes."""
127
128
    def commit_write_group(self):
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
129
        """Commit any pending changes."""
130
0.200.687 by Jelmer Vernooij
Use start_write_group() / commit_write_group() mechanism when creating git SHA maps.
131
    def abort_write_group(self):
132
        """Abort any pending changes."""
133
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
134
0.200.845 by Jelmer Vernooij
Couple of minor fixes.
135
class ContentCache(object):
136
    """Object that can cache Git objects."""
137
138
    def __getitem__(self, sha):
139
        """Retrieve an item, by SHA."""
140
        raise NotImplementedError(self.__getitem__)
141
142
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
143
class BzrGitCacheFormat(object):
144
145
    def get_format_string(self):
0.200.866 by Jelmer Vernooij
More docstrings, prefer migrating git.db to migrating git.tdb.
146
        """Return a single-line unique format string for this cache format."""
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
147
        raise NotImplementedError(self.get_format_string)
148
149
    def open(self, transport):
0.200.866 by Jelmer Vernooij
More docstrings, prefer migrating git.db to migrating git.tdb.
150
        """Open this format on a transport."""
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
151
        raise NotImplementedError(self.open)
152
153
    def initialize(self, transport):
154
        transport.put_bytes('format', self.get_format_string())
155
156
    @classmethod
0.200.866 by Jelmer Vernooij
More docstrings, prefer migrating git.db to migrating git.tdb.
157
    def from_transport(self, transport):
158
        """Open a cache file present on a transport, or initialize one.
159
160
        :param transport: Transport to use
161
        :return: A BzrGitCache instance
162
        """
163
        try:
164
            format_name = transport.get_bytes('format')
165
            format = formats.get(format_name)
166
        except bzrlib.errors.NoSuchFile:
167
            format = formats.get('default')
168
            format.initialize(transport)
169
        return format.open(transport)
170
171
    @classmethod
172
    def from_repository(cls, repository):
173
        """Open a cache file for a repository.
174
175
        This will use the repository's transport to store the cache file, or
176
        use the users global cache directory if the repository has no 
177
        transport associated with it.
178
179
        :param repository: Repository to open the cache for
180
        :return: A `BzrGitCache`
181
        """
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
182
        repo_transport = getattr(repository, "_transport", None)
183
        if repo_transport is not None:
0.200.865 by Jelmer Vernooij
Support serving without --allow-writes.
184
            # Even if we don't write to this repo, we should be able 
185
            # to update its cache.
186
            repo_transport = remove_readonly_transport_decorator(repo_transport)
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
187
            try:
188
                repo_transport.mkdir('git')
189
            except bzrlib.errors.FileExists:
190
                pass
191
            transport = repo_transport.clone('git')
192
        else:
193
            transport = get_remote_cache_transport()
0.200.866 by Jelmer Vernooij
More docstrings, prefer migrating git.db to migrating git.tdb.
194
        return cls.from_transport(transport)
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
195
196
0.200.847 by Jelmer Vernooij
Add BzrGitCache object.
197
class CacheUpdater(object):
198
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
199
    def add_object(self, obj, ie):
200
        raise NotImplementedError(self.add_object)
201
202
    def finish(self):
203
        raise NotImplementedError(self.finish)
204
205
206
class BzrGitCache(object):
207
    """Caching backend."""
208
209
    def __init__(self, idmap, content_cache, cache_updater_klass):
210
        self.idmap = idmap
211
        self.content_cache = content_cache
212
        self._cache_updater_klass = cache_updater_klass
213
214
    def get_updater(self, rev):
215
        return self._cache_updater_klass(self, rev)
216
217
218
DictBzrGitCache = lambda: BzrGitCache(DictGitShaMap(), None, DictCacheUpdater)
219
220
221
class DictCacheUpdater(CacheUpdater):
222
223
    def __init__(self, cache, rev):
0.200.847 by Jelmer Vernooij
Add BzrGitCache object.
224
        self.cache = cache
225
        self.revid = rev.revision_id
226
        self.parent_revids = rev.parent_ids
227
        self._commit = None
228
        self._entries = []
229
230
    def add_object(self, obj, ie):
231
        if obj.type_name == "commit":
232
            self._commit = obj
233
            assert ie is None
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
234
            type_data = (self.revid, self._commit.tree)
0.200.853 by Jelmer Vernooij
Fix lookup of commits in tdb.
235
            self.cache.idmap._by_revid[self.revid] = obj.id
0.200.847 by Jelmer Vernooij
Add BzrGitCache object.
236
        elif obj.type_name in ("blob", "tree"):
0.252.23 by Jelmer Vernooij
More work on roundtripping support.
237
            if ie is not None:
238
                if obj.type_name == "blob":
239
                    revision = ie.revision
240
                else:
241
                    revision = self.revid
242
                type_data = (ie.file_id, revision)
243
                self.cache.idmap._by_fileid.setdefault(type_data[1], {})[type_data[0]] =\
244
                    obj.id
0.200.847 by Jelmer Vernooij
Add BzrGitCache object.
245
        else:
246
            raise AssertionError
0.200.850 by Jelmer Vernooij
Fix tests.
247
        self.cache.idmap._by_sha[obj.id] = (obj.type_name, type_data)
0.200.847 by Jelmer Vernooij
Add BzrGitCache object.
248
249
    def finish(self):
250
        if self._commit is None:
251
            raise AssertionError("No commit object added")
252
        return self._commit
253
254
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
255
class DictGitShaMap(GitShaMap):
256
257
    def __init__(self):
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
258
        self._by_sha = {}
259
        self._by_fileid = {}
0.200.853 by Jelmer Vernooij
Fix lookup of commits in tdb.
260
        self._by_revid = {}
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
261
0.200.841 by Jelmer Vernooij
Eliminate InventorySHAMap.
262
    def lookup_blob_id(self, fileid, revision):
263
        return self._by_fileid[revision][fileid]
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
264
265
    def lookup_git_sha(self, sha):
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
266
        return self._by_sha[sha]
0.230.2 by Jelmer Vernooij
Fix versionedfiles.
267
0.200.841 by Jelmer Vernooij
Eliminate InventorySHAMap.
268
    def lookup_tree_id(self, fileid, revision):
0.200.860 by Jelmer Vernooij
Fix bugs in two lookup_tree_id implementations and add a test for it.
269
        return self._by_fileid[revision][fileid]
0.200.841 by Jelmer Vernooij
Eliminate InventorySHAMap.
270
0.200.853 by Jelmer Vernooij
Fix lookup of commits in tdb.
271
    def lookup_commit(self, revid):
272
        return self._by_revid[revid]
273
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
274
    def revids(self):
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
275
        for key, (type, type_data) in self._by_sha.iteritems():
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
276
            if type == "commit":
0.200.262 by Jelmer Vernooij
Add tests for GitShaMap.
277
                yield type_data[0]
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
278
0.200.422 by Jelmer Vernooij
'bzr git-object' without arguments now prints the available git objects.
279
    def sha1s(self):
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
280
        return self._by_sha.iterkeys()
0.200.422 by Jelmer Vernooij
'bzr git-object' without arguments now prints the available git objects.
281
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
282
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
283
class SqliteCacheUpdater(CacheUpdater):
284
285
    def __init__(self, cache, rev):
286
        self.cache = cache
0.200.850 by Jelmer Vernooij
Fix tests.
287
        self.db = self.cache.idmap.db
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
288
        self.revid = rev.revision_id
289
        self._commit = None
290
        self._trees = []
291
        self._blobs = []
292
293
    def add_object(self, obj, ie):
294
        if obj.type_name == "commit":
295
            self._commit = obj
296
            assert ie is None
297
        elif obj.type_name == "tree":
0.252.23 by Jelmer Vernooij
More work on roundtripping support.
298
            if ie is not None:
299
                self._trees.append((obj.id, ie.file_id, self.revid))
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
300
        elif obj.type_name == "blob":
0.252.23 by Jelmer Vernooij
More work on roundtripping support.
301
            if ie is not None:
302
                self._blobs.append((obj.id, ie.file_id, ie.revision))
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
303
        else:
304
            raise AssertionError
305
306
    def finish(self):
307
        if self._commit is None:
308
            raise AssertionError("No commit object added")
0.200.850 by Jelmer Vernooij
Fix tests.
309
        self.db.executemany(
310
            "replace into trees (sha1, fileid, revid) values (?, ?, ?)",
311
            self._trees)
312
        self.db.executemany(
313
            "replace into blobs (sha1, fileid, revid) values (?, ?, ?)",
314
            self._blobs)
315
        self.db.execute(
316
            "replace into commits (sha1, revid, tree_sha) values (?, ?, ?)",
317
            (self._commit.id, self.revid, self._commit.tree))
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
318
        return self._commit
319
320
321
SqliteBzrGitCache = lambda p: BzrGitCache(SqliteGitShaMap(p), None, SqliteCacheUpdater)
322
323
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
324
class SqliteGitCacheFormat(BzrGitCacheFormat):
325
326
    def get_format_string(self):
327
        return 'bzr-git sha map version 1 using sqlite\n'
328
329
    def open(self, transport):
330
        try:
331
            basepath = transport.local_abspath(".")
332
        except bzrlib.errors.NotLocalUrl:
333
            basepath = get_cache_dir()
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
334
        return SqliteBzrGitCache(os.path.join(basepath, "idmap.db"))
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
335
336
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
337
class SqliteGitShaMap(GitShaMap):
0.200.226 by Jelmer Vernooij
Merge thin-pack work.
338
0.200.365 by Jelmer Vernooij
Share sha map cache connections inside threads.
339
    def __init__(self, path=None):
340
        self.path = path
341
        if path is None:
0.200.262 by Jelmer Vernooij
Add tests for GitShaMap.
342
            self.db = sqlite3.connect(":memory:")
343
        else:
0.200.365 by Jelmer Vernooij
Share sha map cache connections inside threads.
344
            if not mapdbs().has_key(path):
345
                mapdbs()[path] = sqlite3.connect(path)
0.200.675 by Jelmer Vernooij
Fix formatting.
346
            self.db = mapdbs()[path]
0.200.688 by Jelmer Vernooij
Use str text factory rather than encoding/decoding each time.
347
        self.db.text_factory = str
0.200.230 by Jelmer Vernooij
Implement sha cache.
348
        self.db.executescript("""
0.200.691 by Jelmer Vernooij
Add extra constraints in sqlite tables.
349
        create table if not exists commits(
350
            sha1 text not null check(length(sha1) == 40),
351
            revid text not null,
352
            tree_sha text not null check(length(tree_sha) == 40)
353
        );
0.200.230 by Jelmer Vernooij
Implement sha cache.
354
        create index if not exists commit_sha1 on commits(sha1);
0.200.284 by Jelmer Vernooij
Add extra indexes.
355
        create unique index if not exists commit_revid on commits(revid);
0.200.691 by Jelmer Vernooij
Add extra constraints in sqlite tables.
356
        create table if not exists blobs(
357
            sha1 text not null check(length(sha1) == 40),
358
            fileid text not null,
359
            revid text not null
360
        );
0.200.230 by Jelmer Vernooij
Implement sha cache.
361
        create index if not exists blobs_sha1 on blobs(sha1);
0.200.284 by Jelmer Vernooij
Add extra indexes.
362
        create unique index if not exists blobs_fileid_revid on blobs(fileid, revid);
0.200.691 by Jelmer Vernooij
Add extra constraints in sqlite tables.
363
        create table if not exists trees(
0.200.750 by Jelmer Vernooij
Remove unused tree code, add mechanism for migrating between sha maps.
364
            sha1 text unique not null check(length(sha1) == 40),
0.200.691 by Jelmer Vernooij
Add extra constraints in sqlite tables.
365
            fileid text not null,
366
            revid text not null
367
        );
0.200.750 by Jelmer Vernooij
Remove unused tree code, add mechanism for migrating between sha maps.
368
        create unique index if not exists trees_sha1 on trees(sha1);
0.200.343 by Jelmer Vernooij
Use file ids consistently in map.
369
        create unique index if not exists trees_fileid_revid on trees(fileid, revid);
0.200.230 by Jelmer Vernooij
Implement sha cache.
370
""")
0.200.226 by Jelmer Vernooij
Merge thin-pack work.
371
0.200.750 by Jelmer Vernooij
Remove unused tree code, add mechanism for migrating between sha maps.
372
    def __repr__(self):
373
        return "%s(%r)" % (self.__class__.__name__, self.path)
374
    
0.200.487 by Jelmer Vernooij
Prevent deep recursion if the shamap is out of date.
375
    def lookup_commit(self, revid):
0.200.364 by Jelmer Vernooij
Reimplement dpush, but more efficient and only writing a single pack file rather than one per revision.
376
        row = self.db.execute("select sha1 from commits where revid = ?", (revid,)).fetchone()
377
        if row is not None:
0.200.688 by Jelmer Vernooij
Use str text factory rather than encoding/decoding each time.
378
            return row[0]
0.200.364 by Jelmer Vernooij
Reimplement dpush, but more efficient and only writing a single pack file rather than one per revision.
379
        raise KeyError
0.200.231 by Jelmer Vernooij
Partially fix pull.
380
0.200.687 by Jelmer Vernooij
Use start_write_group() / commit_write_group() mechanism when creating git SHA maps.
381
    def commit_write_group(self):
0.200.232 by Jelmer Vernooij
Fix pull from remote branches.
382
        self.db.commit()
383
0.200.841 by Jelmer Vernooij
Eliminate InventorySHAMap.
384
    def lookup_blob_id(self, fileid, revision):
385
        row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revision)).fetchone()
386
        if row is not None:
387
            return row[0]
388
        raise KeyError(fileid)
389
390
    def lookup_tree_id(self, fileid, revision):
0.200.860 by Jelmer Vernooij
Fix bugs in two lookup_tree_id implementations and add a test for it.
391
        row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid, revision)).fetchone()
0.200.841 by Jelmer Vernooij
Eliminate InventorySHAMap.
392
        if row is not None:
393
            return row[0]
394
        raise KeyError(fileid)
0.230.2 by Jelmer Vernooij
Fix versionedfiles.
395
0.200.226 by Jelmer Vernooij
Merge thin-pack work.
396
    def lookup_git_sha(self, sha):
397
        """Lookup a Git sha in the database.
398
399
        :param sha: Git object sha
400
        :return: (type, type_data) with type_data:
401
            revision: revid, tree sha
402
        """
0.200.230 by Jelmer Vernooij
Implement sha cache.
403
        row = self.db.execute("select revid, tree_sha from commits where sha1 = ?", (sha,)).fetchone()
404
        if row is not None:
0.200.845 by Jelmer Vernooij
Couple of minor fixes.
405
            return ("commit", row)
0.200.230 by Jelmer Vernooij
Implement sha cache.
406
        row = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,)).fetchone()
407
        if row is not None:
0.200.845 by Jelmer Vernooij
Couple of minor fixes.
408
            return ("blob", row)
0.200.343 by Jelmer Vernooij
Use file ids consistently in map.
409
        row = self.db.execute("select fileid, revid from trees where sha1 = ?", (sha,)).fetchone()
0.200.230 by Jelmer Vernooij
Implement sha cache.
410
        if row is not None:
0.200.845 by Jelmer Vernooij
Couple of minor fixes.
411
            return ("tree", row)
0.200.230 by Jelmer Vernooij
Implement sha cache.
412
        raise KeyError(sha)
413
414
    def revids(self):
0.200.260 by Jelmer Vernooij
Add DictGitShaMap, useful for testing.
415
        """List the revision ids known."""
0.248.7 by Jelmer Vernooij
Avoid fetching all sha1s at once.
416
        return (row for (row,) in self.db.execute("select revid from commits"))
0.200.422 by Jelmer Vernooij
'bzr git-object' without arguments now prints the available git objects.
417
418
    def sha1s(self):
419
        """List the SHA1s."""
420
        for table in ("blobs", "commits", "trees"):
0.200.845 by Jelmer Vernooij
Couple of minor fixes.
421
            for (sha,) in self.db.execute("select sha1 from %s" % table):
422
                yield sha
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
423
424
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
425
class TdbCacheUpdater(CacheUpdater):
426
427
    def __init__(self, cache, rev):
428
        self.cache = cache
429
        self.db = cache.idmap.db
430
        self.revid = rev.revision_id
431
        self.parent_revids = rev.parent_ids
432
        self._commit = None
433
        self._entries = []
434
435
    def add_object(self, obj, ie):
436
        sha = obj.sha().digest()
437
        if obj.type_name == "commit":
0.200.853 by Jelmer Vernooij
Fix lookup of commits in tdb.
438
            self.db["commit\0" + self.revid] = "\0".join((sha, obj.tree))
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
439
            type_data = (self.revid, obj.tree)
440
            self._commit = obj
441
            assert ie is None
442
        elif obj.type_name == "blob":
0.252.23 by Jelmer Vernooij
More work on roundtripping support.
443
            if ie is None:
444
                return
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
445
            self.db["\0".join(("blob", ie.file_id, ie.revision))] = sha
446
            type_data = (ie.file_id, ie.revision)
447
        elif obj.type_name == "tree":
0.252.23 by Jelmer Vernooij
More work on roundtripping support.
448
            if ie is None:
449
                return
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
450
            type_data = (ie.file_id, self.revid)
451
        else:
452
            raise AssertionError
0.200.853 by Jelmer Vernooij
Fix lookup of commits in tdb.
453
        self.db["git\0" + sha] = "\0".join((obj.type_name, ) + type_data)
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
454
455
    def finish(self):
456
        if self._commit is None:
457
            raise AssertionError("No commit object added")
458
        return self._commit
459
460
461
TdbBzrGitCache = lambda p: BzrGitCache(TdbGitShaMap(p), None, TdbCacheUpdater)
0.200.479 by Jelmer Vernooij
Version tdb sha map.
462
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
463
class TdbGitCacheFormat(BzrGitCacheFormat):
464
465
    def get_format_string(self):
466
        return 'bzr-git sha map version 3 using tdb\n'
467
468
    def open(self, transport):
469
        try:
470
            basepath = transport.local_abspath(".")
471
        except bzrlib.errors.NotLocalUrl:
472
            basepath = get_cache_dir()
473
        try:
0.200.850 by Jelmer Vernooij
Fix tests.
474
            return TdbBzrGitCache(os.path.join(basepath, "idmap.tdb"))
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
475
        except ImportError:
476
            raise ImportError(
477
                "Unable to open existing bzr-git cache because 'tdb' is not "
478
                "installed.")
479
480
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
481
class TdbGitShaMap(GitShaMap):
482
    """SHA Map that uses a TDB database.
483
484
    Entries:
485
0.200.476 by Jelmer Vernooij
Fix Tdb backend, use tdb if possible by default.
486
    "git <sha1>" -> "<type> <type-data1> <type-data2>"
487
    "commit revid" -> "<sha1> <tree-id>"
0.200.477 by Jelmer Vernooij
More tests for sha maps, fix cache misses in tdb.
488
    "tree fileid revid" -> "<sha1>"
489
    "blob fileid revid" -> "<sha1>"
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
490
    """
491
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
492
    TDB_MAP_VERSION = 3
493
    TDB_HASH_SIZE = 50000
494
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
495
    def __init__(self, path=None):
496
        import tdb
497
        self.path = path
498
        if path is None:
499
            self.db = {}
500
        else:
501
            if not mapdbs().has_key(path):
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
502
                mapdbs()[path] = tdb.Tdb(path, self.TDB_HASH_SIZE, tdb.DEFAULT,
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
503
                                          os.O_RDWR|os.O_CREAT)
0.200.676 by Jelmer Vernooij
Avoid iterating over all keys in the tdb database.
504
            self.db = mapdbs()[path]
505
        try:
0.200.751 by Jelmer Vernooij
Unrelated small fixes - import, avoid storing tree info (no longer used).
506
            if int(self.db["version"]) not in (2, 3):
0.235.1 by Jelmer Vernooij
Store sha map more efficiently.
507
                trace.warning("SHA Map is incompatible (%s -> %d), rebuilding database.",
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
508
                              self.db["version"], self.TDB_MAP_VERSION)
0.235.1 by Jelmer Vernooij
Store sha map more efficiently.
509
                self.db.clear()
0.200.676 by Jelmer Vernooij
Avoid iterating over all keys in the tdb database.
510
        except KeyError:
0.200.751 by Jelmer Vernooij
Unrelated small fixes - import, avoid storing tree info (no longer used).
511
            pass
0.200.849 by Jelmer Vernooij
Allow cache backends to decide when to add entries rather than adding once per commit.
512
        self.db["version"] = str(self.TDB_MAP_VERSION)
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
513
0.200.809 by Jelmer Vernooij
Use tdb transactions for write groups.
514
    def start_write_group(self):
515
        """Start writing changes."""
0.200.778 by Jelmer Vernooij
Use transactions in tdb.
516
        self.db.transaction_start()
0.200.809 by Jelmer Vernooij
Use tdb transactions for write groups.
517
518
    def commit_write_group(self):
519
        """Commit any pending changes."""
520
        self.db.transaction_commit()
521
522
    def abort_write_group(self):
523
        """Abort any pending changes."""
524
        self.db.transaction_cancel()
0.200.778 by Jelmer Vernooij
Use transactions in tdb.
525
0.200.750 by Jelmer Vernooij
Remove unused tree code, add mechanism for migrating between sha maps.
526
    def __repr__(self):
527
        return "%s(%r)" % (self.__class__.__name__, self.path)
528
0.200.487 by Jelmer Vernooij
Prevent deep recursion if the shamap is out of date.
529
    def lookup_commit(self, revid):
0.235.1 by Jelmer Vernooij
Store sha map more efficiently.
530
        return sha_to_hex(self.db["commit\0" + revid][:20])
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
531
0.200.841 by Jelmer Vernooij
Eliminate InventorySHAMap.
532
    def lookup_blob_id(self, fileid, revision):
533
        return sha_to_hex(self.db["\0".join(("blob", fileid, revision))])
0.200.753 by Jelmer Vernooij
Move lookup_tree/lookup_blob to a separate object.
534
                
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
535
    def lookup_git_sha(self, sha):
536
        """Lookup a Git sha in the database.
537
538
        :param sha: Git object sha
539
        :return: (type, type_data) with type_data:
540
            revision: revid, tree sha
541
        """
0.200.564 by Jelmer Vernooij
Accept 'binary' shas.
542
        if len(sha) == 40:
543
            sha = hex_to_sha(sha)
544
        data = self.db["git\0" + sha].split("\0")
0.200.476 by Jelmer Vernooij
Fix Tdb backend, use tdb if possible by default.
545
        return (data[0], (data[1], data[2]))
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
546
0.200.677 by Jelmer Vernooij
Implement TdbCache.missing_revisions().
547
    def missing_revisions(self, revids):
548
        ret = set()
549
        for revid in revids:
550
            if self.db.get("commit\0" + revid) is None:
551
                ret.add(revid)
552
        return ret
553
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
554
    def revids(self):
555
        """List the revision ids known."""
556
        for key in self.db.iterkeys():
0.235.1 by Jelmer Vernooij
Store sha map more efficiently.
557
            if key.startswith("commit\0"):
558
                yield key[7:]
0.200.475 by Jelmer Vernooij
Add Tdb database backend.
559
560
    def sha1s(self):
561
        """List the SHA1s."""
562
        for key in self.db.iterkeys():
0.235.1 by Jelmer Vernooij
Store sha map more efficiently.
563
            if key.startswith("git\0"):
564
                yield sha_to_hex(key[4:])
0.200.750 by Jelmer Vernooij
Remove unused tree code, add mechanism for migrating between sha maps.
565
566
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
567
formats = registry.Registry()
568
formats.register(TdbGitCacheFormat().get_format_string(),
569
    TdbGitCacheFormat())
570
formats.register(SqliteGitCacheFormat().get_format_string(),
571
    SqliteGitCacheFormat())
572
try:
573
    import tdb
574
except ImportError:
575
    formats.register('default', SqliteGitCacheFormat())
576
else:
577
    formats.register('default', TdbGitCacheFormat())
578
579
580
def migrate_ancient_formats(repo_transport):
0.200.866 by Jelmer Vernooij
More docstrings, prefer migrating git.db to migrating git.tdb.
581
    # Prefer migrating git.db over git.tdb, since the latter may not 
582
    # be openable on some platforms.
583
    if repo_transport.has("git.db"):
584
        SqliteGitCacheFormat().initialize(repo_transport.clone("git"))
585
        repo_transport.rename("git.db", "git/idmap.db")
586
    elif repo_transport.has("git.tdb"):
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
587
        TdbGitCacheFormat().initialize(repo_transport.clone("git"))
588
        repo_transport.rename("git.tdb", "git/idmap.tdb")
589
590
0.200.865 by Jelmer Vernooij
Support serving without --allow-writes.
591
def remove_readonly_transport_decorator(transport):
592
    if transport.is_readonly():
593
        return transport._decorated
594
    return transport
595
596
0.200.750 by Jelmer Vernooij
Remove unused tree code, add mechanism for migrating between sha maps.
597
def from_repository(repository):
0.200.866 by Jelmer Vernooij
More docstrings, prefer migrating git.db to migrating git.tdb.
598
    """Open a cache file for a repository.
599
600
    If the repository is remote and there is no transport available from it
601
    this will use a local file in the users cache directory
602
    (typically ~/.cache/bazaar/git/)
603
604
    :param repository: A repository object
605
    """
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
606
    repo_transport = getattr(repository, "_transport", None)
607
    if repo_transport is not None:
608
        # Migrate older cache formats
0.200.865 by Jelmer Vernooij
Support serving without --allow-writes.
609
        repo_transport = remove_readonly_transport_decorator(repo_transport)
0.200.844 by Jelmer Vernooij
Add infrastructure for multiple cache formats.
610
        try:
611
            repo_transport.mkdir("git")
612
        except bzrlib.errors.FileExists:
613
            pass
614
        else:
615
            migrate_ancient_formats(repo_transport)
616
    return BzrGitCacheFormat.from_repository(repository)