/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

BetterĀ __repr__.

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