/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

Use dictionary with verifiers rather than requiring testament3-sha1 everywhere.

Show diffs side-by-side

added added

removed removed

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