/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()
7143.22.1 by Jelmer Vernooij
use context on progress bars.
219
        with ui.ui_factory.nested_progress_bar() as pb:
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
220
            while stop_revids:
221
                new_stop_revids = []
222
                for revid in stop_revids:
223
                    sha1 = revid_sha_map.get(revid)
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
224
                    if (revid not in missing and
7143.15.2 by Jelmer Vernooij
Run autopep8.
225
                            self._revision_needs_fetching(sha1, revid)):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
226
                        missing.add(revid)
227
                        new_stop_revids.append(revid)
228
                stop_revids = set()
229
                parent_map = graph.get_parent_map(new_stop_revids)
7018.3.2 by Jelmer Vernooij
Fix some git tests.
230
                for parent_revids in viewvalues(parent_map):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
231
                    stop_revids.update(parent_revids)
232
                pb.update("determining revisions to fetch", len(missing))
233
        return graph.iter_topo_order(missing)
234
235
    def _get_target_bzr_refs(self):
236
        """Return a dictionary with references.
237
238
        :return: Dictionary with reference names as keys and tuples
239
            with Git SHA, Bazaar revid as values.
240
        """
241
        bzr_refs = {}
242
        for k in self.target._git.refs.allkeys():
243
            try:
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
244
                v = self.target._git.refs.read_ref(k)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
245
            except KeyError:
246
                # broken symref?
247
                continue
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
248
            revid = None
249
            if not v.startswith(SYMREF):
250
                try:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
251
                    for (kind, type_data) in self.source_store.lookup_git_sha(
252
                            v):
253
                        if kind == "commit" and self.source.has_revision(
254
                                type_data[0]):
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
255
                            revid = type_data[0]
256
                            break
257
                except KeyError:
258
                    pass
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
259
            bzr_refs[k] = (v, revid)
260
        return bzr_refs
261
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
262
    def fetch_refs(self, update_refs, lossy, overwrite=False):
0.403.1 by Jelmer Vernooij
Properly warn on slow push from bzr->git.
263
        self._warn_slow()
7131.14.2 by Jelmer Vernooij
Make sure resulting branch is correct too.
264
        result_refs = {}
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
265
        with self.source_store.lock_read():
266
            old_refs = self._get_target_bzr_refs()
267
            new_refs = update_refs(old_refs)
268
            revidmap = self.fetch_objects(
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
269
                [(git_sha, bzr_revid)
270
                 for (git_sha, bzr_revid) in new_refs.values()
271
                 if git_sha is None or not git_sha.startswith(SYMREF)],
272
                lossy=lossy)
7018.3.2 by Jelmer Vernooij
Fix some git tests.
273
            for name, (gitid, revid) in viewitems(new_refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
274
                if gitid is None:
275
                    try:
276
                        gitid = revidmap[revid][0]
277
                    except KeyError:
278
                        gitid = self.source_store._lookup_revision_sha1(revid)
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
279
                if gitid.startswith(SYMREF):
7143.15.2 by Jelmer Vernooij
Run autopep8.
280
                    self.target_refs.set_symbolic_ref(
281
                        name, gitid[len(SYMREF):])
6987.1.1 by Jelmer Vernooij
Guard against race conditions.
282
                else:
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
283
                    try:
6988 by Jelmer Vernooij
Merge lp:~jelmer/brz/git-fixes.
284
                        old_git_id = old_refs[name][0]
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
285
                    except KeyError:
286
                        self.target_refs.add_if_new(name, gitid)
287
                    else:
288
                        self.target_refs.set_if_equals(name, old_git_id, gitid)
7131.14.2 by Jelmer Vernooij
Make sure resulting branch is correct too.
289
                    result_refs[name] = (gitid, revid if not lossy else self.mapping.revision_id_foreign_to_bzr(gitid))
290
        return revidmap, old_refs, result_refs
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
291
292
    def fetch_objects(self, revs, lossy, limit=None):
293
        if not lossy and not self.mapping.roundtripping:
294
            for git_sha, bzr_revid in revs:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
295
                if (bzr_revid is not None and
296
                        needs_roundtripping(self.source, bzr_revid)):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
297
                    raise NoPushSupport(self.source, self.target, self.mapping,
298
                                        bzr_revid)
299
        with self.source_store.lock_read():
300
            todo = list(self.missing_revisions(revs))[:limit]
301
            revidmap = {}
7143.22.1 by Jelmer Vernooij
use context on progress bars.
302
            with ui.ui_factory.nested_progress_bar() as pb:
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
303
                object_generator = MissingObjectsIterator(
304
                    self.source_store, self.source, pb)
305
                for (old_revid, git_sha) in object_generator.import_revisions(
7143.15.2 by Jelmer Vernooij
Run autopep8.
306
                        todo, lossy=lossy):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
307
                    if lossy:
7143.15.2 by Jelmer Vernooij
Run autopep8.
308
                        new_revid = self.mapping.revision_id_foreign_to_bzr(
309
                            git_sha)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
310
                    else:
311
                        new_revid = old_revid
312
                        try:
313
                            self.mapping.revision_id_bzr_to_foreign(old_revid)
314
                        except InvalidRevisionId:
315
                            refname = self.mapping.revid_as_refname(old_revid)
316
                            self.target_refs[refname] = git_sha
317
                    revidmap[old_revid] = (git_sha, new_revid)
318
                self.target_store.add_objects(object_generator)
319
                return revidmap
320
321
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
7143.15.2 by Jelmer Vernooij
Run autopep8.
322
              fetch_spec=None, mapped_refs=None):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
323
        if mapped_refs is not None:
324
            stop_revisions = mapped_refs
325
        elif revision_id is not None:
326
            stop_revisions = [(None, revision_id)]
327
        elif fetch_spec is not None:
328
            recipe = fetch_spec.get_recipe()
329
            if recipe[0] in ("search", "proxy-search"):
330
                stop_revisions = [(None, revid) for revid in recipe[1]]
331
            else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
332
                raise AssertionError(
333
                    "Unsupported search result type %s" % recipe[0])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
334
        else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
335
            stop_revisions = [(None, revid)
336
                              for revid in self.source.all_revision_ids()]
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
337
        self._warn_slow()
338
        try:
339
            self.fetch_objects(stop_revisions, lossy=False)
340
        except NoPushSupport:
341
            raise NoRoundtrippingSupport(self.source, self.target)
342
343
    @staticmethod
344
    def is_compatible(source, target):
345
        """Be compatible with GitRepository."""
346
        return (not isinstance(source, GitRepository) and
347
                isinstance(target, LocalGitRepository))
348
349
350
class InterToRemoteGitRepository(InterToGitRepository):
351
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
352
    def fetch_refs(self, update_refs, lossy, overwrite=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
353
        """Import the gist of the ancestry of a particular revision."""
354
        if not lossy and not self.mapping.roundtripping:
355
            raise NoPushSupport(self.source, self.target, self.mapping)
356
        unpeel_map = UnpeelMap.from_repository(self.source)
357
        revidmap = {}
7143.15.2 by Jelmer Vernooij
Run autopep8.
358
7103.2.1 by Jelmer Vernooij
Don't attempt to retrieve peeled SHAs.
359
        def git_update_refs(old_refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
360
            ret = {}
7131.14.3 by Jelmer Vernooij
Merge trunk.
361
            self.old_refs = {
362
                k: (v, None) for (k, v) in viewitems(old_refs)}
7131.14.2 by Jelmer Vernooij
Make sure resulting branch is correct too.
363
            new_refs = update_refs(self.old_refs)
364
            for name, (gitid, revid) in viewitems(new_refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
365
                if gitid is None:
366
                    git_sha = self.source_store._lookup_revision_sha1(revid)
7143.15.2 by Jelmer Vernooij
Run autopep8.
367
                    gitid = unpeel_map.re_unpeel_tag(
368
                        git_sha, old_refs.get(name))
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
369
                if not overwrite:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
370
                    if remote_divergence(
371
                            old_refs.get(name), gitid, self.source_store):
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
372
                        raise DivergedBranches(self.source, self.target)
373
                ret[name] = gitid
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
374
            return ret
375
        self._warn_slow()
376
        with self.source_store.lock_read():
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
377
            new_refs = self.target.send_pack(
378
                git_update_refs, self.source_store.generate_lossy_pack_data)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
379
        # FIXME: revidmap?
7131.14.2 by Jelmer Vernooij
Make sure resulting branch is correct too.
380
        return revidmap, self.old_refs, new_refs
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
381
382
    @staticmethod
383
    def is_compatible(source, target):
384
        """Be compatible with GitRepository."""
385
        return (not isinstance(source, GitRepository) and
386
                isinstance(target, RemoteGitRepository))
387
388
6989.2.5 by Jelmer Vernooij
More test fixes.
389
class GitSearchResult(object):
390
391
    def __init__(self, start, exclude, keys):
392
        self._start = start
393
        self._exclude = exclude
394
        self._keys = keys
395
396
    def get_keys(self):
397
        return self._keys
398
399
    def get_recipe(self):
400
        return ('search', self._start, self._exclude, len(self._keys))
401
402
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
403
class InterFromGitRepository(InterRepository):
404
405
    _matching_repo_format = GitRepositoryFormat()
406
407
    def _target_has_shas(self, shas):
408
        raise NotImplementedError(self._target_has_shas)
409
410
    def get_determine_wants_heads(self, wants, include_tags=False):
411
        wants = set(wants)
7143.15.2 by Jelmer Vernooij
Run autopep8.
412
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
413
        def determine_wants(refs):
7143.12.1 by Jelmer Vernooij
Support cloning revisions referenced only by an annotated tag.
414
            unpeel_lookup = {}
415
            for k, v in viewitems(refs):
416
                if k.endswith(ANNOTATED_TAG_SUFFIX):
417
                    unpeel_lookup[v] = refs[k[:-len(ANNOTATED_TAG_SUFFIX)]]
418
            potential = set([unpeel_lookup.get(w, w) for w in wants])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
419
            if include_tags:
7143.12.1 by Jelmer Vernooij
Support cloning revisions referenced only by an annotated tag.
420
                for k, sha 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
7143.12.1 by Jelmer Vernooij
Support cloning revisions referenced only by an annotated tag.
425
                    if sha == ZERO_SHA:
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
426
                        continue
7143.12.1 by Jelmer Vernooij
Support cloning revisions referenced only by an annotated tag.
427
                    potential.add(sha)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
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 _warn_slow(self):
0.418.1 by Jelmer Vernooij
Support suppressing slow intervcs warnings.
493
        if not config.GlobalConfig().suppress_warning('slow_intervcs_push'):
494
            trace.warning(
495
                'Fetching from Git to Bazaar repository. '
496
                'For better performance, fetch into a Git repository.')
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
497
498
    def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
499
        """Fetch objects from a remote server.
500
501
        :param determine_wants: determine_wants callback
502
        :param mapping: BzrGitMapping to use
503
        :param limit: Maximum number of commits to import.
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
504
        :return: Tuple with pack hint, last imported revision id and remote
505
            refs
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
506
        """
507
        raise NotImplementedError(self.fetch_objects)
508
509
    def get_determine_wants_revids(self, revids, include_tags=False):
510
        wants = set()
511
        for revid in set(revids):
512
            if self.target.has_revision(revid):
513
                continue
514
            git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
515
            wants.add(git_sha)
516
        return self.get_determine_wants_heads(wants, include_tags=include_tags)
517
518
    def fetch(self, revision_id=None, find_ghosts=False,
519
              mapping=None, fetch_spec=None, include_tags=False):
520
        if mapping is None:
521
            mapping = self.source.get_mapping()
522
        if revision_id is not None:
523
            interesting_heads = [revision_id]
524
        elif fetch_spec is not None:
525
            recipe = fetch_spec.get_recipe()
526
            if recipe[0] in ("search", "proxy-search"):
527
                interesting_heads = recipe[1]
528
            else:
529
                raise AssertionError("Unsupported search result type %s" %
7143.15.2 by Jelmer Vernooij
Run autopep8.
530
                                     recipe[0])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
531
        else:
532
            interesting_heads = None
533
534
        if interesting_heads is not None:
535
            determine_wants = self.get_determine_wants_revids(
536
                interesting_heads, include_tags=include_tags)
537
        else:
538
            determine_wants = self.determine_wants_all
539
540
        (pack_hint, _, remote_refs) = self.fetch_objects(determine_wants,
7143.15.2 by Jelmer Vernooij
Run autopep8.
541
                                                         mapping)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
542
        if pack_hint is not None and self.target._format.pack_compresses:
543
            self.target.pack(hint=pack_hint)
544
        return remote_refs
545
546
547
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
548
    """InterRepository that copies revisions from a remote Git into a non-Git
549
    repository."""
550
551
    def get_target_heads(self):
552
        # FIXME: This should be more efficient
553
        all_revs = self.target.all_revision_ids()
554
        parent_map = self.target.get_parent_map(all_revs)
555
        all_parents = set()
7045.4.5 by Jelmer Vernooij
Don't use map as a way to avoid loops.
556
        for values in viewvalues(parent_map):
557
            all_parents.update(values)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
558
        return set(all_revs) - all_parents
559
560
    def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
561
        """See `InterGitNonGitRepository`."""
562
        self._warn_slow()
563
        store = get_object_store(self.target, mapping)
564
        with store.lock_write():
565
            heads = self.get_target_heads()
566
            graph_walker = ObjectStoreGraphWalker(
567
                [store._lookup_revision_sha1(head) for head in heads],
568
                lambda sha: store[sha].parents)
569
            wants_recorder = DetermineWantsRecorder(determine_wants)
570
7143.22.1 by Jelmer Vernooij
use context on progress bars.
571
            with ui.ui_factory.nested_progress_bar() as pb:
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
572
                objects_iter = self.source.fetch_objects(
0.405.1 by Jelmer Vernooij
Use same logic for interpreting progress reports everywhere.
573
                    wants_recorder, graph_walker, store.get_raw)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
574
                trace.mutter("Importing %d new revisions",
575
                             len(wants_recorder.wants))
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
576
                (pack_hint, last_rev) = import_git_objects(
577
                    self.target, mapping, objects_iter, store,
578
                    wants_recorder.wants, pb, limit)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
579
                return (pack_hint, last_rev, wants_recorder.remote_refs)
580
581
    @staticmethod
582
    def is_compatible(source, target):
583
        """Be compatible with GitRepository."""
584
        if not isinstance(source, RemoteGitRepository):
585
            return False
586
        if not target.supports_rich_root():
587
            return False
588
        if isinstance(target, GitRepository):
589
            return False
590
        if not getattr(target._format, "supports_full_versioned_files", True):
591
            return False
592
        return True
593
594
595
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
596
    """InterRepository that copies revisions from a local Git into a non-Git
597
    repository."""
598
599
    def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
600
        """See `InterGitNonGitRepository`."""
601
        self._warn_slow()
602
        remote_refs = self.source.controldir.get_refs_container().as_dict()
603
        wants = determine_wants(remote_refs)
604
        target_git_object_retriever = get_object_store(self.target, mapping)
7143.22.1 by Jelmer Vernooij
use context on progress bars.
605
        with ui.ui_factory.nested_progress_bar() as pb:
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
606
            target_git_object_retriever.lock_write()
607
            try:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
608
                (pack_hint, last_rev) = import_git_objects(
609
                    self.target, mapping, self.source._git.object_store,
610
                    target_git_object_retriever, wants, pb, limit)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
611
                return (pack_hint, last_rev, remote_refs)
612
            finally:
613
                target_git_object_retriever.unlock()
614
615
    @staticmethod
616
    def is_compatible(source, target):
617
        """Be compatible with GitRepository."""
618
        if not isinstance(source, LocalGitRepository):
619
            return False
620
        if not target.supports_rich_root():
621
            return False
622
        if isinstance(target, GitRepository):
623
            return False
624
        if not getattr(target._format, "supports_full_versioned_files", True):
625
            return False
626
        return True
627
628
629
class InterGitGitRepository(InterFromGitRepository):
630
    """InterRepository that copies between Git repositories."""
631
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
632
    def fetch_refs(self, update_refs, lossy, overwrite=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
633
        if lossy:
634
            raise LossyPushToSameVCS(self.source, self.target)
635
        old_refs = self.target.controldir.get_refs_container()
636
        ref_changes = {}
7143.15.2 by Jelmer Vernooij
Run autopep8.
637
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
638
        def determine_wants(heads):
7143.15.2 by Jelmer Vernooij
Run autopep8.
639
            old_refs = dict([(k, (v, None))
640
                             for (k, v) in viewitems(heads.as_dict())])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
641
            new_refs = update_refs(old_refs)
642
            ref_changes.update(new_refs)
7018.3.2 by Jelmer Vernooij
Fix some git tests.
643
            return [sha1 for (sha1, bzr_revid) in viewvalues(new_refs)]
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
644
        self.fetch_objects(determine_wants, lossy=lossy)
7018.3.2 by Jelmer Vernooij
Fix some git tests.
645
        for k, (git_sha, bzr_revid) in viewitems(ref_changes):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
646
            self.target._git.refs[k] = git_sha
647
        new_refs = self.target.controldir.get_refs_container()
648
        return None, old_refs, new_refs
649
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
650
    def fetch_objects(self, determine_wants, mapping=None, limit=None,
651
                      lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
652
        raise NotImplementedError(self.fetch_objects)
653
654
    def _target_has_shas(self, shas):
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
655
        return set(
656
            [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.
657
658
    def fetch(self, revision_id=None, find_ghosts=False,
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
659
              mapping=None, fetch_spec=None, branches=None, limit=None,
660
              include_tags=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
661
        if mapping is None:
662
            mapping = self.source.get_mapping()
663
        if revision_id is not None:
664
            args = [revision_id]
665
        elif fetch_spec is not None:
666
            recipe = fetch_spec.get_recipe()
667
            if recipe[0] in ("search", "proxy-search"):
668
                heads = recipe[1]
669
            else:
670
                raise AssertionError(
671
                    "Unsupported search result type %s" % recipe[0])
672
            args = heads
673
        if branches is not None:
674
            def determine_wants(refs):
675
                ret = []
7018.3.2 by Jelmer Vernooij
Fix some git tests.
676
                for name, value in viewitems(refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
677
                    if value == ZERO_SHA:
678
                        continue
679
680
                    if name in branches or (include_tags and is_tag(name)):
681
                        ret.append(value)
682
                return ret
683
        elif fetch_spec is None and revision_id is None:
684
            determine_wants = self.determine_wants_all
685
        else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
686
            determine_wants = self.get_determine_wants_revids(
687
                args, include_tags=include_tags)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
688
        wants_recorder = DetermineWantsRecorder(determine_wants)
689
        self.fetch_objects(wants_recorder, mapping, limit=limit)
690
        return wants_recorder.remote_refs
691
692
    def get_determine_wants_revids(self, revids, include_tags=False):
693
        wants = set()
694
        for revid in set(revids):
695
            if revid == NULL_REVISION:
696
                continue
697
            git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
698
            wants.add(git_sha)
699
        return self.get_determine_wants_heads(wants, include_tags=include_tags)
700
701
    def determine_wants_all(self, refs):
7103.2.1 by Jelmer Vernooij
Don't attempt to retrieve peeled SHAs.
702
        potential = set([
703
            v for k, v in refs.items()
704
            if not v == ZERO_SHA and not k.endswith(ANNOTATED_TAG_SUFFIX)])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
705
        return list(potential - self._target_has_shas(potential))
706
707
708
class InterLocalGitLocalGitRepository(InterGitGitRepository):
709
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
710
    def fetch_objects(self, determine_wants, mapping=None, limit=None,
711
                      lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
712
        if lossy:
713
            raise LossyPushToSameVCS(self.source, self.target)
714
        if limit is not None:
715
            raise FetchLimitUnsupported(self)
7131.6.1 by Jelmer Vernooij
Add progress bar for local git->git fetches.
716
        from .remote import DefaultProgressReporter
7143.22.1 by Jelmer Vernooij
use context on progress bars.
717
        with ui.ui_factory.nested_progress_bar() as pb:
718
            progress = DefaultProgressReporter(pb).progress
7131.6.1 by Jelmer Vernooij
Add progress bar for local git->git fetches.
719
            refs = self.source._git.fetch(
7143.15.2 by Jelmer Vernooij
Run autopep8.
720
                self.target._git, determine_wants,
721
                progress=progress)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
722
        return (None, None, refs)
723
724
    @staticmethod
725
    def is_compatible(source, target):
726
        """Be compatible with GitRepository."""
727
        return (isinstance(source, LocalGitRepository) and
728
                isinstance(target, LocalGitRepository))
729
730
731
class InterRemoteGitLocalGitRepository(InterGitGitRepository):
732
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
733
    def fetch_objects(self, determine_wants, mapping=None, limit=None,
734
                      lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
735
        if lossy:
736
            raise LossyPushToSameVCS(self.source, self.target)
737
        if limit is not None:
738
            raise FetchLimitUnsupported(self)
739
        graphwalker = self.target._git.get_graph_walker()
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
740
        if (CAPABILITY_THIN_PACK in
741
                self.source.controldir._client._fetch_capabilities):
0.405.1 by Jelmer Vernooij
Use same logic for interpreting progress reports everywhere.
742
            # TODO(jelmer): Avoid reading entire file into memory and
743
            # only processing it after the whole file has been fetched.
744
            f = BytesIO()
745
746
            def commit():
747
                if f.tell():
748
                    f.seek(0)
749
                    self.target._git.object_store.move_in_thin_pack(f)
750
751
            def abort():
752
                pass
753
        else:
754
            f, commit, abort = self.target._git.object_store.add_pack()
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
755
        try:
0.405.1 by Jelmer Vernooij
Use same logic for interpreting progress reports everywhere.
756
            refs = self.source.controldir.fetch_pack(
757
                determine_wants, graphwalker, f.write)
758
            commit()
759
            return (None, None, refs)
760
        except BaseException:
761
            abort()
762
            raise
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
763
764
    @staticmethod
765
    def is_compatible(source, target):
766
        """Be compatible with GitRepository."""
767
        return (isinstance(source, RemoteGitRepository) and
768
                isinstance(target, LocalGitRepository))