/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 thin-pack work.

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