/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

Start working on 0.3.1.

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