/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

Add more tests for fetch code.

Show diffs side-by-side

added added

removed removed

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