/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 converter.py

Fix tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Map from Git sha's to Bazaar objects."""
18
18
 
 
19
import stat
 
20
 
 
21
import bzrlib
 
22
from bzrlib import ui
 
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
 
19
31
from dulwich.objects import (
20
32
    Blob,
21
33
    Tree,
22
34
    )
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
 
    )
42
 
 
43
 
from bzrlib.plugins.git.mapping import (
44
 
    default_mapping,
45
 
    directory_to_tree,
46
 
    extract_unusual_modes,
47
 
    mapping_registry,
48
 
    revision_to_commit,
49
 
    )
50
 
from bzrlib.plugins.git.shamap import (
51
 
    SqliteGitShaMap,
52
 
    TdbGitShaMap,
53
 
    )
54
 
 
55
 
 
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."""
 
35
 
 
36
 
 
37
class GitObjectConverter(object):
65
38
 
66
39
    def __init__(self, repository, mapping=None):
67
40
        self.repository = repository
68
41
        if mapping is None:
69
 
            self.mapping = default_mapping
 
42
            self.mapping = self.repository.get_mapping()
70
43
        else:
71
44
            self.mapping = mapping
72
 
        try:
73
 
            self._idmap = TdbGitShaMap.from_repository(repository)
74
 
        except ImportError:
75
 
            self._idmap = SqliteGitShaMap.from_repository(repository)
 
45
        self._idmap = GitShaMap(self.repository._transport)
76
46
 
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
 
47
    def _update_sha_map(self):
 
48
        all_revids = self.repository.all_revision_ids()
84
49
        graph = self.repository.get_graph()
85
50
        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]
87
51
        pb = ui.ui_factory.nested_progress_bar()
88
52
        try:
89
 
            for i, revid in enumerate(missing_revids):
90
 
                pb.update("updating git map", i, len(missing_revids))
 
53
            for i, revid in enumerate(graph.iter_topo_order(all_revids)):
 
54
                if revid in present_revids:
 
55
                    continue
 
56
                pb.update("updating git map", i, len(all_revids))
91
57
                self._update_sha_map_revision(revid)
92
58
        finally:
93
59
            self._idmap.commit()
94
60
            pb.finished()
95
61
 
96
 
    def __iter__(self):
97
 
        self._update_sha_map()
98
 
        return iter(self._idmap.sha1s())
99
 
 
100
62
    def _update_sha_map_revision(self, revid):
101
63
        inv = self.repository.get_inventory(revid)
 
64
        objects = inventory_to_tree_and_blobs(self.repository, self.mapping, revid)
 
65
        for sha, o, path in objects:
 
66
            if path == "":
 
67
                tree_sha = sha
 
68
            ie = inv[inv.path2id(path)]
 
69
            if ie.kind in ("file", "symlink"):
 
70
                self._idmap.add_entry(sha, "blob", (ie.file_id, ie.revision))
 
71
            elif ie.kind == "directory":
 
72
                self._idmap.add_entry(sha, "tree", (path, ie.revision))
 
73
            else:
 
74
                raise AssertionError()
102
75
        rev = self.repository.get_revision(revid)
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):
 
76
        commit_obj = revision_to_commit(rev, tree_sha, self._idmap._parent_lookup)
 
77
        self._idmap.add_entry(commit_obj.sha().hexdigest(), "commit", (revid, tree_sha))
 
78
 
 
79
    def _get_blob(self, fileid, revision):
149
80
        """Return a Git Blob object from a fileid and revision stored in bzr.
150
81
        
151
82
        :param fileid: File id of the text
152
83
        :param revision: Revision of the text
153
84
        """
154
 
        text = self.repository.texts.get_record_stream([(fileid, revision)],
155
 
            "unordered", True).next().get_bytes_as("fulltext")
 
85
        text = self.repository.texts.get_record_stream([(fileid, revision)], "unordered", True).next().get_bytes_as("fulltext")
156
86
        blob = Blob()
157
87
        blob._text = text
158
 
        self._check_expected_sha(expected_sha, blob)
159
88
        return blob
160
89
 
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.
 
90
    def _get_tree(self, path, revid, inv=None):
 
91
        """Return a Git Tree object from a path and a revision stored in bzr.
163
92
 
164
 
        :param fileid: fileid in the tree.
 
93
        :param path: path in the tree.
165
94
        :param revision: Revision of the tree.
166
95
        """
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)
 
96
        if inv is None:
 
97
            inv = self.repository.get_inventory(revid)
 
98
        tree = Tree()
 
99
        fileid = inv.path2id(path)
 
100
        for name, ie in inv[fileid].children.iteritems():
 
101
            if ie.kind == "directory":
 
102
                subtree = self._get_tree(inv.id2path(ie.file_id), revid, inv)
 
103
                tree.add(stat.S_IFDIR, name.encode('UTF-8'), subtree.sha().hexdigest())
 
104
            elif ie.kind == "file":
 
105
                blob = self._get_blob(inv.path2id(ie.file_id), revid)
 
106
                mode = stat.S_IFREG | 0644
 
107
                if ie.executable:
 
108
                    mode |= 0111
 
109
                tree.add(mode, name.encode('UTF-8'), blob.sha().hexdigest())
 
110
            elif ie.kind == "symlink":
 
111
                raise AssertionError("Symlinks not yet supported")
 
112
        tree.serialize()
171
113
        return tree
172
114
 
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)
 
115
    def _get_commit(self, revid, tree_sha):
 
116
        rev = self.repository.get_revision(revid)
 
117
        return revision_to_commit(rev, tree_sha, self._idmap._parent_lookup)
 
118
 
 
119
    def __getitem__(self, sha):
 
120
        # See if sha is in map
 
121
        try:
 
122
            (type, type_data) = self._idmap.lookup_git_sha(sha)
219
123
        except KeyError:
220
124
            # if not, see if there are any unconverted revisions and add them 
221
125
            # to the map, search for sha in map again
222
126
            self._update_sha_map()
223
 
            return self._idmap.lookup_git_sha(sha)
224
 
 
225
 
    def __getitem__(self, sha):
226
 
        (type, type_data) = self._lookup_git_sha(sha)
 
127
            (type, type_data) = self._idmap.lookup_git_sha(sha)
227
128
        # convert object to git object
228
129
        if type == "commit":
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)
 
130
            return self._get_commit(*type_data)
235
131
        elif type == "blob":
236
 
            return self._get_blob(type_data[0], type_data[1], expected_sha=sha)
 
132
            return self._get_blob(*type_data)
237
133
        elif type == "tree":
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)
 
134
            return self._get_tree(*type_data)
246
135
        else:
247
136
            raise AssertionError("Unknown object type '%s'" % type)