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