/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

Partially fix pull.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008 Jelmer Vernooij <jelmer@samba.org>
 
1
# Copyright (C) 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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 cStringIO import (
18
 
    StringIO,
19
 
    )
 
17
from bzrlib import osutils, ui, urlutils
 
18
from bzrlib.errors import InvalidRevisionId, NoSuchRevision
 
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.repository import (
 
25
        LocalGitRepository, 
 
26
        GitRepository, 
 
27
        GitFormat,
 
28
        )
 
29
from bzrlib.plugins.git.converter import GitObjectConverter
 
30
from bzrlib.plugins.git.remote import RemoteGitRepository
 
31
 
20
32
import dulwich as git
21
 
from dulwich.client import (
22
 
    SimpleFetchGraphWalker,
23
 
    )
24
 
from dulwich.objects import (
25
 
    Commit,
26
 
    Tag,
27
 
    )
28
 
 
29
 
from bzrlib import (
30
 
    osutils,
31
 
    trace,
32
 
    ui,
33
 
    urlutils,
34
 
    )
35
 
from bzrlib.errors import (
36
 
    InvalidRevisionId,
37
 
    NoSuchRevision,
38
 
    )
39
 
from bzrlib.inventory import (
40
 
    Inventory,
41
 
    InventoryDirectory,
42
 
    InventoryFile,
43
 
    InventoryLink,
44
 
    )
45
 
from bzrlib.lru_cache import (
46
 
    LRUCache,
47
 
    )
48
 
from bzrlib.repository import (
49
 
    InterRepository,
50
 
    )
51
 
from bzrlib.revision import (
52
 
    NULL_REVISION,
53
 
    )
54
 
from bzrlib.tsort import (
55
 
    topo_sort,
56
 
    )
57
 
 
58
 
from bzrlib.plugins.git.converter import (
59
 
    GitObjectConverter,
60
 
    )
61
 
from bzrlib.plugins.git.repository import (
62
 
    LocalGitRepository, 
63
 
    GitRepository, 
64
 
    GitRepositoryFormat,
65
 
    )
66
 
from bzrlib.plugins.git.remote import (
67
 
    RemoteGitRepository,
68
 
    )
 
33
from dulwich.client import SimpleFetchGraphWalker
 
34
from dulwich.objects import Commit
 
35
 
 
36
from cStringIO import StringIO
69
37
 
70
38
 
71
39
class BzrFetchGraphWalker(object):
107
75
        return None
108
76
 
109
77
 
110
 
def import_git_blob(texts, mapping, path, blob, base_inv, parent_id, 
111
 
    revision_id, parent_invs, shagitmap, executable):
 
78
def import_git_blob(repo, mapping, path, blob, inv, parent_invs, gitmap, executable):
112
79
    """Import a git blob object into a bzr repository.
113
80
 
114
 
    :param texts: VersionedFiles to add to
 
81
    :param repo: bzr repository
115
82
    :param path: Path in the tree
116
83
    :param blob: A git blob
117
 
    :return: Inventory delta for this file
118
84
    """
119
85
    file_id = mapping.generate_file_id(path)
120
 
    # We just have to hope this is indeed utf-8:
121
 
    ie = InventoryFile(file_id, urlutils.basename(path).decode("utf-8"), 
122
 
        parent_id)
 
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],
 
89
        osutils.split_lines(blob.data))
 
90
    ie = inv.add_path(path, "file", file_id)
 
91
    ie.revision = text_revision
123
92
    ie.text_size = len(blob.data)
124
93
    ie.text_sha1 = osutils.sha_string(blob.data)
125
94
    ie.executable = executable
126
 
    # If there were no changes compared to the base inventory, there's no need 
127
 
    # for a delta
128
 
    if (file_id in base_inv and 
129
 
        base_inv[file_id].parent_id == ie.parent_id and
130
 
        base_inv[file_id].text_sha1 == ie.text_sha1 and
131
 
        base_inv[file_id].executable == ie.executable):
132
 
        return []
133
 
    # Check what revision we should store
134
 
    parent_keys = []
135
 
    for pinv in parent_invs:
136
 
        if not file_id in pinv:
137
 
            continue
138
 
        if pinv[file_id].text_sha1 == ie.text_sha1:
139
 
            # found a revision in one of the parents to use
140
 
            ie.revision = pinv[file_id].revision
141
 
            break
142
 
        parent_keys.append((file_id, pinv[file_id].revision))
143
 
    if ie.revision is None:
144
 
        # Need to store a new revision
145
 
        ie.revision = revision_id
146
 
        assert file_id is not None
147
 
        assert ie.revision is not None
148
 
        texts.add_lines((file_id, ie.revision), parent_keys,
149
 
            osutils.split_lines(blob.data))
150
 
        shagitmap.add_entry(blob.sha().hexdigest(), "blob",
151
 
            (ie.file_id, ie.revision))
152
 
    if file_id in base_inv:
153
 
        old_path = base_inv.id2path(file_id)
154
 
    else:
155
 
        old_path = None
156
 
    return [(old_path, path, file_id, ie)]
157
 
 
158
 
 
159
 
def import_git_tree(texts, mapping, path, tree, base_inv, parent_id, 
160
 
    revision_id, parent_invs, shagitmap, lookup_object):
 
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):
161
100
    """Import a git tree object into a bzr repository.
162
101
 
163
 
    :param texts: VersionedFiles object to add to
 
102
    :param repo: A Bzr repository object
164
103
    :param path: Path in the tree
165
104
    :param tree: A git tree object
166
 
    :param base_inv: Base inventory against which to return inventory delta
167
 
    :return: Inventory delta for this subtree
 
105
    :param inv: Inventory object
168
106
    """
169
 
    ret = []
170
107
    file_id = mapping.generate_file_id(path)
171
 
    # We just have to hope this is indeed utf-8:
172
 
    ie = InventoryDirectory(file_id, urlutils.basename(path.decode("utf-8")), 
173
 
        parent_id)
174
 
    if not file_id in base_inv:
175
 
        # Newly appeared here
176
 
        ie.revision = revision_id
177
 
        texts.add_lines((file_id, ie.revision), [], [])
178
 
        ret.append((None, path, file_id, ie))
179
 
    else:
180
 
        # See if this has changed at all
181
 
        try:
182
 
            base_sha = shagitmap.lookup_tree(path, base_inv.revision_id)
183
 
        except KeyError:
184
 
            pass
185
 
        else:
186
 
            if base_sha == tree.id:
187
 
                # If nothing has changed since the base revision, we're done
188
 
                return []
189
 
    # Remember for next time
190
 
    existing_children = set()
191
 
    shagitmap.add_entry(tree.id, "tree", (file_id, revision_id))
 
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],
 
111
        [])
 
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))
192
115
    for mode, name, hexsha in tree.entries():
193
116
        entry_kind = (mode & 0700000) / 0100000
194
117
        basename = name.decode("utf-8")
195
 
        existing_children.add(basename)
196
118
        if path == "":
197
119
            child_path = name
198
120
        else:
199
121
            child_path = urlutils.join(path, name)
200
 
        obj = lookup_object(hexsha)
201
122
        if entry_kind == 0:
202
 
            ret.extend(import_git_tree(texts, mapping, child_path, obj, base_inv, 
203
 
                file_id, revision_id, parent_invs, shagitmap, lookup_object))
 
123
            tree = lookup_object(hexsha)
 
124
            import_git_tree(repo, mapping, child_path, tree, inv, parent_invs, gitmap, lookup_object)
204
125
        elif entry_kind == 1:
 
126
            blob = lookup_object(hexsha)
205
127
            fs_mode = mode & 0777
206
 
            ret.extend(import_git_blob(texts, mapping, child_path, obj, base_inv, 
207
 
                file_id, revision_id, parent_invs, shagitmap, 
208
 
                bool(fs_mode & 0111)))
 
128
            import_git_blob(repo, mapping, child_path, blob, inv, parent_invs, gitmap, bool(fs_mode & 0111))
209
129
        else:
210
130
            raise AssertionError("Unknown blob kind, perms=%r." % (mode,))
211
 
    # Remove any children that have disappeared
212
 
    if file_id in base_inv:
213
 
        deletable = [v for k,v in base_inv[file_id].children.iteritems() if k not in existing_children]
214
 
        while deletable:
215
 
            ie = deletable.pop()
216
 
            ret.append((base_inv.id2path(ie.file_id), None, ie.file_id, None))
217
 
            if ie.kind == "directory":
218
 
                deletable.extend(ie.children.values())
219
 
    return ret
220
131
 
221
132
 
222
133
def import_git_objects(repo, mapping, object_iter, target_git_object_retriever, 
223
 
        heads, pb=None):
 
134
        pb=None):
224
135
    """Import a set of git objects into a bzr repository.
225
136
 
226
137
    :param repo: Bazaar repository
231
142
    graph = []
232
143
    root_trees = {}
233
144
    revisions = {}
234
 
    checked = set()
235
 
    heads = list(heads)
236
 
    parent_invs_cache = LRUCache(50)
237
145
    # Find and convert commit objects
238
 
    while heads:
239
 
        if pb is not None:
240
 
            pb.update("finding revisions to fetch", len(graph), None)
241
 
        head = heads.pop()
242
 
        assert isinstance(head, str)
243
 
        o = object_iter[head]
 
146
    for o in object_iter.iterobjects():
244
147
        if isinstance(o, Commit):
245
148
            rev = mapping.import_commit(o)
246
 
            if repo.has_revision(rev.revision_id):
247
 
                continue
248
 
            root_trees[rev.revision_id] = o.tree
 
149
            root_trees[rev.revision_id] = object_iter[o.tree]
249
150
            revisions[rev.revision_id] = rev
250
151
            graph.append((rev.revision_id, rev.parent_ids))
251
 
            target_git_object_retriever._idmap.add_entry(o.sha().hexdigest(),
252
 
                "commit", (rev.revision_id, o._tree))
253
 
            heads.extend([p for p in o.parents if p not in checked])
254
 
        elif isinstance(o, Tag):
255
 
            heads.append(o.object[1])
256
 
        else:
257
 
            trace.warning("Unable to import head object %r" % o)
258
 
        checked.add(head)
 
152
            target_git_object_retriever._idmap.add_entry(o.sha().hexdigest(), "commit", (rev.revision_id, o._tree))
259
153
    # Order the revisions
260
154
    # Create the inventory objects
261
155
    for i, revid in enumerate(topo_sort(graph)):
262
156
        if pb is not None:
263
157
            pb.update("fetching revisions", i, len(graph))
264
 
        root_tree = object_iter[root_trees[revid]]
 
158
        root_tree = root_trees[revid]
265
159
        rev = revisions[revid]
266
160
        # We have to do this here, since we have to walk the tree and 
267
 
        # we need to make sure to import the blobs / trees with the right 
 
161
        # we need to make sure to import the blobs / trees with the riht 
268
162
        # path; this may involve adding them more than once.
 
163
        inv = Inventory()
 
164
        inv.revision_id = rev.revision_id
269
165
        def lookup_object(sha):
270
 
            try:
 
166
            if sha in object_iter:
271
167
                return object_iter[sha]
272
 
            except KeyError:
273
 
                return target_git_object_retriever[sha]
274
 
        parent_invs = []
275
 
        for parent_id in rev.parent_ids:
276
 
            try:
277
 
                parent_invs.append(parent_invs_cache[parent_id])
278
 
            except KeyError:
279
 
                parent_inv = repo.get_inventory(parent_id)
280
 
                parent_invs.append(parent_inv)
281
 
                parent_invs_cache[parent_id] = parent_inv
282
 
        if parent_invs == []:
283
 
            base_inv = Inventory(root_id=None)
284
 
        else:
285
 
            base_inv = parent_invs[0]
286
 
        inv_delta = import_git_tree(repo.texts, mapping, "", root_tree, 
287
 
            base_inv, None, revid, parent_invs, 
288
 
            target_git_object_retriever._idmap, lookup_object)
289
 
        try:
290
 
            basis_id = rev.parent_ids[0]
291
 
        except IndexError:
292
 
            basis_id = NULL_REVISION
293
 
        rev.inventory_sha1, inv = repo.add_inventory_by_delta(basis_id,
294
 
                  inv_delta, rev.revision_id, rev.parent_ids)
295
 
        parent_invs_cache[rev.revision_id] = inv
296
 
        repo.add_revision(rev.revision_id, rev)
297
 
    target_git_object_retriever._idmap.commit()
 
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)
 
172
        repo.add_revision(rev.revision_id, rev, inv)
298
173
 
299
174
 
300
175
class InterGitNonGitRepository(InterRepository):
301
 
    """InterRepository that copies revisions from a Git into a non-Git 
302
 
    repository."""
303
176
 
304
 
    _matching_repo_format = GitRepositoryFormat()
 
177
    _matching_repo_format = GitFormat()
305
178
 
306
179
    @staticmethod
307
180
    def _get_repo_format_to_test():
319
192
        if pb is None:
320
193
            create_pb = pb = ui.ui_factory.nested_progress_bar()
321
194
        target_git_object_retriever = GitObjectConverter(self.target, mapping)
322
 
        recorded_wants = []
323
 
 
324
 
        def record_determine_wants(heads):
325
 
            wants = determine_wants(heads)
326
 
            recorded_wants.extend(wants)
327
 
            return wants
328
195
        
329
196
        try:
330
197
            self.target.lock_write()
331
198
            try:
332
199
                self.target.start_write_group()
333
200
                try:
334
 
                    objects_iter = self.source.fetch_objects(
335
 
                                record_determine_wants, 
 
201
                    objects_iter = self.source.fetch_objects(determine_wants, 
336
202
                                graph_walker, 
337
203
                                target_git_object_retriever.__getitem__, 
338
204
                                progress)
339
205
                    import_git_objects(self.target, mapping, objects_iter, 
340
 
                            target_git_object_retriever, recorded_wants, pb)
 
206
                            target_git_object_retriever, pb)
341
207
                finally:
342
208
                    self.target.commit_write_group()
343
209
            finally:
346
212
            if create_pb:
347
213
                create_pb.finished()
348
214
 
349
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, mapping=None,
350
 
            fetch_spec=None):
351
 
        self.fetch_refs(revision_id=revision_id, pb=pb, find_ghosts=find_ghosts,
352
 
                mapping=mapping, fetch_spec=fetch_spec)
353
 
 
354
 
    def fetch_refs(self, revision_id=None, pb=None, find_ghosts=False, 
355
 
              mapping=None, fetch_spec=None):
 
215
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
 
216
              mapping=None):
356
217
        if mapping is None:
357
218
            mapping = self.source.get_mapping()
358
 
        if revision_id is not None:
359
 
            interesting_heads = [revision_id]
360
 
        elif fetch_spec is not None:
361
 
            interesting_heads = fetch_spec.heads
362
 
        else:
363
 
            interesting_heads = None
364
 
        self._refs = {}
365
 
        def determine_wants(refs):
366
 
            self._refs = refs
367
 
            if interesting_heads is None:
368
 
                ret = [sha for (ref, sha) in refs.iteritems() if not ref.endswith("^{}")]
 
219
        def determine_wants(heads):
 
220
            if revision_id is None:
 
221
                ret = heads.values()
369
222
            else:
370
 
                ret = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in interesting_heads]
 
223
                ret = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
371
224
            return [rev for rev in ret if not self.target.has_revision(mapping.revision_id_foreign_to_bzr(rev))]
372
 
        self.fetch_objects(determine_wants, mapping, pb)
373
 
        return self._refs
 
225
        return self.fetch_objects(determine_wants, mapping, pb)
374
226
 
375
227
    @staticmethod
376
228
    def is_compatible(source, target):
382
234
 
383
235
 
384
236
class InterGitRepository(InterRepository):
385
 
    """InterRepository that copies between Git repositories."""
386
237
 
387
 
    _matching_repo_format = GitRepositoryFormat()
 
238
    _matching_repo_format = GitFormat()
388
239
 
389
240
    @staticmethod
390
241
    def _get_repo_format_to_test():
395
246
        self.fetch(revision_id, pb, find_ghosts=False)
396
247
 
397
248
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
398
 
              mapping=None, fetch_spec=None):
 
249
              mapping=None):
399
250
        if mapping is None:
400
251
            mapping = self.source.get_mapping()
401
252
        def progress(text):
402
 
            trace.info("git: %s", text)
 
253
            info("git: %s", text)
403
254
        r = self.target._git
404
 
        if revision_id is not None:
 
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:
405
258
            args = [mapping.revision_id_bzr_to_foreign(revision_id)[0]]
406
 
        elif fetch_spec is not None:
407
 
            args = [mapping.revision_id_bzr_to_foreign(revid)[0] for revid in fetch_spec.heads]
408
 
        if fetch_spec is None and revision_id is None:
409
 
            determine_wants = r.object_store.determine_wants_all
410
 
        else:
411
259
            determine_wants = lambda x: [y for y in args if not y in r.object_store]
412
260
 
413
261
        graphwalker = SimpleFetchGraphWalker(r.heads().values(), r.get_parents)