/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

Cope with NULL_REVISION.

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