/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
1
# Copyright (C) 2009-2018 Jelmer Vernooij <jelmer@jelmer.uk>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
"""InterRepository operations."""
18
19
from __future__ import absolute_import
20
21
from io import BytesIO
6989.2.5 by Jelmer Vernooij
More test fixes.
22
import itertools
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
23
24
from dulwich.errors import (
25
    NotCommitError,
26
    )
27
from dulwich.object_store import (
28
    ObjectStoreGraphWalker,
29
    )
30
from dulwich.protocol import (
31
    CAPABILITY_THIN_PACK,
32
    ZERO_SHA,
33
    )
7103.2.1 by Jelmer Vernooij
Don't attempt to retrieve peeled SHAs.
34
from dulwich.refs import (
35
    ANNOTATED_TAG_SUFFIX,
36
    SYMREF,
37
    )
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
38
from dulwich.walk import Walker
39
6986.2.1 by Jelmer Vernooij
Move breezy.plugins.git to breezy.git.
40
from ..errors import (
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
41
    DivergedBranches,
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
42
    FetchLimitUnsupported,
43
    InvalidRevisionId,
44
    LossyPushToSameVCS,
45
    NoRoundtrippingSupport,
46
    NoSuchRevision,
47
    )
6986.2.1 by Jelmer Vernooij
Move breezy.plugins.git to breezy.git.
48
from ..repository import (
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
49
    InterRepository,
50
    )
6986.2.1 by Jelmer Vernooij
Move breezy.plugins.git to breezy.git.
51
from ..revision import (
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
52
    NULL_REVISION,
53
    )
6986.2.3 by Jelmer Vernooij
Merge trunk
54
from ..sixish import (
7018.3.2 by Jelmer Vernooij
Fix some git tests.
55
    viewitems,
6986.2.3 by Jelmer Vernooij
Merge trunk
56
    viewvalues,
7018.3.2 by Jelmer Vernooij
Fix some git tests.
57
    )
6986.2.1 by Jelmer Vernooij
Move breezy.plugins.git to breezy.git.
58
from .. import (
0.418.1 by Jelmer Vernooij
Support suppressing slow intervcs warnings.
59
    config,
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
60
    trace,
61
    ui,
62
    )
63
64
from .errors import (
65
    NoPushSupport,
66
    )
67
from .fetch import (
68
    import_git_objects,
69
    DetermineWantsRecorder,
70
    )
71
from .mapping import (
72
    needs_roundtripping,
73
    )
74
from .object_store import (
75
    get_object_store,
76
    )
77
from .push import (
78
    MissingObjectsIterator,
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
79
    remote_divergence,
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
80
    )
81
from .refs import (
82
    is_tag,
83
    )
84
from .repository import (
85
    GitRepository,
86
    LocalGitRepository,
87
    GitRepositoryFormat,
88
    )
89
from .remote import (
90
    RemoteGitRepository,
91
    )
92
from .unpeel_map import (
93
    UnpeelMap,
94
    )
95
96
97
class InterToGitRepository(InterRepository):
98
    """InterRepository that copies into a Git repository."""
99
100
    _matching_repo_format = GitRepositoryFormat()
101
102
    def __init__(self, source, target):
103
        super(InterToGitRepository, self).__init__(source, target)
104
        self.mapping = self.target.get_mapping()
105
        self.source_store = get_object_store(self.source, self.mapping)
106
107
    @staticmethod
108
    def _get_repo_format_to_test():
109
        return None
110
111
    def copy_content(self, revision_id=None, pb=None):
112
        """See InterRepository.copy_content."""
113
        self.fetch(revision_id, pb, find_ghosts=False)
114
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
115
    def fetch_refs(self, update_refs, lossy, overwrite=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
116
        """Fetch possibly roundtripped revisions into the target repository
117
        and update refs.
118
119
        :param update_refs: Generate refs to fetch. Receives dictionary
120
            with old refs (git shas), returns dictionary of new names to
121
            git shas.
122
        :param lossy: Whether to roundtrip
123
        :return: old refs, new refs
124
        """
125
        raise NotImplementedError(self.fetch_refs)
126
127
    def search_missing_revision_ids(self,
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
128
                                    find_ghosts=True, revision_ids=None,
129
                                    if_present_ids=None, limit=None):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
130
        if limit is not None:
131
            raise FetchLimitUnsupported(self)
132
        git_shas = []
133
        todo = []
134
        if revision_ids:
135
            todo.extend(revision_ids)
136
        if if_present_ids:
137
            todo.extend(revision_ids)
138
        with self.source_store.lock_read():
139
            for revid in revision_ids:
140
                if revid == NULL_REVISION:
141
                    continue
6989.2.3 by Jelmer Vernooij
Allow testing interrepo formats that don't support roundtripping.
142
                try:
143
                    git_sha = self.source_store._lookup_revision_sha1(revid)
144
                except KeyError:
145
                    raise NoSuchRevision(revid, self.source)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
146
                git_shas.append(git_sha)
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
147
            walker = Walker(
148
                self.source_store,
149
                include=git_shas,
150
                exclude=[
151
                    sha for sha in self.target.controldir.get_refs_container().as_dict().values()
152
                    if sha != ZERO_SHA])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
153
            missing_revids = set()
154
            for entry in walker:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
155
                for (kind, type_data) in self.source_store.lookup_git_sha(
156
                        entry.commit.id):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
157
                    if kind == "commit":
158
                        missing_revids.add(type_data[0])
6989.2.3 by Jelmer Vernooij
Allow testing interrepo formats that don't support roundtripping.
159
            return self.source.revision_ids_to_search_result(missing_revids)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
160
161
    def _warn_slow(self):
0.418.1 by Jelmer Vernooij
Support suppressing slow intervcs warnings.
162
        if not config.GlobalConfig().suppress_warning('slow_intervcs_push'):
163
            trace.warning(
164
                'Pushing from a Bazaar to a Git repository. '
165
                'For better performance, push into a Bazaar repository.')
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
166
167
168
class InterToLocalGitRepository(InterToGitRepository):
169
    """InterBranch implementation between a Bazaar and a Git repository."""
170
171
    def __init__(self, source, target):
172
        super(InterToLocalGitRepository, self).__init__(source, target)
173
        self.target_store = self.target.controldir._git.object_store
174
        self.target_refs = self.target.controldir._git.refs
175
176
    def _commit_needs_fetching(self, sha_id):
177
        try:
178
            return (sha_id not in self.target_store)
179
        except NoSuchRevision:
180
            # Ghost, can't push
181
            return False
182
183
    def _revision_needs_fetching(self, sha_id, revid):
184
        if revid == NULL_REVISION:
185
            return False
186
        if sha_id is None:
187
            try:
188
                sha_id = self.source_store._lookup_revision_sha1(revid)
189
            except KeyError:
190
                return False
191
        return self._commit_needs_fetching(sha_id)
192
193
    def missing_revisions(self, stop_revisions):
194
        """Find the revisions that are missing from the target repository.
195
196
        :param stop_revisions: Revisions to check for (tuples with
197
            Git SHA1, bzr revid)
198
        :return: sequence of missing revisions, in topological order
199
        :raise: NoSuchRevision if the stop_revisions are not present in
200
            the source
201
        """
202
        revid_sha_map = {}
203
        stop_revids = []
204
        for (sha1, revid) in stop_revisions:
205
            if sha1 is not None and revid is not None:
206
                revid_sha_map[revid] = sha1
207
                stop_revids.append(revid)
208
            elif sha1 is not None:
209
                if self._commit_needs_fetching(sha1):
210
                    for (kind, (revid, tree_sha, verifiers)) in self.source_store.lookup_git_sha(sha1):
211
                        revid_sha_map[revid] = sha1
212
                        stop_revids.append(revid)
213
            else:
214
                if revid is None:
215
                    raise AssertionError
216
                stop_revids.append(revid)
217
        missing = set()
218
        graph = self.source.get_graph()
219
        pb = ui.ui_factory.nested_progress_bar()
220
        try:
221
            while stop_revids:
222
                new_stop_revids = []
223
                for revid in stop_revids:
224
                    sha1 = revid_sha_map.get(revid)
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
225
                    if (revid not in missing and
7143.15.2 by Jelmer Vernooij
Run autopep8.
226
                            self._revision_needs_fetching(sha1, revid)):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
227
                        missing.add(revid)
228
                        new_stop_revids.append(revid)
229
                stop_revids = set()
230
                parent_map = graph.get_parent_map(new_stop_revids)
7018.3.2 by Jelmer Vernooij
Fix some git tests.
231
                for parent_revids in viewvalues(parent_map):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
232
                    stop_revids.update(parent_revids)
233
                pb.update("determining revisions to fetch", len(missing))
234
        finally:
235
            pb.finished()
236
        return graph.iter_topo_order(missing)
237
238
    def _get_target_bzr_refs(self):
239
        """Return a dictionary with references.
240
241
        :return: Dictionary with reference names as keys and tuples
242
            with Git SHA, Bazaar revid as values.
243
        """
244
        bzr_refs = {}
245
        for k in self.target._git.refs.allkeys():
246
            try:
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
247
                v = self.target._git.refs.read_ref(k)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
248
            except KeyError:
249
                # broken symref?
250
                continue
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
251
            revid = None
252
            if not v.startswith(SYMREF):
253
                try:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
254
                    for (kind, type_data) in self.source_store.lookup_git_sha(
255
                            v):
256
                        if kind == "commit" and self.source.has_revision(
257
                                type_data[0]):
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
258
                            revid = type_data[0]
259
                            break
260
                except KeyError:
261
                    pass
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
262
            bzr_refs[k] = (v, revid)
263
        return bzr_refs
264
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
265
    def fetch_refs(self, update_refs, lossy, overwrite=False):
0.403.1 by Jelmer Vernooij
Properly warn on slow push from bzr->git.
266
        self._warn_slow()
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
267
        with self.source_store.lock_read():
268
            old_refs = self._get_target_bzr_refs()
269
            new_refs = update_refs(old_refs)
270
            revidmap = self.fetch_objects(
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
271
                [(git_sha, bzr_revid)
272
                 for (git_sha, bzr_revid) in new_refs.values()
273
                 if git_sha is None or not git_sha.startswith(SYMREF)],
274
                lossy=lossy)
7018.3.2 by Jelmer Vernooij
Fix some git tests.
275
            for name, (gitid, revid) in viewitems(new_refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
276
                if gitid is None:
277
                    try:
278
                        gitid = revidmap[revid][0]
279
                    except KeyError:
280
                        gitid = self.source_store._lookup_revision_sha1(revid)
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
281
                if gitid.startswith(SYMREF):
7143.15.2 by Jelmer Vernooij
Run autopep8.
282
                    self.target_refs.set_symbolic_ref(
283
                        name, gitid[len(SYMREF):])
6987.1.1 by Jelmer Vernooij
Guard against race conditions.
284
                else:
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
285
                    try:
6988 by Jelmer Vernooij
Merge lp:~jelmer/brz/git-fixes.
286
                        old_git_id = old_refs[name][0]
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
287
                    except KeyError:
288
                        self.target_refs.add_if_new(name, gitid)
289
                    else:
290
                        self.target_refs.set_if_equals(name, old_git_id, gitid)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
291
        return revidmap, old_refs, new_refs
292
293
    def fetch_objects(self, revs, lossy, limit=None):
294
        if not lossy and not self.mapping.roundtripping:
295
            for git_sha, bzr_revid in revs:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
296
                if (bzr_revid is not None and
297
                        needs_roundtripping(self.source, bzr_revid)):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
298
                    raise NoPushSupport(self.source, self.target, self.mapping,
299
                                        bzr_revid)
300
        with self.source_store.lock_read():
301
            todo = list(self.missing_revisions(revs))[:limit]
302
            revidmap = {}
303
            pb = ui.ui_factory.nested_progress_bar()
304
            try:
305
                object_generator = MissingObjectsIterator(
306
                    self.source_store, self.source, pb)
307
                for (old_revid, git_sha) in object_generator.import_revisions(
7143.15.2 by Jelmer Vernooij
Run autopep8.
308
                        todo, lossy=lossy):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
309
                    if lossy:
7143.15.2 by Jelmer Vernooij
Run autopep8.
310
                        new_revid = self.mapping.revision_id_foreign_to_bzr(
311
                            git_sha)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
312
                    else:
313
                        new_revid = old_revid
314
                        try:
315
                            self.mapping.revision_id_bzr_to_foreign(old_revid)
316
                        except InvalidRevisionId:
317
                            refname = self.mapping.revid_as_refname(old_revid)
318
                            self.target_refs[refname] = git_sha
319
                    revidmap[old_revid] = (git_sha, new_revid)
320
                self.target_store.add_objects(object_generator)
321
                return revidmap
322
            finally:
323
                pb.finished()
324
325
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
7143.15.2 by Jelmer Vernooij
Run autopep8.
326
              fetch_spec=None, mapped_refs=None):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
327
        if mapped_refs is not None:
328
            stop_revisions = mapped_refs
329
        elif revision_id is not None:
330
            stop_revisions = [(None, revision_id)]
331
        elif fetch_spec is not None:
332
            recipe = fetch_spec.get_recipe()
333
            if recipe[0] in ("search", "proxy-search"):
334
                stop_revisions = [(None, revid) for revid in recipe[1]]
335
            else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
336
                raise AssertionError(
337
                    "Unsupported search result type %s" % recipe[0])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
338
        else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
339
            stop_revisions = [(None, revid)
340
                              for revid in self.source.all_revision_ids()]
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
341
        self._warn_slow()
342
        try:
343
            self.fetch_objects(stop_revisions, lossy=False)
344
        except NoPushSupport:
345
            raise NoRoundtrippingSupport(self.source, self.target)
346
347
    @staticmethod
348
    def is_compatible(source, target):
349
        """Be compatible with GitRepository."""
350
        return (not isinstance(source, GitRepository) and
351
                isinstance(target, LocalGitRepository))
352
353
354
class InterToRemoteGitRepository(InterToGitRepository):
355
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
356
    def fetch_refs(self, update_refs, lossy, overwrite=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
357
        """Import the gist of the ancestry of a particular revision."""
358
        if not lossy and not self.mapping.roundtripping:
359
            raise NoPushSupport(self.source, self.target, self.mapping)
360
        unpeel_map = UnpeelMap.from_repository(self.source)
361
        revidmap = {}
7143.15.2 by Jelmer Vernooij
Run autopep8.
362
7103.2.1 by Jelmer Vernooij
Don't attempt to retrieve peeled SHAs.
363
        def git_update_refs(old_refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
364
            ret = {}
7143.15.2 by Jelmer Vernooij
Run autopep8.
365
            self.old_refs = dict([(k, (v, None))
366
                                  for (k, v) in viewitems(old_refs)])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
367
            self.new_refs = update_refs(self.old_refs)
7018.3.2 by Jelmer Vernooij
Fix some git tests.
368
            for name, (gitid, revid) in viewitems(self.new_refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
369
                if gitid is None:
370
                    git_sha = self.source_store._lookup_revision_sha1(revid)
7143.15.2 by Jelmer Vernooij
Run autopep8.
371
                    gitid = unpeel_map.re_unpeel_tag(
372
                        git_sha, old_refs.get(name))
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
373
                if not overwrite:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
374
                    if remote_divergence(
375
                            old_refs.get(name), gitid, self.source_store):
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
376
                        raise DivergedBranches(self.source, self.target)
377
                ret[name] = gitid
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
378
            return ret
379
        self._warn_slow()
380
        with self.source_store.lock_read():
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
381
            new_refs = self.target.send_pack(
382
                git_update_refs, self.source_store.generate_lossy_pack_data)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
383
        # FIXME: revidmap?
384
        return revidmap, self.old_refs, self.new_refs
385
386
    @staticmethod
387
    def is_compatible(source, target):
388
        """Be compatible with GitRepository."""
389
        return (not isinstance(source, GitRepository) and
390
                isinstance(target, RemoteGitRepository))
391
392
6989.2.5 by Jelmer Vernooij
More test fixes.
393
class GitSearchResult(object):
394
395
    def __init__(self, start, exclude, keys):
396
        self._start = start
397
        self._exclude = exclude
398
        self._keys = keys
399
400
    def get_keys(self):
401
        return self._keys
402
403
    def get_recipe(self):
404
        return ('search', self._start, self._exclude, len(self._keys))
405
406
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
407
class InterFromGitRepository(InterRepository):
408
409
    _matching_repo_format = GitRepositoryFormat()
410
411
    def _target_has_shas(self, shas):
412
        raise NotImplementedError(self._target_has_shas)
413
414
    def get_determine_wants_heads(self, wants, include_tags=False):
415
        wants = set(wants)
7143.15.2 by Jelmer Vernooij
Run autopep8.
416
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
417
        def determine_wants(refs):
418
            potential = set(wants)
419
            if include_tags:
7018.3.2 by Jelmer Vernooij
Fix some git tests.
420
                for k, unpeeled in viewitems(refs):
7103.2.1 by Jelmer Vernooij
Don't attempt to retrieve peeled SHAs.
421
                    if k.endswith(ANNOTATED_TAG_SUFFIX):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
422
                        continue
423
                    if not is_tag(k):
424
                        continue
425
                    if unpeeled == ZERO_SHA:
426
                        continue
427
                    potential.add(unpeeled)
428
            return list(potential - self._target_has_shas(potential))
429
        return determine_wants
430
431
    def determine_wants_all(self, refs):
432
        raise NotImplementedError(self.determine_wants_all)
433
434
    @staticmethod
435
    def _get_repo_format_to_test():
436
        return None
437
438
    def copy_content(self, revision_id=None):
439
        """See InterRepository.copy_content."""
440
        self.fetch(revision_id, find_ghosts=False)
441
442
    def search_missing_revision_ids(self,
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
443
                                    find_ghosts=True, revision_ids=None,
444
                                    if_present_ids=None, limit=None):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
445
        if limit is not None:
446
            raise FetchLimitUnsupported(self)
6989.2.7 by Jelmer Vernooij
hackishly fix search_missing_revision_ids.
447
        if revision_ids is None and if_present_ids is None:
6989.2.5 by Jelmer Vernooij
More test fixes.
448
            todo = set(self.source.all_revision_ids())
6989.2.7 by Jelmer Vernooij
hackishly fix search_missing_revision_ids.
449
        else:
450
            todo = set()
451
            if revision_ids is not None:
452
                for revid in revision_ids:
453
                    if not self.source.has_revision(revid):
454
                        raise NoSuchRevision(revid, self.source)
455
                todo.update(revision_ids)
456
            if if_present_ids is not None:
457
                todo.update(if_present_ids)
458
        result_set = todo.difference(self.target.all_revision_ids())
459
        result_parents = set(itertools.chain.from_iterable(viewvalues(
460
            self.source.get_graph().get_parent_map(result_set))))
461
        included_keys = result_set.intersection(result_parents)
462
        start_keys = result_set.difference(included_keys)
463
        exclude_keys = result_parents.difference(result_set)
464
        return GitSearchResult(start_keys, exclude_keys, result_set)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
465
466
467
class InterGitNonGitRepository(InterFromGitRepository):
468
    """Base InterRepository that copies revisions from a Git into a non-Git
469
    repository."""
470
471
    def _target_has_shas(self, shas):
472
        revids = {}
473
        for sha in shas:
474
            try:
475
                revid = self.source.lookup_foreign_revision_id(sha)
476
            except NotCommitError:
477
                # Commit is definitely not present
478
                continue
479
            else:
480
                revids[revid] = sha
481
        return set([revids[r] for r in self.target.has_revisions(revids)])
482
483
    def determine_wants_all(self, refs):
484
        potential = set()
7018.3.2 by Jelmer Vernooij
Fix some git tests.
485
        for k, v in viewitems(refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
486
            # For non-git target repositories, only worry about peeled
487
            if v == ZERO_SHA:
488
                continue
489
            potential.add(self.source.controldir.get_peeled(k) or v)
490
        return list(potential - self._target_has_shas(potential))
491
492
    def get_determine_wants_heads(self, wants, include_tags=False):
493
        wants = set(wants)
7143.15.2 by Jelmer Vernooij
Run autopep8.
494
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
495
        def determine_wants(refs):
496
            potential = set(wants)
497
            if include_tags:
7018.3.2 by Jelmer Vernooij
Fix some git tests.
498
                for k, unpeeled in viewitems(refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
499
                    if not is_tag(k):
500
                        continue
501
                    if unpeeled == ZERO_SHA:
502
                        continue
7143.15.2 by Jelmer Vernooij
Run autopep8.
503
                    potential.add(
504
                        self.source.controldir.get_peeled(k) or unpeeled)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
505
            return list(potential - self._target_has_shas(potential))
506
        return determine_wants
507
508
    def _warn_slow(self):
0.418.1 by Jelmer Vernooij
Support suppressing slow intervcs warnings.
509
        if not config.GlobalConfig().suppress_warning('slow_intervcs_push'):
510
            trace.warning(
511
                'Fetching from Git to Bazaar repository. '
512
                'For better performance, fetch into a Git repository.')
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
513
514
    def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
515
        """Fetch objects from a remote server.
516
517
        :param determine_wants: determine_wants callback
518
        :param mapping: BzrGitMapping to use
519
        :param limit: Maximum number of commits to import.
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
520
        :return: Tuple with pack hint, last imported revision id and remote
521
            refs
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
522
        """
523
        raise NotImplementedError(self.fetch_objects)
524
525
    def get_determine_wants_revids(self, revids, include_tags=False):
526
        wants = set()
527
        for revid in set(revids):
528
            if self.target.has_revision(revid):
529
                continue
530
            git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
531
            wants.add(git_sha)
532
        return self.get_determine_wants_heads(wants, include_tags=include_tags)
533
534
    def fetch(self, revision_id=None, find_ghosts=False,
535
              mapping=None, fetch_spec=None, include_tags=False):
536
        if mapping is None:
537
            mapping = self.source.get_mapping()
538
        if revision_id is not None:
539
            interesting_heads = [revision_id]
540
        elif fetch_spec is not None:
541
            recipe = fetch_spec.get_recipe()
542
            if recipe[0] in ("search", "proxy-search"):
543
                interesting_heads = recipe[1]
544
            else:
545
                raise AssertionError("Unsupported search result type %s" %
7143.15.2 by Jelmer Vernooij
Run autopep8.
546
                                     recipe[0])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
547
        else:
548
            interesting_heads = None
549
550
        if interesting_heads is not None:
551
            determine_wants = self.get_determine_wants_revids(
552
                interesting_heads, include_tags=include_tags)
553
        else:
554
            determine_wants = self.determine_wants_all
555
556
        (pack_hint, _, remote_refs) = self.fetch_objects(determine_wants,
7143.15.2 by Jelmer Vernooij
Run autopep8.
557
                                                         mapping)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
558
        if pack_hint is not None and self.target._format.pack_compresses:
559
            self.target.pack(hint=pack_hint)
560
        return remote_refs
561
562
563
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
564
    """InterRepository that copies revisions from a remote Git into a non-Git
565
    repository."""
566
567
    def get_target_heads(self):
568
        # FIXME: This should be more efficient
569
        all_revs = self.target.all_revision_ids()
570
        parent_map = self.target.get_parent_map(all_revs)
571
        all_parents = set()
7045.4.5 by Jelmer Vernooij
Don't use map as a way to avoid loops.
572
        for values in viewvalues(parent_map):
573
            all_parents.update(values)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
574
        return set(all_revs) - all_parents
575
576
    def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
577
        """See `InterGitNonGitRepository`."""
578
        self._warn_slow()
579
        store = get_object_store(self.target, mapping)
580
        with store.lock_write():
581
            heads = self.get_target_heads()
582
            graph_walker = ObjectStoreGraphWalker(
583
                [store._lookup_revision_sha1(head) for head in heads],
584
                lambda sha: store[sha].parents)
585
            wants_recorder = DetermineWantsRecorder(determine_wants)
586
587
            pb = ui.ui_factory.nested_progress_bar()
588
            try:
589
                objects_iter = self.source.fetch_objects(
0.405.1 by Jelmer Vernooij
Use same logic for interpreting progress reports everywhere.
590
                    wants_recorder, graph_walker, store.get_raw)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
591
                trace.mutter("Importing %d new revisions",
592
                             len(wants_recorder.wants))
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
593
                (pack_hint, last_rev) = import_git_objects(
594
                    self.target, mapping, objects_iter, store,
595
                    wants_recorder.wants, pb, limit)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
596
                return (pack_hint, last_rev, wants_recorder.remote_refs)
597
            finally:
598
                pb.finished()
599
600
    @staticmethod
601
    def is_compatible(source, target):
602
        """Be compatible with GitRepository."""
603
        if not isinstance(source, RemoteGitRepository):
604
            return False
605
        if not target.supports_rich_root():
606
            return False
607
        if isinstance(target, GitRepository):
608
            return False
609
        if not getattr(target._format, "supports_full_versioned_files", True):
610
            return False
611
        return True
612
613
614
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
615
    """InterRepository that copies revisions from a local Git into a non-Git
616
    repository."""
617
618
    def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
619
        """See `InterGitNonGitRepository`."""
620
        self._warn_slow()
621
        remote_refs = self.source.controldir.get_refs_container().as_dict()
622
        wants = determine_wants(remote_refs)
623
        pb = ui.ui_factory.nested_progress_bar()
624
        target_git_object_retriever = get_object_store(self.target, mapping)
625
        try:
626
            target_git_object_retriever.lock_write()
627
            try:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
628
                (pack_hint, last_rev) = import_git_objects(
629
                    self.target, mapping, self.source._git.object_store,
630
                    target_git_object_retriever, wants, pb, limit)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
631
                return (pack_hint, last_rev, remote_refs)
632
            finally:
633
                target_git_object_retriever.unlock()
634
        finally:
635
            pb.finished()
636
637
    @staticmethod
638
    def is_compatible(source, target):
639
        """Be compatible with GitRepository."""
640
        if not isinstance(source, LocalGitRepository):
641
            return False
642
        if not target.supports_rich_root():
643
            return False
644
        if isinstance(target, GitRepository):
645
            return False
646
        if not getattr(target._format, "supports_full_versioned_files", True):
647
            return False
648
        return True
649
650
651
class InterGitGitRepository(InterFromGitRepository):
652
    """InterRepository that copies between Git repositories."""
653
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
654
    def fetch_refs(self, update_refs, lossy, overwrite=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
655
        if lossy:
656
            raise LossyPushToSameVCS(self.source, self.target)
657
        old_refs = self.target.controldir.get_refs_container()
658
        ref_changes = {}
7143.15.2 by Jelmer Vernooij
Run autopep8.
659
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
660
        def determine_wants(heads):
7143.15.2 by Jelmer Vernooij
Run autopep8.
661
            old_refs = dict([(k, (v, None))
662
                             for (k, v) in viewitems(heads.as_dict())])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
663
            new_refs = update_refs(old_refs)
664
            ref_changes.update(new_refs)
7018.3.2 by Jelmer Vernooij
Fix some git tests.
665
            return [sha1 for (sha1, bzr_revid) in viewvalues(new_refs)]
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
666
        self.fetch_objects(determine_wants, lossy=lossy)
7018.3.2 by Jelmer Vernooij
Fix some git tests.
667
        for k, (git_sha, bzr_revid) in viewitems(ref_changes):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
668
            self.target._git.refs[k] = git_sha
669
        new_refs = self.target.controldir.get_refs_container()
670
        return None, old_refs, new_refs
671
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
672
    def fetch_objects(self, determine_wants, mapping=None, limit=None,
673
                      lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
674
        raise NotImplementedError(self.fetch_objects)
675
676
    def _target_has_shas(self, shas):
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
677
        return set(
678
            [sha for sha in shas if sha in self.target._git.object_store])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
679
680
    def fetch(self, revision_id=None, find_ghosts=False,
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
681
              mapping=None, fetch_spec=None, branches=None, limit=None,
682
              include_tags=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
683
        if mapping is None:
684
            mapping = self.source.get_mapping()
685
        if revision_id is not None:
686
            args = [revision_id]
687
        elif fetch_spec is not None:
688
            recipe = fetch_spec.get_recipe()
689
            if recipe[0] in ("search", "proxy-search"):
690
                heads = recipe[1]
691
            else:
692
                raise AssertionError(
693
                    "Unsupported search result type %s" % recipe[0])
694
            args = heads
695
        if branches is not None:
696
            def determine_wants(refs):
697
                ret = []
7018.3.2 by Jelmer Vernooij
Fix some git tests.
698
                for name, value in viewitems(refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
699
                    if value == ZERO_SHA:
700
                        continue
701
702
                    if name in branches or (include_tags and is_tag(name)):
703
                        ret.append(value)
704
                return ret
705
        elif fetch_spec is None and revision_id is None:
706
            determine_wants = self.determine_wants_all
707
        else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
708
            determine_wants = self.get_determine_wants_revids(
709
                args, include_tags=include_tags)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
710
        wants_recorder = DetermineWantsRecorder(determine_wants)
711
        self.fetch_objects(wants_recorder, mapping, limit=limit)
712
        return wants_recorder.remote_refs
713
714
    def get_determine_wants_revids(self, revids, include_tags=False):
715
        wants = set()
716
        for revid in set(revids):
717
            if revid == NULL_REVISION:
718
                continue
719
            git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
720
            wants.add(git_sha)
721
        return self.get_determine_wants_heads(wants, include_tags=include_tags)
722
723
    def determine_wants_all(self, refs):
7103.2.1 by Jelmer Vernooij
Don't attempt to retrieve peeled SHAs.
724
        potential = set([
725
            v for k, v in refs.items()
726
            if not v == ZERO_SHA and not k.endswith(ANNOTATED_TAG_SUFFIX)])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
727
        return list(potential - self._target_has_shas(potential))
728
729
730
class InterLocalGitLocalGitRepository(InterGitGitRepository):
731
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
732
    def fetch_objects(self, determine_wants, mapping=None, limit=None,
733
                      lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
734
        if lossy:
735
            raise LossyPushToSameVCS(self.source, self.target)
736
        if limit is not None:
737
            raise FetchLimitUnsupported(self)
7131.6.1 by Jelmer Vernooij
Add progress bar for local git->git fetches.
738
        from .remote import DefaultProgressReporter
739
        pb = ui.ui_factory.nested_progress_bar()
740
        progress = DefaultProgressReporter(pb).progress
741
        try:
742
            refs = self.source._git.fetch(
7143.15.2 by Jelmer Vernooij
Run autopep8.
743
                self.target._git, determine_wants,
744
                progress=progress)
7131.6.1 by Jelmer Vernooij
Add progress bar for local git->git fetches.
745
        finally:
746
            pb.finished()
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
747
        return (None, None, refs)
748
749
    @staticmethod
750
    def is_compatible(source, target):
751
        """Be compatible with GitRepository."""
752
        return (isinstance(source, LocalGitRepository) and
753
                isinstance(target, LocalGitRepository))
754
755
756
class InterRemoteGitLocalGitRepository(InterGitGitRepository):
757
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
758
    def fetch_objects(self, determine_wants, mapping=None, limit=None,
759
                      lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
760
        if lossy:
761
            raise LossyPushToSameVCS(self.source, self.target)
762
        if limit is not None:
763
            raise FetchLimitUnsupported(self)
764
        graphwalker = self.target._git.get_graph_walker()
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
765
        if (CAPABILITY_THIN_PACK in
766
                self.source.controldir._client._fetch_capabilities):
0.405.1 by Jelmer Vernooij
Use same logic for interpreting progress reports everywhere.
767
            # TODO(jelmer): Avoid reading entire file into memory and
768
            # only processing it after the whole file has been fetched.
769
            f = BytesIO()
770
771
            def commit():
772
                if f.tell():
773
                    f.seek(0)
774
                    self.target._git.object_store.move_in_thin_pack(f)
775
776
            def abort():
777
                pass
778
        else:
779
            f, commit, abort = self.target._git.object_store.add_pack()
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
780
        try:
0.405.1 by Jelmer Vernooij
Use same logic for interpreting progress reports everywhere.
781
            refs = self.source.controldir.fetch_pack(
782
                determine_wants, graphwalker, f.write)
783
            commit()
784
            return (None, None, refs)
785
        except BaseException:
786
            abort()
787
            raise
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
788
789
    @staticmethod
790
    def is_compatible(source, target):
791
        """Be compatible with GitRepository."""
792
        return (isinstance(source, RemoteGitRepository) and
793
                isinstance(target, LocalGitRepository))