/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

  • Committer: John Arbash Meinel
  • Date: 2010-01-12 22:51:31 UTC
  • mto: This revision was merged to the branch mainline in revision 4955.
  • Revision ID: john@arbash-meinel.com-20100112225131-he8h411p6aeeb947
Delay grabbing an output stream until we actually go to show a diff.

This makes the test suite happy, but it also seems to be reasonable.
If we aren't going to write anything, we don't need to hold an
output stream open.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2009-2010 Jelmer Vernooij <jelmer@samba.org>
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
 
"""Push implementation that simply prints message saying push is not supported."""
18
 
 
19
 
from bzrlib import (
20
 
    errors,
21
 
    ui,
22
 
    )
23
 
from bzrlib.repository import (
24
 
    InterRepository,
25
 
    )
26
 
from bzrlib.revision import (
27
 
    NULL_REVISION,
28
 
    )
29
 
 
30
 
from bzrlib.plugins.git.errors import (
31
 
    NoPushSupport,
32
 
    )
33
 
from bzrlib.plugins.git.object_store import (
34
 
    get_object_store,
35
 
    )
36
 
from bzrlib.plugins.git.repository import (
37
 
    GitRepository,
38
 
    LocalGitRepository,
39
 
    GitRepositoryFormat,
40
 
    )
41
 
from bzrlib.plugins.git.remote import (
42
 
    RemoteGitRepository,
43
 
    )
44
 
from bzrlib.plugins.git.unpeel_map import (
45
 
    UnpeelMap,
46
 
    )
47
 
 
48
 
 
49
 
class MissingObjectsIterator(object):
50
 
    """Iterate over git objects that are missing from a target repository.
51
 
 
52
 
    """
53
 
 
54
 
    def __init__(self, store, source, pb=None):
55
 
        """Create a new missing objects iterator.
56
 
 
57
 
        """
58
 
        self.source = source
59
 
        self._object_store = store
60
 
        self._pending = []
61
 
        self.pb = pb
62
 
 
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
 
        """
69
 
        for i, revid in enumerate(revids):
70
 
            if self.pb:
71
 
                self.pb.update("pushing revisions", i, len(revids))
72
 
            git_commit = self.import_revision(revid, roundtrip)
73
 
            yield (revid, git_commit)
74
 
 
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
80
 
        """
81
 
        tree = self._object_store.tree_cache.revision_tree(revid)
82
 
        rev = self.source.get_revision(revid)
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))
89
 
        return commit.id
90
 
 
91
 
    def __len__(self):
92
 
        return len(self._pending)
93
 
 
94
 
    def __iter__(self):
95
 
        return iter(self._pending)
96
 
 
97
 
 
98
 
class InterToGitRepository(InterRepository):
99
 
    """InterRepository that copies into a Git repository."""
100
 
 
101
 
    _matching_repo_format = GitRepositoryFormat()
102
 
 
103
 
    def __init__(self, source, target):
104
 
        super(InterToGitRepository, self).__init__(source, target)
105
 
        self.mapping = self.target.get_mapping()
106
 
        self.source_store = get_object_store(self.source, self.mapping)
107
 
 
108
 
    @staticmethod
109
 
    def _get_repo_format_to_test():
110
 
        return None
111
 
 
112
 
    def copy_content(self, revision_id=None, pb=None):
113
 
        """See InterRepository.copy_content."""
114
 
        self.fetch(revision_id, pb, find_ghosts=False)
115
 
 
116
 
    def fetch_refs(self, update_refs, lossy):
117
 
        """Fetch possibly roundtripped revisions into the target repository
118
 
        and update refs.
119
 
 
120
 
        :param update_refs: Generate refs to fetch. Receives dictionary
121
 
            with old refs (git shas), returns dictionary of new names to
122
 
            git shas.
123
 
        :param lossy: Whether to roundtrip
124
 
        :return: old refs, new refs
125
 
        """
126
 
        raise NotImplementedError(self.fetch_refs)
127
 
 
128
 
 
129
 
class InterToLocalGitRepository(InterToGitRepository):
130
 
    """InterBranch implementation between a Bazaar and a Git repository."""
131
 
 
132
 
    def __init__(self, source, target):
133
 
        super(InterToLocalGitRepository, self).__init__(source, target)
134
 
        self.target_store = self.target._git.object_store
135
 
        self.target_refs = self.target._git.refs
136
 
 
137
 
    def _revision_needs_fetching(self, sha_id, revid):
138
 
        if revid == NULL_REVISION:
139
 
            return False
140
 
        if sha_id is None:
141
 
            try:
142
 
                sha_id = self.source_store._lookup_revision_sha1(revid)
143
 
            except KeyError:
144
 
                return False
145
 
        try:
146
 
            return (sha_id not in self.target_store)
147
 
        except errors.NoSuchRevision:
148
 
            # Ghost, can't push
149
 
            return False
150
 
 
151
 
    def missing_revisions(self, stop_revisions):
152
 
        """Find the revisions that are missing from the target repository.
153
 
 
154
 
        :param stop_revisions: Revisions to check for (tuples with
155
 
            Git SHA1, bzr revid)
156
 
        :return: sequence of missing revisions, in topological order
157
 
        :raise: NoSuchRevision if the stop_revisions are not present in
158
 
            the source
159
 
        """
160
 
        revid_sha_map = {}
161
 
        stop_revids = []
162
 
        stop_sha1s = []
163
 
        for (sha1, revid) in stop_revisions:
164
 
            if sha1 is not None and revid is not None:
165
 
                revid_sha_map[revid] = sha1
166
 
            elif sha1 is not None:
167
 
                stop_sha1s.append(sha1)
168
 
            else:
169
 
                assert revid is not None
170
 
                stop_revids.append(revid)
171
 
        missing = []
172
 
        graph = self.source.get_graph()
173
 
        pb = ui.ui_factory.nested_progress_bar()
174
 
        try:
175
 
            for revid, _ in graph.iter_ancestry(stop_revids):
176
 
                assert type(revid) is str
177
 
                pb.update("determining revisions to fetch", len(missing))
178
 
                sha1 = revid_sha_map.get(revid)
179
 
                if self._revision_needs_fetching(sha1, revid):
180
 
                    missing.append(revid)
181
 
        finally:
182
 
            pb.finished()
183
 
        for sha1 in stop_sha1s:
184
 
            try:
185
 
                for (kind, (revid, tree_sha, verifiers)) in self.source_store.lookup_git_sha(sha1):
186
 
                    missing.append(revid)
187
 
                    revid_sha_map[revid] = sha1
188
 
            except KeyError:
189
 
                continue
190
 
        return graph.iter_topo_order(missing)
191
 
 
192
 
    def _get_target_bzr_refs(self):
193
 
        """Return a dictionary with references.
194
 
 
195
 
        :return: Dictionary with reference names as keys and tuples
196
 
            with Git SHA, Bazaar revid as values.
197
 
        """
198
 
        bzr_refs = {}
199
 
        refs = {}
200
 
        for k in self.target._git.refs.allkeys():
201
 
            v = self.target._git.refs.read_ref(k)
202
 
            try:
203
 
                for (kind, type_data) in self.source_store.lookup_git_sha(v):
204
 
                    if kind == "commit" and self.source.has_revision(type_data[0]):
205
 
                        revid = type_data[0]
206
 
                        break
207
 
                else:
208
 
                    revid = None
209
 
            except KeyError:
210
 
                revid = None
211
 
            bzr_refs[k] = (v, revid)
212
 
        return bzr_refs
213
 
 
214
 
    def fetch_refs(self, update_refs, lossy):
215
 
        self.source_store.lock_read()
216
 
        try:
217
 
            old_refs = self._get_target_bzr_refs()
218
 
            new_refs = update_refs(old_refs)
219
 
            revidmap = self.fetch_objects(new_refs.values(), roundtrip=not lossy)
220
 
            for name, (gitid, revid) in new_refs.iteritems():
221
 
                if gitid is None:
222
 
                    try:
223
 
                        gitid = revidmap[revid][0]
224
 
                    except KeyError:
225
 
                        gitid = self.source_store._lookup_revision_sha1(revid)
226
 
                assert len(gitid) == 40
227
 
                self.target_refs[name] = gitid
228
 
        finally:
229
 
            self.source_store.unlock()
230
 
        return revidmap, old_refs, new_refs
231
 
 
232
 
    def fetch_objects(self, revs, roundtrip):
233
 
        todo = list(self.missing_revisions(revs))
234
 
        revidmap = {}
235
 
        pb = ui.ui_factory.nested_progress_bar()
236
 
        try:
237
 
            object_generator = self._get_missing_objects_iterator(pb)
238
 
            for (old_revid, git_sha) in object_generator.import_revisions(
239
 
                todo, roundtrip=roundtrip):
240
 
                try:
241
 
                    self.mapping.revision_id_bzr_to_foreign(old_revid)
242
 
                except errors.InvalidRevisionId:
243
 
                    self.target_refs[self.mapping.revid_as_refname(old_revid)] = git_sha
244
 
                if not roundtrip:
245
 
                    new_revid = self.mapping.revision_id_foreign_to_bzr(git_sha)
246
 
                else:
247
 
                    new_revid = old_revid
248
 
                revidmap[old_revid] = (git_sha, new_revid)
249
 
            self.target_store.add_objects(object_generator)
250
 
            return revidmap
251
 
        finally:
252
 
            pb.finished()
253
 
 
254
 
    def _get_missing_objects_iterator(self, pb):
255
 
        return MissingObjectsIterator(self.source_store, self.source, pb)
256
 
 
257
 
    def dfetch(self, stop_revisions):
258
 
        """Import the gist of the ancestry of a particular revision."""
259
 
 
260
 
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
261
 
            fetch_spec=None, mapped_refs=None):
262
 
        if not self.mapping.roundtripping:
263
 
            raise NoPushSupport()
264
 
        self.source_store.lock_read()
265
 
        try:
266
 
            if mapped_refs is not None:
267
 
                stop_revisions = mapped_refs
268
 
            elif revision_id is not None:
269
 
                stop_revisions = [(None, revision_id)]
270
 
            elif fetch_spec is not None:
271
 
                stop_revisions = [(None, revid) for revid in fetch_spec.heads]
272
 
            else:
273
 
                stop_revisions = [(None, revid) for revid in self.source.all_revision_ids()]
274
 
            self.fetch_objects(stop_revisions, roundtrip=True)
275
 
        finally:
276
 
            self.source_store.unlock()
277
 
 
278
 
    @staticmethod
279
 
    def is_compatible(source, target):
280
 
        """Be compatible with GitRepository."""
281
 
        return (not isinstance(source, GitRepository) and
282
 
                isinstance(target, LocalGitRepository))
283
 
 
284
 
 
285
 
class InterToRemoteGitRepository(InterToGitRepository):
286
 
 
287
 
    def fetch_refs(self, update_refs, lossy):
288
 
        """Import the gist of the ancestry of a particular revision."""
289
 
        if not lossy:
290
 
            raise NoPushSupport()
291
 
        unpeel_map = UnpeelMap.from_repository(self.source)
292
 
        revidmap = {}
293
 
        def determine_wants(old_refs):
294
 
            ret = {}
295
 
            self.old_refs = dict([(k, (v, None)) for (k, v) in old_refs.iteritems()])
296
 
            self.new_refs = update_refs(self.old_refs)
297
 
            for name, (gitid, revid) in self.new_refs.iteritems():
298
 
                if gitid is None:
299
 
                    git_sha = self.source_store._lookup_revision_sha1(revid)
300
 
                    ret[name] = unpeel_map.re_unpeel_tag(git_sha, old_refs.get(name))
301
 
                else:
302
 
                    ret[name] = gitid
303
 
            return ret
304
 
        self.source_store.lock_read()
305
 
        try:
306
 
            new_refs = self.target.send_pack(determine_wants,
307
 
                    self.source_store.generate_lossy_pack_contents)
308
 
        finally:
309
 
            self.source_store.unlock()
310
 
        # FIXME: revidmap?
311
 
        return revidmap, self.old_refs, self.new_refs
312
 
 
313
 
    @staticmethod
314
 
    def is_compatible(source, target):
315
 
        """Be compatible with GitRepository."""
316
 
        return (not isinstance(source, GitRepository) and
317
 
                isinstance(target, RemoteGitRepository))