/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 branch tests.

Show diffs side-by-side

added added

removed removed

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