/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

startĀ onĀ 0.4.4

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