/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 helper function, always set encoding to utf-8.

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