/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

Support remote dpush (except for references).

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