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

Fix git-import.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
from bzrlib import osutils, ui, urlutils
18
 
from bzrlib.errors import InvalidRevisionId
 
18
from bzrlib.errors import InvalidRevisionId, NoSuchRevision
19
19
from bzrlib.inventory import Inventory
20
20
from bzrlib.repository import InterRepository
21
21
from bzrlib.trace import info
22
22
from bzrlib.tsort import topo_sort
23
23
 
24
 
from bzrlib.plugins.git import git
25
24
from bzrlib.plugins.git.repository import (
26
25
        LocalGitRepository, 
27
26
        GitRepository, 
28
27
        GitFormat,
29
28
        )
 
29
from bzrlib.plugins.git.converter import GitObjectConverter
30
30
from bzrlib.plugins.git.remote import RemoteGitRepository
31
31
 
 
32
import dulwich as git
 
33
from dulwich.client import SimpleFetchGraphWalker
32
34
from dulwich.objects import Commit
33
35
 
34
36
from cStringIO import StringIO
35
37
 
36
38
 
37
39
class BzrFetchGraphWalker(object):
 
40
    """GraphWalker implementation that uses a Bazaar repository."""
38
41
 
39
42
    def __init__(self, repository, mapping):
40
43
        self.repository = repository
43
46
        self.heads = set(repository.all_revision_ids())
44
47
        self.parents = {}
45
48
 
 
49
    def __iter__(self):
 
50
        return iter(self.next, None)
 
51
 
46
52
    def ack(self, sha):
47
53
        revid = self.mapping.revision_id_foreign_to_bzr(sha)
48
54
        self.remove(revid)
49
55
 
50
56
    def remove(self, revid):
51
57
        self.done.add(revid)
52
 
        if ref in self.heads:
 
58
        if revid in self.heads:
53
59
            self.heads.remove(revid)
54
60
        if revid in self.parents:
55
61
            for p in self.parents[revid]:
63
69
            self.heads.update([p for p in ps if not p in self.done])
64
70
            try:
65
71
                self.done.add(ret)
66
 
                return self.mapping.revision_id_bzr_to_foreign(ret)
 
72
                return self.mapping.revision_id_bzr_to_foreign(ret)[0]
67
73
            except InvalidRevisionId:
68
74
                pass
69
75
        return None
70
76
 
71
77
 
72
 
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, executable):
 
78
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, gitmap, executable):
73
79
    """Import a git blob object into a bzr repository.
74
80
 
75
81
    :param repo: bzr repository
86
92
    ie.text_size = len(blob.data)
87
93
    ie.text_sha1 = osutils.sha_string(blob.data)
88
94
    ie.executable = executable
89
 
 
90
 
 
91
 
def import_git_tree(repo, mapping, path, tree, inv, parent_invs, lookup_object):
 
95
    gitmap._idmap.add_entry(blob.sha().hexdigest(), "blob", (ie.file_id, ie.revision))
 
96
 
 
97
 
 
98
def import_git_tree(repo, mapping, path, tree, inv, parent_invs, 
 
99
                    gitmap, lookup_object):
92
100
    """Import a git tree object into a bzr repository.
93
101
 
94
102
    :param repo: A Bzr repository object
103
111
        [])
104
112
    ie = inv.add_path(path, "directory", file_id)
105
113
    ie.revision = text_revision
 
114
    gitmap._idmap.add_entry(tree.sha().hexdigest(), "tree", (file_id, text_revision))
106
115
    for mode, name, hexsha in tree.entries():
107
116
        entry_kind = (mode & 0700000) / 0100000
108
117
        basename = name.decode("utf-8")
112
121
            child_path = urlutils.join(path, name)
113
122
        if entry_kind == 0:
114
123
            tree = lookup_object(hexsha)
115
 
            import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, lookup_object)
 
124
            import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, gitmap, lookup_object)
116
125
        elif entry_kind == 1:
117
126
            blob = lookup_object(hexsha)
118
127
            fs_mode = mode & 0777
119
 
            import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, bool(fs_mode & 0111))
 
128
            import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, gitmap, bool(fs_mode & 0111))
120
129
        else:
121
130
            raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
122
131
 
123
132
 
124
 
def import_git_objects(repo, mapping, object_iter, pb=None):
 
133
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever, 
 
134
        pb=None):
125
135
    """Import a set of git objects into a bzr repository.
126
136
 
127
137
    :param repo: Bazaar repository
129
139
    :param object_iter: Iterator over Git objects.
130
140
    """
131
141
    # TODO: a more (memory-)efficient implementation of this
132
 
    objects = {}
133
 
    for i, o in enumerate(object_iter):
134
 
        if pb is not None:
135
 
            pb.update("fetching objects", i) 
136
 
        objects[o.id] = o
137
142
    graph = []
138
143
    root_trees = {}
139
144
    revisions = {}
140
145
    # Find and convert commit objects
141
 
    for o in objects.itervalues():
 
146
    for o in object_iter.iterobjects():
142
147
        if isinstance(o, Commit):
143
148
            rev = mapping.import_commit(o)
144
 
            root_trees[rev.revision_id] = objects[o.tree]
 
149
            root_trees[rev.revision_id] = object_iter[o.tree]
145
150
            revisions[rev.revision_id] = rev
146
151
            graph.append((rev.revision_id, rev.parent_ids))
 
152
            target_git_object_retriever._idmap.add_entry(o.sha().hexdigest(), "commit", (rev.revision_id, o._tree))
147
153
    # Order the revisions
148
154
    # Create the inventory objects
149
155
    for i, revid in enumerate(topo_sort(graph)):
157
163
        inv = Inventory()
158
164
        inv.revision_id = rev.revision_id
159
165
        def lookup_object(sha):
160
 
            if sha in objects:
161
 
                return objects[sha]
162
 
            return reconstruct_git_object(repo, mapping, sha)
 
166
            if sha in object_iter:
 
167
                return object_iter[sha]
 
168
            return target_git_object_retriever[sha]
163
169
        parent_invs = [repo.get_inventory(r) for r in rev.parent_ids]
164
 
        import_git_tree(repo, mapping, "", root_tree, inv, parent_invs, lookup_object)
 
170
        import_git_tree(repo, mapping, "", root_tree, inv, parent_invs, 
 
171
            target_git_object_retriever, lookup_object)
165
172
        repo.add_revision(rev.revision_id, rev, inv)
166
173
 
167
174
 
168
 
def reconstruct_git_commit(repo, rev):
169
 
    raise NotImplementedError(self.reconstruct_git_commit)
170
 
 
171
 
 
172
 
def reconstruct_git_object(repo, mapping, sha):
173
 
    # Commit
174
 
    revid = mapping.revision_id_foreign_to_bzr(sha)
175
 
    try:
176
 
        rev = repo.get_revision(revid)
177
 
    except NoSuchRevision:
178
 
        pass
179
 
    else:
180
 
        return reconstruct_git_commit(rev)
181
 
 
182
 
    # TODO: Tree
183
 
    # TODO: Blob
184
 
    raise KeyError("No such object %s" % sha)
185
 
 
186
 
 
187
 
class InterGitRepository(InterRepository):
 
175
class InterGitNonGitRepository(InterRepository):
188
176
 
189
177
    _matching_repo_format = GitFormat()
190
178
 
196
184
        """See InterRepository.copy_content."""
197
185
        self.fetch(revision_id, pb, find_ghosts=False)
198
186
 
199
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
200
 
              mapping=None):
201
 
        if mapping is None:
202
 
            mapping = self.source.get_mapping()
 
187
    def fetch_objects(self, determine_wants, mapping, pb=None):
203
188
        def progress(text):
204
 
            pb.note("git: %s", text)
205
 
        def determine_wants(heads):
206
 
            if revision_id is None:
207
 
                ret = heads.values()
208
 
            else:
209
 
                ret = [mapping.revision_id_bzr_to_foreign(revision_id)]
210
 
            return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
 
189
            pb.update("git: %s" % text.rstrip("\r\n"), 0, 0)
211
190
        graph_walker = BzrFetchGraphWalker(self.target, mapping)
212
191
        create_pb = None
213
192
        if pb is None:
214
193
            create_pb = pb = ui.ui_factory.nested_progress_bar()
 
194
        target_git_object_retriever = GitObjectConverter(self.target, mapping)
 
195
        
215
196
        try:
216
197
            self.target.lock_write()
217
198
            try:
218
199
                self.target.start_write_group()
219
200
                try:
220
 
                    import_git_objects(self.target, mapping,
221
 
                        iter(self.source.fetch_objects(determine_wants, graph_walker, 
222
 
                            progress)), pb)
 
201
                    objects_iter = self.source.fetch_objects(determine_wants, 
 
202
                                graph_walker, 
 
203
                                target_git_object_retriever.__getitem__, 
 
204
                                progress)
 
205
                    import_git_objects(self.target, mapping, objects_iter, 
 
206
                            target_git_object_retriever, pb)
223
207
                finally:
224
208
                    self.target.commit_write_group()
225
209
            finally:
228
212
            if create_pb:
229
213
                create_pb.finished()
230
214
 
 
215
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
 
216
              mapping=None, fetch_spec=None):
 
217
        self.fetch_refs(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts,
 
218
                mapping=mapping, fetch_spec=fetch_spec)
 
219
 
 
220
    def fetch_refs(self, revision_id=None, pb=None, find_ghosts=False, 
 
221
              mapping=None, fetch_spec=None):
 
222
        if mapping is None:
 
223
            mapping = self.source.get_mapping()
 
224
        if revision_id is not None:
 
225
            interesting_heads = [revision_id]
 
226
        elif fetch_spec is not None:
 
227
            interesting_heads = fetch_spec.heads
 
228
        else:
 
229
            interesting_heads = None
 
230
        self._refs = {}
 
231
        def determine_wants(refs):
 
232
            self._refs = refs
 
233
            if interesting_heads is None:
 
234
                ret = [sha for (ref, sha) in refs.iteritems() if not ref.endswith("^{}")]
 
235
            else:
 
236
                ret = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in interesting_heads]
 
237
            return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
 
238
        self.fetch_objects(determine_wants, mapping, pb)
 
239
        return self._refs
 
240
 
231
241
    @staticmethod
232
242
    def is_compatible(source, target):
233
243
        """Be compatible with GitRepository."""
234
244
        # FIXME: Also check target uses VersionedFile
235
245
        return (isinstance(source, GitRepository) and 
236
 
                target.supports_rich_root())
 
246
                target.supports_rich_root() and
 
247
                not isinstance(target, GitRepository))
 
248
 
 
249
 
 
250
class InterGitRepository(InterRepository):
 
251
 
 
252
    _matching_repo_format = GitFormat()
 
253
 
 
254
    @staticmethod
 
255
    def _get_repo_format_to_test():
 
256
        return None
 
257
 
 
258
    def copy_content(self, revision_id=None, pb=None):
 
259
        """See InterRepository.copy_content."""
 
260
        self.fetch(revision_id, pb, find_ghosts=False)
 
261
 
 
262
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
 
263
              mapping=None, fetch_spec=None):
 
264
        if mapping is None:
 
265
            mapping = self.source.get_mapping()
 
266
        def progress(text):
 
267
            info("git: %s", text)
 
268
        r = self.target._git
 
269
        if revision_id is not None:
 
270
            args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
 
271
        elif fetch_spec is not None:
 
272
            args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in fetch_spec.heads]
 
273
        if fetch_spec is None and revision_id is None:
 
274
            determine_wants = r.object_store.determine_wants_all
 
275
        else:
 
276
            determine_wants = lambda x: [y for y in args if not y in r.object_store]
 
277
 
 
278
        graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)
 
279
        f, commit = r.object_store.add_pack()
 
280
        try:
 
281
            self.source._git.fetch_pack(path, determine_wants, graphwalker, f.write, progress)
 
282
            f.close()
 
283
            commit()
 
284
        except:
 
285
            f.close()
 
286
            raise
 
287
 
 
288
    @staticmethod
 
289
    def is_compatible(source, target):
 
290
        """Be compatible with GitRepository."""
 
291
        return (isinstance(source, GitRepository) and 
 
292
                isinstance(target, GitRepository))