/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

Implement GitRepository.revision_graph_can_have_wrong_parents().

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