/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

ImportĀ RemoteGitBranch._get_config().

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