/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to shamap.py

Fix Tdb backend, use tdb if possible by default.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009 Canonical Ltd
 
1
# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
"""Map from Git sha's to Bazaar objects."""
18
18
 
 
19
import os
 
20
import threading
 
21
 
19
22
import bzrlib
20
 
 
21
 
from bzrlib.errors import NoSuchRevision
22
 
 
23
 
import os
 
23
from bzrlib.errors import (
 
24
    NoSuchRevision,
 
25
    )
24
26
 
25
27
 
26
28
def check_pysqlite_version(sqlite3):
46
48
    raise bzrlib.errors.BzrError("missing sqlite library")
47
49
 
48
50
 
 
51
_mapdbs = threading.local()
 
52
def mapdbs():
 
53
    """Get a cache for this thread's db connections."""
 
54
    try:
 
55
        return _mapdbs.cache
 
56
    except AttributeError:
 
57
        _mapdbs.cache = {}
 
58
        return _mapdbs.cache
 
59
 
 
60
 
49
61
class GitShaMap(object):
50
 
 
51
 
    def __init__(self, transport):
52
 
        self.transport = transport
53
 
        self.db = sqlite3.connect(
54
 
            os.path.join(self.transport.local_abspath("."), "git.db"))
 
62
    """Git<->Bzr revision id mapping database."""
 
63
 
 
64
    def add_entry(self, sha, type, type_data):
 
65
        """Add a new entry to the database.
 
66
        """
 
67
        raise NotImplementedError(self.add_entry)
 
68
 
 
69
    def add_entries(self, entries):
 
70
        """Add multiple new entries to the database.
 
71
        """
 
72
        for e in entries:
 
73
            self.add_entry(*e)
 
74
 
 
75
    def lookup_tree(self, fileid, revid):
 
76
        """Lookup the SHA of a git tree."""
 
77
        raise NotImplementedError(self.lookup_tree)
 
78
 
 
79
    def lookup_blob(self, fileid, revid):
 
80
        raise NotImplementedError(self.lookup_blob)
 
81
 
 
82
    def lookup_git_sha(self, sha):
 
83
        """Lookup a Git sha in the database.
 
84
 
 
85
        :param sha: Git object sha
 
86
        :return: (type, type_data) with type_data:
 
87
            revision: revid, tree sha
 
88
        """
 
89
        raise NotImplementedError(self.lookup_git_sha)
 
90
 
 
91
    def revids(self):
 
92
        """List the revision ids known."""
 
93
        raise NotImplementedError(self.revids)
 
94
 
 
95
    def sha1s(Self):
 
96
        """List the SHA1s."""
 
97
        raise NotImplementedError(self.sha1s)
 
98
 
 
99
    def commit(self):
 
100
        """Commit any pending changes."""
 
101
 
 
102
 
 
103
class DictGitShaMap(GitShaMap):
 
104
 
 
105
    def __init__(self):
 
106
        self.dict = {}
 
107
 
 
108
    def add_entry(self, sha, type, type_data):
 
109
        self.dict[sha] = (type, type_data)
 
110
 
 
111
    def lookup_git_sha(self, sha):
 
112
        return self.dict[sha]
 
113
 
 
114
    def lookup_tree(self, fileid, revid):
 
115
        for k, v in self.dict.iteritems():
 
116
            if v == ("tree", (fileid, revid)):
 
117
                return k
 
118
        raise KeyError((fileid, revid))
 
119
 
 
120
    def lookup_blob(self, fileid, revid):
 
121
        for k, v in self.dict.iteritems():
 
122
            if v == ("blob", (fileid, revid)):
 
123
                return k
 
124
        raise KeyError((fileid, revid))
 
125
 
 
126
    def revids(self):
 
127
        for key, (type, type_data) in self.dict.iteritems():
 
128
            if type == "commit":
 
129
                yield type_data[0]
 
130
 
 
131
    def sha1s(self):
 
132
        return self.dict.iterkeys()
 
133
 
 
134
 
 
135
class SqliteGitShaMap(GitShaMap):
 
136
 
 
137
    def __init__(self, path=None):
 
138
        self.path = path
 
139
        if path is None:
 
140
            self.db = sqlite3.connect(":memory:")
 
141
        else:
 
142
            if not mapdbs().has_key(path):
 
143
                mapdbs()[path] = sqlite3.connect(path)
 
144
            self.db = mapdbs()[path]    
55
145
        self.db.executescript("""
56
146
        create table if not exists commits(sha1 text, revid text, tree_sha text);
57
147
        create index if not exists commit_sha1 on commits(sha1);
 
148
        create unique index if not exists commit_revid on commits(revid);
58
149
        create table if not exists blobs(sha1 text, fileid text, revid text);
59
150
        create index if not exists blobs_sha1 on blobs(sha1);
 
151
        create unique index if not exists blobs_fileid_revid on blobs(fileid, revid);
60
152
        create table if not exists trees(sha1 text, fileid text, revid text);
61
153
        create index if not exists trees_sha1 on trees(sha1);
 
154
        create unique index if not exists trees_fileid_revid on trees(fileid, revid);
62
155
""")
63
156
 
 
157
    @classmethod
 
158
    def from_repository(cls, repository):
 
159
        return cls(os.path.join(repository._transport.local_abspath("."), "git.db"))
 
160
 
64
161
    def _parent_lookup(self, revid):
65
 
        return self.db.execute("select sha1 from commits where revid = ?", (revid,)).fetchone()[0].encode("utf-8")
 
162
        row = self.db.execute("select sha1 from commits where revid = ?", (revid,)).fetchone()
 
163
        if row is not None:
 
164
            return row[0].encode("utf-8")
 
165
        raise KeyError
 
166
 
 
167
    def commit(self):
 
168
        self.db.commit()
 
169
 
 
170
    def add_entries(self, entries):
 
171
        trees = []
 
172
        blobs = []
 
173
        for sha, type, type_data in entries:
 
174
            assert isinstance(type_data[0], str)
 
175
            assert isinstance(type_data[1], str)
 
176
            entry = (sha.decode("utf-8"), type_data[0].decode("utf-8"), 
 
177
                     type_data[1].decode("utf-8"))
 
178
            if type == "tree":
 
179
                trees.append(entry)
 
180
            elif type == "blob":
 
181
                blobs.append(entry)
 
182
            else:
 
183
                raise AssertionError
 
184
        if trees:
 
185
            self.db.executemany("replace into trees (sha1, fileid, revid) values (?, ?, ?)", trees)
 
186
        if blobs:
 
187
            self.db.executemany("replace into blobs (sha1, fileid, revid) values (?, ?, ?)", blobs)
 
188
 
66
189
 
67
190
    def add_entry(self, sha, type, type_data):
68
191
        """Add a new entry to the database.
71
194
        assert isinstance(sha, str), "type was %r" % sha
72
195
        if type == "commit":
73
196
            self.db.execute("replace into commits (sha1, revid, tree_sha) values (?, ?, ?)", (sha, type_data[0], type_data[1]))
74
 
        elif type == "blob":
75
 
            self.db.execute("replace into blobs (sha1, fileid, revid) values (?, ?, ?)", (sha, type_data[0], type_data[1]))
76
 
        elif type == "tree":
77
 
            self.db.execute("replace into trees (sha1, fileid, revid) values (?, ?, ?)", (sha, type_data[0], type_data[1]))
 
197
        elif type in ("blob", "tree"):
 
198
            self.db.execute("replace into %ss (sha1, fileid, revid) values (?, ?, ?)" % type, (sha, type_data[0], type_data[1]))
78
199
        else:
79
200
            raise AssertionError("Unknown type %s" % type)
80
201
 
 
202
    def lookup_tree(self, fileid, revid):
 
203
        row = self.db.execute("select sha1 from trees where fileid = ? and revid = ?", (fileid,revid)).fetchone()
 
204
        if row is None:
 
205
            raise KeyError((fileid, revid))
 
206
        return row[0].encode("utf-8")
 
207
 
 
208
    def lookup_blob(self, fileid, revid):
 
209
        row = self.db.execute("select sha1 from blobs where fileid = ? and revid = ?", (fileid, revid)).fetchone()
 
210
        if row is None:
 
211
            raise KeyError((fileid, revid))
 
212
        return row[0].encode("utf-8")
 
213
 
81
214
    def lookup_git_sha(self, sha):
82
215
        """Lookup a Git sha in the database.
83
216
 
85
218
        :return: (type, type_data) with type_data:
86
219
            revision: revid, tree sha
87
220
        """
 
221
        def format(type, row):
 
222
            return (type, (row[0].encode("utf-8"), row[1].encode("utf-8")))
88
223
        row = self.db.execute("select revid, tree_sha from commits where sha1 = ?", (sha,)).fetchone()
89
224
        if row is not None:
90
 
            return ("commit", row)
 
225
            return format("commit", row)
91
226
        row = self.db.execute("select fileid, revid from blobs where sha1 = ?", (sha,)).fetchone()
92
227
        if row is not None:
93
 
            return ("blob", row)
 
228
            return format("blob", row)
94
229
        row = self.db.execute("select fileid, revid from trees where sha1 = ?", (sha,)).fetchone()
95
230
        if row is not None:
96
 
            return ("tree", row)
 
231
            return format("tree", row)
97
232
        raise KeyError(sha)
98
233
 
99
234
    def revids(self):
 
235
        """List the revision ids known."""
100
236
        for row in self.db.execute("select revid from commits").fetchall():
101
 
            yield row[0]
 
237
            yield row[0].encode("utf-8")
 
238
 
 
239
    def sha1s(self):
 
240
        """List the SHA1s."""
 
241
        for table in ("blobs", "commits", "trees"):
 
242
            for row in self.db.execute("select sha1 from %s" % table).fetchall():
 
243
                yield row[0].encode("utf-8")
 
244
 
 
245
 
 
246
class TdbGitShaMap(GitShaMap):
 
247
    """SHA Map that uses a TDB database.
 
248
 
 
249
    Entries:
 
250
 
 
251
    "git <sha1>" -> "<type> <type-data1> <type-data2>"
 
252
    "commit revid" -> "<sha1> <tree-id>"
 
253
    "tree revid fileid" -> "<sha1>"
 
254
    "blob revid fileid" -> "<sha1>"
 
255
    """
 
256
 
 
257
    def __init__(self, path=None):
 
258
        import tdb
 
259
        self.path = path
 
260
        if path is None:
 
261
            self.db = {}
 
262
        else:
 
263
            if not mapdbs().has_key(path):
 
264
                mapdbs()[path] = tdb.open(path, 0, tdb.DEFAULT, 
 
265
                                          os.O_RDWR|os.O_CREAT)
 
266
            self.db = mapdbs()[path]    
 
267
 
 
268
    @classmethod
 
269
    def from_repository(cls, repository):
 
270
        return cls(os.path.join(repository._transport.local_abspath("."), "git.tdb"))
 
271
 
 
272
    def _parent_lookup(self, revid):
 
273
        return self.db["commit %s" % revid].split(" ")[0]
 
274
 
 
275
    def commit(self):
 
276
        pass
 
277
 
 
278
    def add_entries(self, entries):
 
279
        """Add multiple new entries to the database.
 
280
        """
 
281
        self.db.transaction_start()
 
282
        try:
 
283
            for e in entries:
 
284
                self.add_entry(*e)
 
285
        except:
 
286
            self.db.transaction_cancel()
 
287
            raise
 
288
        self.db.transaction_commit()
 
289
 
 
290
    def add_entry(self, sha, type, type_data):
 
291
        """Add a new entry to the database.
 
292
        """
 
293
        self.db["git %s" % sha] = "%s %s %s" % (type, type_data[0], type_data[1])
 
294
        if type == "commit":
 
295
            self.db["commit %s" % type_data[0]] = "%s %s" % (sha, type_data[1])
 
296
        else:
 
297
            self.db["%s %s %s" % (type, type_data[0], type_data[1])] = sha
 
298
 
 
299
    def lookup_tree(self, fileid, revid):
 
300
        return self.db["tree %s %s" % (revid, fileid)]
 
301
 
 
302
    def lookup_blob(self, fileid, revid):
 
303
        return self.db["blob %s %s" % (revid, fileid)]
 
304
 
 
305
    def lookup_git_sha(self, sha):
 
306
        """Lookup a Git sha in the database.
 
307
 
 
308
        :param sha: Git object sha
 
309
        :return: (type, type_data) with type_data:
 
310
            revision: revid, tree sha
 
311
        """
 
312
        data = self.db["git %s" % sha].split(" ")
 
313
        return (data[0], (data[1], data[2]))
 
314
 
 
315
    def revids(self):
 
316
        """List the revision ids known."""
 
317
        for key in self.db.iterkeys():
 
318
            if key.startswith("commit "):
 
319
                yield key.split(" ")[1]
 
320
 
 
321
    def sha1s(self):
 
322
        """List the SHA1s."""
 
323
        for key in self.db.iterkeys():
 
324
            if key.startswith("git "):
 
325
                yield key.split(" ")[1]