/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 object_store.py

Add extra constraints in sqlite tables.

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 bzrlib
20
 
 
21
 
from bzrlib import ui
22
 
 
23
 
from bzrlib.errors import NoSuchRevision
24
 
 
25
 
from bzrlib.plugins.git.mapping import (
26
 
    inventory_to_tree_and_blobs,
27
 
    revision_to_commit,
28
 
    )
29
 
from bzrlib.plugins.git.shamap import GitShaMap
30
 
 
31
19
from dulwich.objects import (
32
20
    Blob,
33
 
    )
34
 
 
35
 
 
36
 
class GitObjectConverter(object):
 
21
    sha_to_hex,
 
22
    )
 
23
from dulwich.object_store import (
 
24
    BaseObjectStore,
 
25
    )
 
26
 
 
27
from bzrlib import (
 
28
    debug,
 
29
    errors,
 
30
    trace,
 
31
    ui,
 
32
    )
 
33
from bzrlib.revision import (
 
34
    NULL_REVISION,
 
35
    )
 
36
 
 
37
from bzrlib.plugins.git.mapping import (
 
38
    default_mapping,
 
39
    directory_to_tree,
 
40
    extract_unusual_modes,
 
41
    mapping_registry,
 
42
    )
 
43
from bzrlib.plugins.git.shamap import (
 
44
    SqliteGitShaMap,
 
45
    TdbGitShaMap,
 
46
    )
 
47
 
 
48
 
 
49
def get_object_store(repo, mapping=None):
 
50
    git = getattr(repo, "_git", None)
 
51
    if git is not None:
 
52
        return git.object_store
 
53
    return BazaarObjectStore(repo, mapping)
 
54
 
 
55
 
 
56
class BazaarObjectStore(BaseObjectStore):
 
57
    """A Git-style object store backed onto a Bazaar repository."""
37
58
 
38
59
    def __init__(self, repository, mapping=None):
39
60
        self.repository = repository
40
61
        if mapping is None:
41
 
            self.mapping = self.repository.get_mapping()
 
62
            self.mapping = default_mapping
42
63
        else:
43
64
            self.mapping = mapping
44
 
        self._idmap = GitShaMap(self.repository._transport)
 
65
        try:
 
66
            self._idmap = TdbGitShaMap.from_repository(repository)
 
67
        except ImportError:
 
68
            self._idmap = SqliteGitShaMap.from_repository(repository)
45
69
 
46
 
    def _update_sha_map(self):
47
 
        all_revids = self.repository.all_revision_ids()
 
70
    def _update_sha_map(self, stop_revision=None):
48
71
        graph = self.repository.get_graph()
49
 
        present_revids = set(self._idmap.revids())
 
72
        if stop_revision is None:
 
73
            heads = graph.heads(self.repository.all_revision_ids())
 
74
        else:
 
75
            heads = set([stop_revision])
 
76
        missing_revids = self._idmap.missing_revisions(heads)
 
77
        while heads:
 
78
            parents = graph.get_parent_map(heads)
 
79
            todo = set()
 
80
            for p in parents.values():
 
81
                todo.update([x for x in p if x not in missing_revids])
 
82
            heads = self._idmap.missing_revisions(todo)
 
83
            missing_revids.update(heads)
50
84
        pb = ui.ui_factory.nested_progress_bar()
51
85
        try:
52
 
            for i, revid in enumerate(graph.iter_topo_order(all_revids)):
53
 
                if revid in present_revids:
54
 
                    continue
55
 
                pb.update("updating git map", i, len(all_revids))
 
86
            for i, revid in enumerate(graph.iter_topo_order(missing_revids)):
 
87
                pb.update("updating git map", i, len(missing_revids))
56
88
                self._update_sha_map_revision(revid)
57
89
        finally:
58
90
            pb.finished()
 
91
        self._idmap.commit_write_group()
 
92
 
 
93
    def __iter__(self):
 
94
        self._update_sha_map()
 
95
        return iter(self._idmap.sha1s())
 
96
 
 
97
    def _revision_to_commit(self, rev, tree_sha):
 
98
        def parent_lookup(revid):
 
99
            try:
 
100
                return self._lookup_revision_sha1(revid)
 
101
            except errors.NoSuchRevision:
 
102
                trace.warning("Ignoring ghost parent %s", revid)
 
103
                return None
 
104
        return self.mapping.export_commit(rev, tree_sha, parent_lookup)
59
105
 
60
106
    def _update_sha_map_revision(self, revid):
61
107
        inv = self.repository.get_inventory(revid)
62
 
        objects = inventory_to_tree_and_blobs(self.repository, self.mapping, revid)
63
 
        for sha, o, path in objects:
64
 
            if path == "":
65
 
                tree_sha = sha
66
 
            ie = inv[inv.path2id(path)]
67
 
            if ie.kind in ("file", "symlink"):
68
 
                self._idmap.add_entry(sha, "blob", (ie.file_id, ie.revision))
69
 
            else:
70
 
                self._idmap.add_entry(sha, "tree", (ie.file_id, ie.revision))
71
108
        rev = self.repository.get_revision(revid)
72
 
        commit_obj = revision_to_commit(rev, tree_sha, self._idmap._parent_lookup)
73
 
        self._idmap.add_entry(commit_obj.sha().hexdigest(), "commit", (revid, tree_sha))
74
 
 
75
 
    def _get_blob(self, fileid, revision):
76
 
        text = self.repository.texts.get_record_stream([(fileid, revision)], "unordered", True).next().get_bytes_as("fulltext")
 
109
        unusual_modes = extract_unusual_modes(rev)
 
110
        tree_sha = self._get_ie_sha1(inv.root, inv, unusual_modes)
 
111
        commit_obj = self._revision_to_commit(rev, tree_sha)
 
112
        try:
 
113
            foreign_revid, mapping = mapping_registry.parse_revision_id(revid)
 
114
        except errors.InvalidRevisionId:
 
115
            pass
 
116
        else:
 
117
            if foreign_revid != commit_obj.id:
 
118
                if not "fix-shamap" in debug.debug_flags:
 
119
                    raise AssertionError("recreated git commit had different sha1: expected %s, got %s" % (foreign_revid, commit_obj.id))
 
120
        self._idmap.add_entry(commit_obj.id, "commit", (revid, tree_sha))
 
121
 
 
122
    def _check_expected_sha(self, expected_sha, object):
 
123
        if expected_sha is None:
 
124
            return
 
125
        if len(expected_sha) == 40:
 
126
            if expected_sha != object.sha().hexdigest():
 
127
                raise AssertionError("Invalid sha for %r: %s" % (object, expected_sha))
 
128
        elif len(expected_sha) == 20:
 
129
            if expected_sha != object.sha().digest():
 
130
                raise AssertionError("Invalid sha for %r: %s" % (object, sha_to_hex(expected_sha)))
 
131
        else:
 
132
            raise AssertionError("Unknown length %d for %r" % (len(expected_sha), expected_sha))
 
133
 
 
134
    def _get_ie_object(self, entry, inv, unusual_modes):
 
135
        if entry.kind == "directory":
 
136
            return self._get_tree(entry.file_id, inv.revision_id, inv, unusual_modes)
 
137
        elif entry.kind in ("file", "symlink"):
 
138
            return self._get_blob(entry.file_id, entry.revision)
 
139
        else:
 
140
            raise AssertionError("unknown entry kind '%s'" % entry.kind)
 
141
 
 
142
    def _get_ie_object_or_sha1(self, entry, inv, unusual_modes):
 
143
        if entry.kind == "directory":
 
144
            try:
 
145
                return self._idmap.lookup_tree(entry.file_id, inv.revision_id), None
 
146
            except KeyError:
 
147
                ret = self._get_ie_object(entry, inv, unusual_modes)
 
148
                if ret is None:
 
149
                    hexsha = None
 
150
                else:
 
151
                    hexsha = ret.id
 
152
                self._idmap.add_entry(hexsha, "tree", (entry.file_id, inv.revision_id))
 
153
                return hexsha, ret
 
154
        elif entry.kind in ("file", "symlink"):
 
155
            try:
 
156
                return self._idmap.lookup_blob(entry.file_id, entry.revision), None
 
157
            except KeyError:
 
158
                ret = self._get_ie_object(entry, inv, unusual_modes)
 
159
                self._idmap.add_entry(ret.id, "blob", (entry.file_id, entry.revision))
 
160
                return ret.id, ret
 
161
        else:
 
162
            raise AssertionError("unknown entry kind '%s'" % entry.kind)
 
163
 
 
164
    def _get_ie_sha1(self, entry, inv, unusual_modes):
 
165
        return self._get_ie_object_or_sha1(entry, inv, unusual_modes)[0]
 
166
 
 
167
    def _get_blob(self, fileid, revision, expected_sha=None):
 
168
        """Return a Git Blob object from a fileid and revision stored in bzr.
 
169
 
 
170
        :param fileid: File id of the text
 
171
        :param revision: Revision of the text
 
172
        """
 
173
        chunks = self.repository.iter_files_bytes([(fileid, revision, None)]).next()[1]
77
174
        blob = Blob()
78
 
        blob._text = text
 
175
        blob._text = "".join(chunks)
 
176
        self._check_expected_sha(expected_sha, blob)
79
177
        return blob
80
178
 
81
 
    def _get_tree(self, fileid, revid):
82
 
        raise NotImplementedError(self._get_tree)
83
 
 
84
 
    def _get_commit(self, revid, tree_sha):
85
 
        rev = self.repository.get_revision(revid)
86
 
        return revision_to_commit(rev, tree_sha, self._idmap._parent_lookup)
87
 
 
88
 
    def __getitem__(self, sha):
89
 
        # See if sha is in map
90
 
        try:
91
 
            (type, type_data) = self._idmap.lookup_git_sha(sha)
92
 
        except KeyError:
93
 
            # if not, see if there are any unconverted revisions and add them 
 
179
    def _get_tree(self, fileid, revid, inv, unusual_modes, expected_sha=None):
 
180
        """Return a Git Tree object from a file id and a revision stored in bzr.
 
181
 
 
182
        :param fileid: fileid in the tree.
 
183
        :param revision: Revision of the tree.
 
184
        """
 
185
        tree = directory_to_tree(inv[fileid],
 
186
            lambda ie: self._get_ie_sha1(ie, inv, unusual_modes),
 
187
            unusual_modes)
 
188
        self._check_expected_sha(expected_sha, tree)
 
189
        return tree
 
190
 
 
191
    def _get_commit(self, rev, tree_sha, expected_sha=None):
 
192
        commit = self._revision_to_commit(rev, tree_sha)
 
193
        self._check_expected_sha(expected_sha, commit)
 
194
        return commit
 
195
 
 
196
    def get_parents(self, sha):
 
197
        """Retrieve the parents of a Git commit by SHA1.
 
198
 
 
199
        :param sha: SHA1 of the commit
 
200
        :raises: KeyError, NotCommitError
 
201
        """
 
202
        return self[sha].parents
 
203
 
 
204
    def _lookup_revision_sha1(self, revid):
 
205
        """Return the SHA1 matching a Bazaar revision."""
 
206
        if revid == NULL_REVISION:
 
207
            return "0" * 40
 
208
        try:
 
209
            return self._idmap.lookup_commit(revid)
 
210
        except KeyError:
 
211
            try:
 
212
                return mapping_registry.parse_revision_id(revid)[0]
 
213
            except errors.InvalidRevisionId:
 
214
                self._update_sha_map(revid)
 
215
                return self._idmap.lookup_commit(revid)
 
216
 
 
217
    def get_raw(self, sha):
 
218
        """Get the raw representation of a Git object by SHA1.
 
219
 
 
220
        :param sha: SHA1 of the git object
 
221
        """
 
222
        obj = self[sha]
 
223
        return (obj.type, obj.as_raw_string())
 
224
 
 
225
    def __contains__(self, sha):
 
226
        # See if sha is in map
 
227
        try:
 
228
            (type, type_data) = self._lookup_git_sha(sha)
 
229
            if type == "commit":
 
230
                return self.repository.has_revision(type_data[0])
 
231
            elif type == "blob":
 
232
                return self.repository.texts.has_version(type_data)
 
233
            elif type == "tree":
 
234
                return self.repository.has_revision(type_data[1])
 
235
            else:
 
236
                raise AssertionError("Unknown object type '%s'" % type)
 
237
        except KeyError:
 
238
            return False
 
239
        else:
 
240
            return True
 
241
 
 
242
    def _lookup_git_sha(self, sha):
 
243
        # See if sha is in map
 
244
        try:
 
245
            return self._idmap.lookup_git_sha(sha)
 
246
        except KeyError:
 
247
            # if not, see if there are any unconverted revisions and add them
94
248
            # to the map, search for sha in map again
95
249
            self._update_sha_map()
96
 
            (type, type_data) = self._idmap.lookup_git_sha(sha)
 
250
            return self._idmap.lookup_git_sha(sha)
 
251
 
 
252
    def __getitem__(self, sha):
 
253
        (type, type_data) = self._lookup_git_sha(sha)
97
254
        # convert object to git object
98
255
        if type == "commit":
99
 
            return self._get_commit(*type_data)
 
256
            try:
 
257
                rev = self.repository.get_revision(type_data[0])
 
258
            except errors.NoSuchRevision:
 
259
                trace.mutter('entry for %s %s in shamap: %r, but not found in repository', type, sha, type_data)
 
260
                raise KeyError(sha)
 
261
            return self._get_commit(rev, type_data[1], expected_sha=sha)
100
262
        elif type == "blob":
101
 
            return self._get_blob(*type_data)
 
263
            return self._get_blob(type_data[0], type_data[1], expected_sha=sha)
102
264
        elif type == "tree":
103
 
            return self._get_tree(*type_data)
 
265
            try:
 
266
                inv = self.repository.get_inventory(type_data[1])
 
267
                rev = self.repository.get_revision(type_data[1])
 
268
            except errors.NoSuchRevision:
 
269
                trace.mutter('entry for %s %s in shamap: %r, but not found in repository', type, sha, type_data)
 
270
                raise KeyError(sha)
 
271
            unusual_modes = extract_unusual_modes(rev)
 
272
            try:
 
273
                return self._get_tree(type_data[0], type_data[1], inv, unusual_modes,
 
274
                                      expected_sha=sha)
 
275
            except errors.NoSuchRevision:
 
276
                raise KeyError(sha)
104
277
        else:
105
278
            raise AssertionError("Unknown object type '%s'" % type)