/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 branch cloning.

Show diffs side-by-side

added added

removed removed

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