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