/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

Fix git -> git fetching.

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