/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 optimized handling when fetching from git to git.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
17
from bzrlib import osutils, ui, urlutils
18
18
from bzrlib.errors import InvalidRevisionId
19
19
from bzrlib.inventory import Inventory
20
20
from bzrlib.repository import InterRepository
21
21
from bzrlib.trace import info
 
22
from bzrlib.tsort import topo_sort
22
23
 
23
24
from bzrlib.plugins.git import git
24
 
from bzrlib.plugins.git.repository import LocalGitRepository, GitRepository, GitFormat
 
25
from bzrlib.plugins.git.repository import (
 
26
        LocalGitRepository, 
 
27
        GitRepository, 
 
28
        GitFormat,
 
29
        )
25
30
from bzrlib.plugins.git.remote import RemoteGitRepository
26
31
 
 
32
from dulwich.client import SimpleFetchGraphWalker
27
33
from dulwich.objects import Commit
28
34
 
29
35
from cStringIO import StringIO
64
70
        return None
65
71
 
66
72
 
67
 
def import_git_blob(repo, mapping, path, blob):
 
73
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, executable):
68
74
    """Import a git blob object into a bzr repository.
69
75
 
70
76
    :param repo: bzr repository
72
78
    :param blob: A git blob
73
79
    """
74
80
    file_id = mapping.generate_file_id(path)
75
 
    repo.texts.add_lines((file_id, blob.id),
76
 
        [], #FIXME 
 
81
    text_revision = inv.revision_id
 
82
    repo.texts.add_lines((file_id, text_revision),
 
83
        [(file_id, p[file_id].revision) for p in parent_invs if file_id in p],
77
84
        osutils.split_lines(blob.data))
78
 
    inv.add_path(path, "file", file_id)
79
 
 
80
 
 
81
 
def import_git_tree(repo, mapping, path, tree, inv, lookup_object):
 
85
    ie = inv.add_path(path, "file", file_id)
 
86
    ie.revision = text_revision
 
87
    ie.text_size = len(blob.data)
 
88
    ie.text_sha1 = osutils.sha_string(blob.data)
 
89
    ie.executable = executable
 
90
 
 
91
 
 
92
def import_git_tree(repo, mapping, path, tree, inv, parent_invs, lookup_object):
82
93
    """Import a git tree object into a bzr repository.
83
94
 
84
95
    :param repo: A Bzr repository object
87
98
    :param inv: Inventory object
88
99
    """
89
100
    file_id = mapping.generate_file_id(path)
90
 
    repo.texts.add_lines((file_id, tree.id),
91
 
        [], #FIXME 
 
101
    text_revision = inv.revision_id
 
102
    repo.texts.add_lines((file_id, text_revision),
 
103
        [(file_id, p[file_id].revision) for p in parent_invs if file_id in p],
92
104
        [])
93
 
    inv.add_path(path, "directory", file_id)
 
105
    ie = inv.add_path(path, "directory", file_id)
 
106
    ie.revision = text_revision
94
107
    for mode, name, hexsha in tree.entries():
95
108
        entry_kind = (mode & 0700000) / 0100000
96
109
        basename = name.decode("utf-8")
99
112
        else:
100
113
            child_path = urlutils.join(path, name)
101
114
        if entry_kind == 0:
102
 
            import_git_tree(repo, mapping, child_path, lookup_object, inv)
 
115
            tree = lookup_object(hexsha)
 
116
            import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, lookup_object)
103
117
        elif entry_kind == 1:
104
 
            import_git_blob(repo, mapping, child_path, lookup_object, inv)
 
118
            blob = lookup_object(hexsha)
 
119
            fs_mode = mode & 0777
 
120
            import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, bool(fs_mode & 0111))
105
121
        else:
106
122
            raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
107
123
 
108
124
 
109
 
def import_git_objects(repo, mapping, object_iter):
 
125
def import_git_objects(repo, mapping, object_iter, pb=None):
110
126
    """Import a set of git objects into a bzr repository.
111
127
 
112
128
    :param repo: Bazaar repository
115
131
    """
116
132
    # TODO: a more (memory-)efficient implementation of this
117
133
    objects = {}
118
 
    for o in object_iter:
 
134
    for i, o in enumerate(object_iter):
 
135
        if pb is not None:
 
136
            pb.update("fetching objects", i) 
119
137
        objects[o.id] = o
 
138
    graph = []
120
139
    root_trees = {}
 
140
    revisions = {}
121
141
    # Find and convert commit objects
122
 
    for o in objects.iterkeys():
 
142
    for o in objects.itervalues():
123
143
        if isinstance(o, Commit):
124
144
            rev = mapping.import_commit(o)
125
 
            root_trees[rev] = objects[o.tree_sha]
 
145
            root_trees[rev.revision_id] = objects[o.tree]
 
146
            revisions[rev.revision_id] = rev
 
147
            graph.append((rev.revision_id, rev.parent_ids))
 
148
    # Order the revisions
126
149
    # Create the inventory objects
127
 
    for rev, root_tree in root_trees.iteritems():
 
150
    for i, revid in enumerate(topo_sort(graph)):
 
151
        if pb is not None:
 
152
            pb.update("fetching revisions", i, len(graph))
 
153
        root_tree = root_trees[revid]
 
154
        rev = revisions[revid]
128
155
        # We have to do this here, since we have to walk the tree and 
129
156
        # we need to make sure to import the blobs / trees with the riht 
130
157
        # path; this may involve adding them more than once.
131
158
        inv = Inventory()
 
159
        inv.revision_id = rev.revision_id
132
160
        def lookup_object(sha):
133
161
            if sha in objects:
134
162
                return objects[sha]
135
163
            return reconstruct_git_object(repo, mapping, sha)
136
 
        import_git_tree(repo, mapping, "", tree, inv, lookup_object)
 
164
        parent_invs = [repo.get_inventory(r) for r in rev.parent_ids]
 
165
        import_git_tree(repo, mapping, "", root_tree, inv, parent_invs, 
 
166
            lookup_object)
137
167
        repo.add_revision(rev.revision_id, rev, inv)
138
168
 
139
169
 
156
186
    raise KeyError("No such object %s" % sha)
157
187
 
158
188
 
159
 
class InterGitRepository(InterRepository):
 
189
class InterGitNonGitRepository(InterRepository):
160
190
 
161
191
    _matching_repo_format = GitFormat()
162
192
 
173
203
        if mapping is None:
174
204
            mapping = self.source.get_mapping()
175
205
        def progress(text):
176
 
            if pb is not None:
177
 
                pb.note("git: %s" % text)
178
 
            else:
179
 
                info("git: %s" % text)
 
206
            pb.note("git: %s", text)
180
207
        def determine_wants(heads):
181
208
            if revision_id is None:
182
209
                ret = heads.values()
184
211
                ret = [mapping.revision_id_bzr_to_foreign(revision_id)]
185
212
            return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
186
213
        graph_walker = BzrFetchGraphWalker(self.target, mapping)
187
 
        self.target.lock_write()
 
214
        create_pb = None
 
215
        if pb is None:
 
216
            create_pb = pb = ui.ui_factory.nested_progress_bar()
188
217
        try:
189
 
            import_git_objects(self.target, mapping,
190
 
                self.source.fetch_objects(determine_wants, graph_walker, 
191
 
                    progress))
 
218
            self.target.lock_write()
 
219
            try:
 
220
                self.target.start_write_group()
 
221
                try:
 
222
                    import_git_objects(self.target, mapping,
 
223
                        iter(self.source.fetch_objects(determine_wants, graph_walker, 
 
224
                            progress)), pb)
 
225
                finally:
 
226
                    self.target.commit_write_group()
 
227
            finally:
 
228
                self.target.unlock()
192
229
        finally:
193
 
            self.target.unlock()
 
230
            if create_pb:
 
231
                create_pb.finished()
194
232
 
195
233
    @staticmethod
196
234
    def is_compatible(source, target):
197
235
        """Be compatible with GitRepository."""
198
236
        # FIXME: Also check target uses VersionedFile
199
 
        return (isinstance(source, LocalGitRepository) and 
200
 
                target.supports_rich_root())
 
237
        return (isinstance(source, GitRepository) and 
 
238
                target.supports_rich_root() and
 
239
                not isinstance(target, GitRepository))
 
240
 
 
241
 
 
242
class InterGitRepository(InterRepository):
 
243
 
 
244
    _matching_repo_format = GitFormat()
 
245
 
 
246
    @staticmethod
 
247
    def _get_repo_format_to_test():
 
248
        return None
 
249
 
 
250
    def copy_content(self, revision_id=None, pb=None):
 
251
        """See InterRepository.copy_content."""
 
252
        self.fetch(revision_id, pb, find_ghosts=False)
 
253
 
 
254
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
 
255
              mapping=None):
 
256
        if mapping is None:
 
257
            mapping = self.source.get_mapping()
 
258
        def progress(text):
 
259
            info("git: %s", text)
 
260
        r = self.target._git
 
261
        if revision_id is None:
 
262
            determine_wants = lambda x: [y for y in x.values() if not y in r.object_store]
 
263
        else:
 
264
            args = [mapping.revision_id_bzr_to_foreign(revision_id)]
 
265
            determine_wants = lambda x: [y for y in args if not y in r.object_store]
 
266
 
 
267
        graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)
 
268
        f, commit = r.object_store.add_pack()
 
269
        try:
 
270
            self.source._git.fetch_pack(path, determine_wants, graphwalker, f.write, progress)
 
271
            f.close()
 
272
            commit()
 
273
        except:
 
274
            f.close()
 
275
            raise
 
276
 
 
277
    @staticmethod
 
278
    def is_compatible(source, target):
 
279
        """Be compatible with GitRepository."""
 
280
        return (isinstance(source, GitRepository) and 
 
281
                isinstance(target, GitRepository))