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