/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

Merge in dulwich.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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
 
    )
42
 
from bzrlib.tsort import topo_sort
43
 
 
44
 
from bzrlib.plugins.git.converter import (
45
 
    GitObjectConverter,
46
 
    )
47
 
from bzrlib.plugins.git.repository import (
48
 
    LocalGitRepository, 
49
 
    GitRepository, 
50
 
    GitFormat,
51
 
    )
52
 
from bzrlib.plugins.git.remote import (
53
 
    RemoteGitRepository,
54
 
    )
55
 
 
56
 
 
57
 
class BzrFetchGraphWalker(object):
58
 
    """GraphWalker implementation that uses a Bazaar repository."""
59
 
 
60
 
    def __init__(self, repository, mapping):
61
 
        self.repository = repository
62
 
        self.mapping = mapping
63
 
        self.done = set()
64
 
        self.heads = set(repository.all_revision_ids())
65
 
        self.parents = {}
66
 
 
67
 
    def __iter__(self):
68
 
        return iter(self.next, None)
69
 
 
70
 
    def ack(self, sha):
71
 
        revid = self.mapping.revision_id_foreign_to_bzr(sha)
72
 
        self.remove(revid)
73
 
 
74
 
    def remove(self, revid):
75
 
        self.done.add(revid)
76
 
        if revid in self.heads:
77
 
            self.heads.remove(revid)
78
 
        if revid in self.parents:
79
 
            for p in self.parents[revid]:
80
 
                self.remove(p)
81
 
 
82
 
    def next(self):
83
 
        while self.heads:
84
 
            ret = self.heads.pop()
85
 
            ps = self.repository.get_parent_map([ret])[ret]
86
 
            self.parents[ret] = ps
87
 
            self.heads.update([p for p in ps if not p in self.done])
88
 
            try:
89
 
                self.done.add(ret)
90
 
                return self.mapping.revision_id_bzr_to_foreign(ret)[0]
91
 
            except InvalidRevisionId:
92
 
                pass
93
 
        return None
94
 
 
95
 
 
96
 
def import_git_blob(texts, mapping, path, blob, inv, parent_invs, shagitmap,
97
 
    executable):
98
 
    """Import a git blob object into a bzr repository.
99
 
 
100
 
    :param texts: VersionedFiles to add to
101
 
    :param path: Path in the tree
102
 
    :param blob: A git blob
103
 
    """
104
 
    file_id = mapping.generate_file_id(path)
105
 
    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),
109
 
        [(file_id, p[file_id].revision) for p in parent_invs if file_id in p],
110
 
        osutils.split_lines(blob.data))
111
 
    ie = inv.add_path(path, "file", file_id)
112
 
    ie.revision = text_revision
113
 
    ie.text_size = len(blob.data)
114
 
    ie.text_sha1 = osutils.sha_string(blob.data)
115
 
    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):
122
 
    """Import a git tree object into a bzr repository.
123
 
 
124
 
    :param texts: VersionedFiles object to add to
125
 
    :param path: Path in the tree
126
 
    :param tree: A git tree object
127
 
    :param inv: Inventory object
128
 
    """
129
 
    file_id = mapping.generate_file_id(path)
130
 
    text_revision = inv.revision_id
131
 
    texts.add_lines((file_id, text_revision),
132
 
        [(file_id, p[file_id].revision) for p in parent_invs if file_id in p],
133
 
        [])
134
 
    ie = inv.add_path(path, "directory", file_id)
135
 
    ie.revision = text_revision
136
 
    shagitmap.add_entry(tree.id, "tree", (file_id, text_revision))
137
 
    for mode, name, hexsha in tree.entries():
138
 
        entry_kind = (mode & 0700000) / 0100000
139
 
        basename = name.decode("utf-8")
140
 
        if path == "":
141
 
            child_path = name
142
 
        else:
143
 
            child_path = urlutils.join(path, name)
144
 
        obj = lookup_object(hexsha)
145
 
        if entry_kind == 0:
146
 
            import_git_tree(texts, mapping, child_path, obj, inv, parent_invs,
147
 
                shagitmap, lookup_object)
148
 
        elif entry_kind == 1:
149
 
            fs_mode = mode & 0777
150
 
            import_git_blob(texts, mapping, child_path, obj, inv, parent_invs,
151
 
                shagitmap, bool(fs_mode & 0111))
152
 
        else:
153
 
            raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
154
 
 
155
 
 
156
 
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever, 
157
 
        pb=None):
158
 
    """Import a set of git objects into a bzr repository.
159
 
 
160
 
    :param repo: Bazaar repository
161
 
    :param mapping: Mapping to use
162
 
    :param object_iter: Iterator over Git objects.
163
 
    """
164
 
    # TODO: a more (memory-)efficient implementation of this
165
 
    graph = []
166
 
    root_trees = {}
167
 
    revisions = {}
168
 
    # Find and convert commit objects
169
 
    for o in object_iter.iterobjects():
170
 
        if isinstance(o, Commit):
171
 
            rev = mapping.import_commit(o)
172
 
            root_trees[rev.revision_id] = object_iter[o.tree]
173
 
            revisions[rev.revision_id] = rev
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))
177
 
    # Order the revisions
178
 
    # Create the inventory objects
179
 
    for i, revid in enumerate(topo_sort(graph)):
180
 
        if pb is not None:
181
 
            pb.update("fetching revisions", i, len(graph))
182
 
        root_tree = root_trees[revid]
183
 
        rev = revisions[revid]
184
 
        # We have to do this here, since we have to walk the tree and 
185
 
        # we need to make sure to import the blobs / trees with the riht 
186
 
        # path; this may involve adding them more than once.
187
 
        inv = Inventory()
188
 
        inv.revision_id = rev.revision_id
189
 
        def lookup_object(sha):
190
 
            if sha in object_iter:
191
 
                return object_iter[sha]
192
 
            return target_git_object_retriever[sha]
193
 
        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)
196
 
        repo.add_revision(rev.revision_id, rev, inv)
197
 
 
198
 
 
199
 
class InterGitNonGitRepository(InterRepository):
200
 
 
201
 
    _matching_repo_format = GitFormat()
202
 
 
203
 
    @staticmethod
204
 
    def _get_repo_format_to_test():
205
 
        return None
206
 
 
207
 
    def copy_content(self, revision_id=None, pb=None):
208
 
        """See InterRepository.copy_content."""
209
 
        self.fetch(revision_id, pb, find_ghosts=False)
210
 
 
211
 
    def fetch_objects(self, determine_wants, mapping, pb=None):
212
 
        def progress(text):
213
 
            pb.update("git: %s" % text.rstrip("\r\n"), 0, 0)
214
 
        graph_walker = BzrFetchGraphWalker(self.target, mapping)
215
 
        create_pb = None
216
 
        if pb is None:
217
 
            create_pb = pb = ui.ui_factory.nested_progress_bar()
218
 
        target_git_object_retriever = GitObjectConverter(self.target, mapping)
219
 
        
220
 
        try:
221
 
            self.target.lock_write()
222
 
            try:
223
 
                self.target.start_write_group()
224
 
                try:
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)
231
 
                finally:
232
 
                    self.target.commit_write_group()
233
 
            finally:
234
 
                self.target.unlock()
235
 
        finally:
236
 
            if create_pb:
237
 
                create_pb.finished()
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
 
 
265
 
    @staticmethod
266
 
    def is_compatible(source, target):
267
 
        """Be compatible with GitRepository."""
268
 
        # FIXME: Also check target uses VersionedFile
269
 
        return (isinstance(source, GitRepository) and 
270
 
                target.supports_rich_root() and
271
 
                not isinstance(target, GitRepository))
272
 
 
273
 
 
274
 
class InterGitRepository(InterRepository):
275
 
 
276
 
    _matching_repo_format = GitFormat()
277
 
 
278
 
    @staticmethod
279
 
    def _get_repo_format_to_test():
280
 
        return None
281
 
 
282
 
    def copy_content(self, revision_id=None, pb=None):
283
 
        """See InterRepository.copy_content."""
284
 
        self.fetch(revision_id, pb, find_ghosts=False)
285
 
 
286
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
287
 
              mapping=None, fetch_spec=None):
288
 
        if mapping is None:
289
 
            mapping = self.source.get_mapping()
290
 
        def progress(text):
291
 
            trace.info("git: %s", text)
292
 
        r = self.target._git
293
 
        if revision_id is not None:
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:
300
 
            determine_wants = lambda x: [y for y in args if not y in r.object_store]
301
 
 
302
 
        graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)
303
 
        f, commit = r.object_store.add_pack()
304
 
        try:
305
 
            self.source._git.fetch_pack(path, determine_wants, graphwalker, f.write, progress)
306
 
            f.close()
307
 
            commit()
308
 
        except:
309
 
            f.close()
310
 
            raise
311
 
 
312
 
    @staticmethod
313
 
    def is_compatible(source, target):
314
 
        """Be compatible with GitRepository."""
315
 
        return (isinstance(source, GitRepository) and 
316
 
                isinstance(target, GitRepository))