/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

Somewhat fix commit in git working trees.

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