/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 get_ancestry() contents.

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.converter 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, gitmap, 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
    gitmap._idmap.add_entry(blob.sha().hexdigest(), "blob", (ie.file_id, ie.revision))
 
96
 
 
97
 
 
98
def import_git_tree(repo, mapping, path, tree, inv, parent_invs, 
 
99
                    gitmap, lookup_object):
82
100
    """Import a git tree object into a bzr repository.
83
101
 
84
102
    :param repo: A Bzr repository object
87
105
    :param inv: Inventory object
88
106
    """
89
107
    file_id = mapping.generate_file_id(path)
90
 
    repo.texts.add_lines((file_id, tree.id),
91
 
        [], #FIXME 
 
108
    text_revision = inv.revision_id
 
109
    repo.texts.add_lines((file_id, text_revision),
 
110
        [(file_id, p[file_id].revision) for p in parent_invs if file_id in p],
92
111
        [])
93
 
    inv.add_path(path, "directory", file_id)
 
112
    ie = inv.add_path(path, "directory", file_id)
 
113
    ie.revision = text_revision
 
114
    gitmap._idmap.add_entry(tree.sha().hexdigest(), "tree", (file_id, text_revision))
94
115
    for mode, name, hexsha in tree.entries():
95
116
        entry_kind = (mode & 0700000) / 0100000
96
117
        basename = name.decode("utf-8")
99
120
        else:
100
121
            child_path = urlutils.join(path, name)
101
122
        if entry_kind == 0:
102
 
            import_git_tree(repo, mapping, child_path, lookup_object, inv)
 
123
            tree = lookup_object(hexsha)
 
124
            import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, gitmap, lookup_object)
103
125
        elif entry_kind == 1:
104
 
            import_git_blob(repo, mapping, child_path, lookup_object, inv)
 
126
            blob = lookup_object(hexsha)
 
127
            fs_mode = mode & 0777
 
128
            import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, gitmap, bool(fs_mode & 0111))
105
129
        else:
106
130
            raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
107
131
 
108
132
 
109
 
def import_git_objects(repo, mapping, object_iter):
 
133
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever, 
 
134
        pb=None):
110
135
    """Import a set of git objects into a bzr repository.
111
136
 
112
137
    :param repo: Bazaar repository
114
139
    :param object_iter: Iterator over Git objects.
115
140
    """
116
141
    # TODO: a more (memory-)efficient implementation of this
117
 
    objects = {}
118
 
    for o in object_iter:
119
 
        objects[o.id] = o
 
142
    graph = []
120
143
    root_trees = {}
 
144
    revisions = {}
121
145
    # Find and convert commit objects
122
 
    for o in objects.iterkeys():
 
146
    for o in object_iter.iterobjects():
123
147
        if isinstance(o, Commit):
124
148
            rev = mapping.import_commit(o)
125
 
            root_trees[rev] = objects[o.tree_sha]
 
149
            root_trees[rev.revision_id] = object_iter[o.tree]
 
150
            revisions[rev.revision_id] = rev
 
151
            graph.append((rev.revision_id, rev.parent_ids))
 
152
            target_git_object_retriever._idmap.add_entry(o.sha().hexdigest(), "commit", (rev.revision_id, o._tree))
 
153
    # Order the revisions
126
154
    # Create the inventory objects
127
 
    for rev, root_tree in root_trees.iteritems():
 
155
    for i, revid in enumerate(topo_sort(graph)):
 
156
        if pb is not None:
 
157
            pb.update("fetching revisions", i, len(graph))
 
158
        root_tree = root_trees[revid]
 
159
        rev = revisions[revid]
128
160
        # We have to do this here, since we have to walk the tree and 
129
161
        # we need to make sure to import the blobs / trees with the riht 
130
162
        # path; this may involve adding them more than once.
131
163
        inv = Inventory()
 
164
        inv.revision_id = rev.revision_id
132
165
        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)
 
166
            if sha in object_iter:
 
167
                return object_iter[sha]
 
168
            return target_git_object_retriever[sha]
 
169
        parent_invs = [repo.get_inventory(r) for r in rev.parent_ids]
 
170
        import_git_tree(repo, mapping, "", root_tree, inv, parent_invs, 
 
171
            target_git_object_retriever, lookup_object)
137
172
        repo.add_revision(rev.revision_id, rev, inv)
138
173
 
139
174
 
140
 
def reconstruct_git_commit(repo, rev):
141
 
    raise NotImplementedError(self.reconstruct_git_commit)
142
 
 
143
 
 
144
 
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)
153
 
 
154
 
    # TODO: Tree
155
 
    # TODO: Blob
156
 
    raise KeyError("No such object %s" % sha)
157
 
 
158
 
 
159
 
class InterGitRepository(InterRepository):
 
175
class InterGitNonGitRepository(InterRepository):
160
176
 
161
177
    _matching_repo_format = GitFormat()
162
178
 
168
184
        """See InterRepository.copy_content."""
169
185
        self.fetch(revision_id, pb, find_ghosts=False)
170
186
 
 
187
    def fetch_objects(self, determine_wants, mapping, pb=None):
 
188
        def progress(text):
 
189
            pb.update("git: %s" % text.rstrip("\r\n"), 0, 0)
 
190
        graph_walker = BzrFetchGraphWalker(self.target, mapping)
 
191
        create_pb = None
 
192
        if pb is None:
 
193
            create_pb = pb = ui.ui_factory.nested_progress_bar()
 
194
        target_git_object_retriever = GitObjectConverter(self.target, mapping)
 
195
        
 
196
        try:
 
197
            self.target.lock_write()
 
198
            try:
 
199
                self.target.start_write_group()
 
200
                try:
 
201
                    objects_iter = self.source.fetch_objects(determine_wants, 
 
202
                                graph_walker, 
 
203
                                target_git_object_retriever.__getitem__, 
 
204
                                progress)
 
205
                    import_git_objects(self.target, mapping, objects_iter, 
 
206
                            target_git_object_retriever, pb)
 
207
                finally:
 
208
                    self.target.commit_write_group()
 
209
            finally:
 
210
                self.target.unlock()
 
211
        finally:
 
212
            if create_pb:
 
213
                create_pb.finished()
 
214
 
171
215
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
172
216
              mapping=None):
173
217
        if mapping is None:
174
218
            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
219
        def determine_wants(heads):
181
220
            if revision_id is None:
182
221
                ret = heads.values()
183
222
            else:
184
 
                ret = [mapping.revision_id_bzr_to_foreign(revision_id)]
 
223
                ret = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
185
224
            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()
 
225
        return self.fetch_objects(determine_wants, mapping, pb)
194
226
 
195
227
    @staticmethod
196
228
    def is_compatible(source, target):
197
229
        """Be compatible with GitRepository."""
198
230
        # FIXME: Also check target uses VersionedFile
199
 
        return (isinstance(source, LocalGitRepository) and 
200
 
                target.supports_rich_root())
 
231
        return (isinstance(source, GitRepository) and 
 
232
                target.supports_rich_root() and
 
233
                not isinstance(target, GitRepository))
 
234
 
 
235
 
 
236
class InterGitRepository(InterRepository):
 
237
 
 
238
    _matching_repo_format = GitFormat()
 
239
 
 
240
    @staticmethod
 
241
    def _get_repo_format_to_test():
 
242
        return None
 
243
 
 
244
    def copy_content(self, revision_id=None, pb=None):
 
245
        """See InterRepository.copy_content."""
 
246
        self.fetch(revision_id, pb, find_ghosts=False)
 
247
 
 
248
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
 
249
              mapping=None):
 
250
        if mapping is None:
 
251
            mapping = self.source.get_mapping()
 
252
        def progress(text):
 
253
            info("git: %s", text)
 
254
        r = self.target._git
 
255
        if revision_id is None:
 
256
            determine_wants = lambda x: [y for y in x.values() if not y in r.object_store]
 
257
        else:
 
258
            args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
 
259
            determine_wants = lambda x: [y for y in args if not y in r.object_store]
 
260
 
 
261
        graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)
 
262
        f, commit = r.object_store.add_pack()
 
263
        try:
 
264
            self.source._git.fetch_pack(path, determine_wants, graphwalker, f.write, progress)
 
265
            f.close()
 
266
            commit()
 
267
        except:
 
268
            f.close()
 
269
            raise
 
270
 
 
271
    @staticmethod
 
272
    def is_compatible(source, target):
 
273
        """Be compatible with GitRepository."""
 
274
        return (isinstance(source, GitRepository) and 
 
275
                isinstance(target, GitRepository))