/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 FOSDEM roundtripping notes.

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