/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

Cope with disappeared revisions.

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