/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 push.py

Licensing information is in all of the individual files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009-2010 Jelmer Vernooij <jelmer@samba.org>
 
1
# Copyright (C) 2009 Jelmer Vernooij <jelmer@samba.org>
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
16
16
 
17
17
"""Push implementation that simply prints message saying push is not supported."""
18
18
 
 
19
from dulwich.client import (
 
20
    SimpleFetchGraphWalker,
 
21
    )
 
22
from dulwich.repo import (
 
23
    MissingObjectFinder,
 
24
    )
 
25
 
19
26
from bzrlib import (
20
 
    errors,
21
27
    ui,
22
28
    )
23
29
from bzrlib.repository import (
27
33
    NULL_REVISION,
28
34
    )
29
35
 
 
36
from bzrlib.plugins.git.converter import (
 
37
    BazaarObjectStore,
 
38
    )
30
39
from bzrlib.plugins.git.errors import (
31
40
    NoPushSupport,
32
41
    )
33
 
from bzrlib.plugins.git.object_store import (
34
 
    BazaarObjectStore,
35
 
    )
36
42
from bzrlib.plugins.git.repository import (
37
43
    GitRepository,
38
44
    LocalGitRepository,
48
54
 
49
55
    """
50
56
 
51
 
    def __init__(self, store, source, pb=None):
 
57
    def __init__(self, source, mapping, pb=None):
52
58
        """Create a new missing objects iterator.
53
59
 
54
60
        """
55
61
        self.source = source
56
 
        self._object_store = store
 
62
        self._object_store = BazaarObjectStore(self.source, mapping)
 
63
        self._revids = set()
 
64
        self._sent_shas = set()
57
65
        self._pending = []
58
66
        self.pb = pb
59
67
 
60
 
    def import_revisions(self, revids, roundtrip):
61
 
        """Import a set of revisions into this git repository.
62
 
 
63
 
        :param revids: Revision ids of revisions to import
64
 
        :param roundtrip: Whether to roundtrip bzr metadata
65
 
        """
 
68
    def import_revisions(self, revids):
 
69
        self._revids.update(revids)
66
70
        for i, revid in enumerate(revids):
67
71
            if self.pb:
68
72
                self.pb.update("pushing revisions", i, len(revids))
69
 
            git_commit = self.import_revision(revid, roundtrip)
 
73
            git_commit = self.import_revision(revid)
70
74
            yield (revid, git_commit)
71
75
 
72
 
    def import_revision(self, revid, roundtrip):
73
 
        """Import a revision into this Git repository.
74
 
 
75
 
        :param revid: Revision id of the revision
76
 
        :param roundtrip: Whether to roundtrip bzr metadata
 
76
    def need_sha(self, sha):
 
77
        if sha in self._sent_shas:
 
78
            return False
 
79
        (type, (fileid, revid)) = self._object_store._idmap.lookup_git_sha(sha)
 
80
        assert type in ("blob", "tree")
 
81
        if revid in self._revids:
 
82
            # Not sent yet, and part of the set of revisions to send
 
83
            return True
 
84
        # Not changed in the revisions to send, so either not necessary
 
85
        # or already present remotely (as git doesn't do ghosts)
 
86
        return False
 
87
 
 
88
    def queue(self, sha, obj, path, ie=None, inv=None):
 
89
        if obj is None:
 
90
            obj = (ie, inv)
 
91
        self._pending.append((obj, path))
 
92
        self._sent_shas.add(sha)
 
93
 
 
94
    def import_revision(self, revid):
 
95
        """Import the gist of a revision into this Git repository.
 
96
 
77
97
        """
78
 
        tree = self._object_store.tree_cache.revision_tree(revid)
79
 
        rev = self.source.get_revision(revid)
80
 
        commit = None
81
 
        for path, obj, ie in self._object_store._revision_to_objects(rev, tree,
82
 
            roundtrip):
83
 
            if obj.type_name == "commit":
84
 
                commit = obj
85
 
            self._pending.append((obj, path))
 
98
        inv = self.source.get_inventory(revid)
 
99
        todo = [inv.root]
 
100
        tree_sha = None
 
101
        while todo:
 
102
            ie = todo.pop()
 
103
            (sha, object) = self._object_store._get_ie_object_or_sha1(ie, inv)
 
104
            if ie.parent_id is None:
 
105
                tree_sha = sha
 
106
            if not self.need_sha(sha):
 
107
                continue
 
108
            self.queue(sha, object, inv.id2path(ie.file_id), ie, inv)
 
109
            if ie.kind == "directory":
 
110
                todo.extend(ie.children.values())
 
111
        assert tree_sha is not None
 
112
        commit = self._object_store._get_commit(revid, tree_sha)
 
113
        self.queue(commit.id, commit, None)
86
114
        return commit.id
87
115
 
88
116
    def __len__(self):
89
117
        return len(self._pending)
90
118
 
91
119
    def __iter__(self):
92
 
        return iter(self._pending)
 
120
        for i, (object, path) in enumerate(self._pending):
 
121
            if self.pb:
 
122
                self.pb.update("writing pack objects", i, len(self))
 
123
            if isinstance(object, tuple):
 
124
                object = self._object_store._get_ie_object(*object)
 
125
            yield (object, path)   
93
126
 
94
127
 
95
128
class InterToGitRepository(InterRepository):
100
133
    def __init__(self, source, target):
101
134
        super(InterToGitRepository, self).__init__(source, target)
102
135
        self.mapping = self.target.get_mapping()
103
 
        self.source_store = BazaarObjectStore(self.source, self.mapping)
104
136
 
105
137
    @staticmethod
106
138
    def _get_repo_format_to_test():
110
142
        """See InterRepository.copy_content."""
111
143
        self.fetch(revision_id, pb, find_ghosts=False)
112
144
 
113
 
    def dfetch_refs(self, update_refs):
114
 
        """Fetch non-roundtripped revisions into the target repository.
115
 
 
116
 
        :param update_refs: Generate refs to fetch. Receives dictionary
117
 
            with old names to old git shas. Should return a dictionary
118
 
            of new names to Bazaar revision ids.
119
 
        :return: revision id map, old refs dictionary and new refs dictionary
120
 
        """
121
 
        raise NotImplementedError(self.dfetch_refs)
122
 
 
123
 
    def fetch_refs(self, update_refs):
124
 
        """Fetch possibly roundtripped revisions into the target repository.
125
 
 
126
 
        :param update_refs: Generate refs to fetch. Receives dictionary
127
 
            with old refs (git shas), returns dictionary of new names to
128
 
            git shas.
129
 
        :return: old refs, new refs
130
 
        """
131
 
        raise NotImplementedError(self.fetch_refs)
 
145
    def fetch(self, revision_id=None, pb=None, find_ghosts=False, 
 
146
            fetch_spec=None):
 
147
        raise NoPushSupport()
132
148
 
133
149
 
134
150
class InterToLocalGitRepository(InterToGitRepository):
135
 
    """InterBranch implementation between a Bazaar and a Git repository."""
136
 
 
137
 
    def __init__(self, source, target):
138
 
        super(InterToLocalGitRepository, self).__init__(source, target)
139
 
        self.target_store = self.target._git.object_store
140
 
        self.target_refs = self.target._git.refs
141
 
 
142
 
    def _revision_needs_fetching(self, sha_id, revid):
143
 
        if revid == NULL_REVISION:
144
 
            return False
145
 
        if sha_id is None:
146
 
            try:
147
 
                sha_id = self.source_store._lookup_revision_sha1(revid)
148
 
            except KeyError:
149
 
                return False
150
 
        try:
151
 
            return (sha_id not in self.target_store)
152
 
        except errors.NoSuchRevision:
153
 
            # Ghost, can't push
154
 
            return False
155
 
 
156
 
    def missing_revisions(self, stop_revisions):
157
 
        """Find the revisions that are missing from the target repository.
158
 
 
159
 
        :param stop_revisions: Revisions to check for (tuples with
160
 
            Git SHA1, bzr revid)
161
 
        :return: sequence of missing revisions, in topological order
162
 
        :raise: NoSuchRevision if the stop_revisions are not present in
163
 
            the source
164
 
        """
165
 
        revid_sha_map = {}
166
 
        stop_revids = []
167
 
        stop_sha1s = []
168
 
        for (sha1, revid) in stop_revisions:
169
 
            if sha1 is not None and revid is not None:
170
 
                revid_sha_map[revid] = sha1
171
 
            elif sha1 is not None:
172
 
                stop_sha1s.append(sha1)
173
 
            else:
174
 
                assert revid is not None
175
 
                stop_revids.append(revid)
 
151
 
 
152
    def missing_revisions(self, stop_revision):
 
153
        if stop_revision is None:
 
154
            raise NotImplementedError
176
155
        missing = []
177
 
        graph = self.source.get_graph()
178
156
        pb = ui.ui_factory.nested_progress_bar()
179
157
        try:
180
 
            for revid, _ in graph.iter_ancestry(stop_revids):
181
 
                assert type(revid) is str
 
158
            graph = self.source.get_graph()
 
159
            for revid, _ in graph.iter_ancestry([stop_revision]):
182
160
                pb.update("determining revisions to fetch", len(missing))
183
 
                sha1 = revid_sha_map.get(revid)
184
 
                if self._revision_needs_fetching(sha1, revid):
 
161
                if not self.target.has_revision(revid):
185
162
                    missing.append(revid)
 
163
            return graph.iter_topo_order(missing)
186
164
        finally:
187
165
            pb.finished()
188
 
        for sha1 in stop_sha1s:
189
 
            try:
190
 
                (kind, (revid, tree_sha, verifiers)) = self.source_store.lookup_git_sha(sha1)
191
 
            except KeyError:
192
 
                continue
193
 
            else:
194
 
                missing.append(revid)
195
 
                revid_sha_map[revid] = sha1
196
 
        return graph.iter_topo_order(missing)
197
 
 
198
 
    def _get_target_bzr_refs(self):
199
 
        """Return a dictionary with references.
200
 
 
201
 
        :return: Dictionary with reference names as keys and tuples
202
 
            with Git SHA, Bazaar revid as values.
203
 
        """
204
 
        bzr_refs = {}
205
 
        refs = self.target._git.get_refs()
206
 
        for k, v in refs.iteritems():
207
 
            try:
208
 
                (kind, type_data) = self.source_store.lookup_git_sha(v)
209
 
            except KeyError:
210
 
                revid = None
211
 
            else:
212
 
                if kind == "commit":
213
 
                    revid = type_data[0]
214
 
                else:
215
 
                    revid = None
216
 
            bzr_refs[k] = (v, revid)
217
 
        return bzr_refs
218
 
 
219
 
    def fetch_refs(self, update_refs):
220
 
        self.source.lock_read()
221
 
        try:
222
 
            old_refs = self._get_target_bzr_refs()
223
 
            new_refs = update_refs(old_refs)
224
 
            self.fetch(mapped_refs=new_refs.values())
225
 
        finally:
226
 
            self.source.unlock()
227
 
        return old_refs, new_refs
228
 
 
229
 
    def dfetch_refs(self, update_refs):
230
 
        self.source.lock_read()
231
 
        try:
232
 
            old_refs = self._get_target_bzr_refs()
233
 
            new_refs = update_refs(old_refs)
234
 
            revidmap, gitidmap = self.dfetch(new_refs.values())
235
 
            for name, (gitid, revid) in new_refs.iteritems():
236
 
                if gitid is None:
237
 
                    try:
238
 
                        gitid = gitidmap[revid]
239
 
                    except KeyError:
240
 
                        gitid = self.source_store._lookup_revision_sha1(revid)
241
 
                self.target._git.refs[name] = gitid
242
 
                new_refs[name] = (gitid, self.source_store.lookup_git_sha(gitid)[1][0])
243
 
        finally:
244
 
            self.source.unlock()
245
 
        return revidmap, old_refs, new_refs
246
 
 
247
 
    def _get_missing_objects_iterator(self, pb):
248
 
        return MissingObjectsIterator(self.source_store, self.source, pb)
249
 
 
250
 
    def dfetch(self, stop_revisions):
 
166
 
 
167
    def dfetch_refs(self, refs):
 
168
        revidmap = {}
 
169
        new_refs = {}
 
170
        for name, revid in refs.iteritems():
 
171
            newrevidmap, newgitidmap = self.dfetch(revid)
 
172
            revidmap.update(newrevidmap)
 
173
            if revid in newgitidmap:
 
174
                gitid = newgitidmap[revid]
 
175
            else:
 
176
                gitid, _ = self.mapping.revision_id_bzr_to_foreign(revid)
 
177
            self.target._git.set_ref(name, gitid)
 
178
            new_refs[name] = gitid
 
179
        return revidmap, new_refs
 
180
 
 
181
    def dfetch(self, stop_revision=None):
251
182
        """Import the gist of the ancestry of a particular revision."""
252
183
        gitidmap = {}
253
184
        revidmap = {}
254
185
        self.source.lock_read()
255
186
        try:
256
 
            todo = list(self.missing_revisions(stop_revisions))
 
187
            todo = [revid for revid in self.missing_revisions(stop_revision) if revid != NULL_REVISION]
257
188
            pb = ui.ui_factory.nested_progress_bar()
258
189
            try:
259
 
                object_generator = self._get_missing_objects_iterator(pb)
 
190
                object_generator = MissingObjectsIterator(self.source, self.mapping, pb)
260
191
                for old_bzr_revid, git_commit in object_generator.import_revisions(
261
 
                    todo, roundtrip=False):
 
192
                    todo):
262
193
                    new_bzr_revid = self.mapping.revision_id_foreign_to_bzr(git_commit)
263
194
                    revidmap[old_bzr_revid] = new_bzr_revid
264
195
                    gitidmap[old_bzr_revid] = git_commit
265
 
                self.target_store.add_objects(object_generator)
 
196
                self.target._git.object_store.add_objects(object_generator) 
266
197
            finally:
267
198
                pb.finished()
268
199
        finally:
269
200
            self.source.unlock()
270
201
        return revidmap, gitidmap
271
202
 
272
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
273
 
            fetch_spec=None, mapped_refs=None):
274
 
        if mapped_refs is not None:
275
 
            stop_revisions = mapped_refs
276
 
        elif revision_id is not None:
277
 
            stop_revisions = [(None, revision_id)]
278
 
        elif fetch_spec is not None:
279
 
            stop_revisions = [(None, revid) for revid in fetch_spec.heads]
280
 
        else:
281
 
            stop_revisions = [(None, revid) for revid in self.source.all_revision_ids()]
282
 
        self.source.lock_read()
283
 
        try:
284
 
            todo = list(self.missing_revisions(stop_revisions))
285
 
            pb = ui.ui_factory.nested_progress_bar()
286
 
            try:
287
 
                object_generator = self._get_missing_objects_iterator(pb)
288
 
                for (revid, git_sha) in object_generator.import_revisions(
289
 
                    todo, roundtrip=True):
290
 
                    try:
291
 
                        self.mapping.revision_id_bzr_to_foreign(revid)
292
 
                    except errors.InvalidRevisionId:
293
 
                        self.target_refs[self.mapping.revid_as_refname(revid)] = git_sha
294
 
                self.target_store.add_objects(object_generator)
295
 
            finally:
296
 
                pb.finished()
297
 
        finally:
298
 
            self.source.unlock()
299
 
 
300
203
    @staticmethod
301
204
    def is_compatible(source, target):
302
205
        """Be compatible with GitRepository."""
303
 
        return (not isinstance(source, GitRepository) and
 
206
        return (not isinstance(source, GitRepository) and 
304
207
                isinstance(target, LocalGitRepository))
305
208
 
306
209
 
307
210
class InterToRemoteGitRepository(InterToGitRepository):
308
211
 
309
 
    def dfetch_refs(self, update_refs):
 
212
    def dfetch_refs(self, new_refs):
310
213
        """Import the gist of the ancestry of a particular revision."""
311
214
        revidmap = {}
312
 
        def determine_wants(old_refs):
 
215
        def get_changed_refs(refs):
313
216
            ret = {}
314
 
            self.old_refs = old_refs
315
 
            self.new_refs = update_refs(self.old_refs)
316
 
            for name, (gitid, revid) in self.new_refs.iteritems():
317
 
                if gitid is None:
318
 
                    ret[name] = self.source_store._lookup_revision_sha1(revid)
319
 
                else:
320
 
                    ret[name] = gitid
 
217
            for name, revid in new_refs.iteritems():
 
218
                ret[name] = store._lookup_revision_sha1(revid)
321
219
            return ret
322
220
        self.source.lock_read()
323
221
        try:
324
 
            new_refs = self.target.send_pack(determine_wants,
325
 
                    self.source_store.generate_lossy_pack_contents)
 
222
            store = BazaarObjectStore(self.source, self.mapping)
 
223
            def generate_blob_contents(have, want):
 
224
                graphwalker = SimpleFetchGraphWalker(have, store.get_parents)
 
225
                objfinder = MissingObjectFinder(store, want, graphwalker)
 
226
                return store.iter_shas(iter(objfinder.next, None))
 
227
            new_refs = self.target.send_pack(get_changed_refs, generate_blob_contents)
326
228
        finally:
327
229
            self.source.unlock()
328
 
        return revidmap, self.old_refs, self.new_refs
329
 
 
330
 
    def fetch_refs(self, update_refs):
331
 
        raise NoPushSupport()
 
230
        return revidmap, new_refs
332
231
 
333
232
    @staticmethod
334
233
    def is_compatible(source, target):
335
234
        """Be compatible with GitRepository."""
336
 
        return (not isinstance(source, GitRepository) and
 
235
        return (not isinstance(source, GitRepository) and 
337
236
                isinstance(target, RemoteGitRepository))