/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

make add_entry private.

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