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