/brz/remove-bazaar

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