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