/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
7490.93.2 by Jelmer Vernooij
Ignore missing revs.
246
            if v and not v.startswith(SYMREF):
6987.1.2 by Jelmer Vernooij
Correctly deal with symbolic refs when pushing.
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)
7490.148.1 by Jelmer Vernooij
Different names for functions with a different signature.:
265
            revidmap = self.fetch_revs(
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
7490.148.1 by Jelmer Vernooij
Different names for functions with a different signature.:
289
    def fetch_revs(self, revs, lossy, limit=None):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
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:
7490.92.1 by Jelmer Vernooij
Don't spam refnames.
312
                            pass
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
313
                    revidmap[old_revid] = (git_sha, new_revid)
314
                self.target_store.add_objects(object_generator)
315
                return revidmap
316
317
    def fetch(self, revision_id=None, pb=None, find_ghosts=False,
7455.2.1 by Jelmer Vernooij
Return a FetchResult object from Repository.fetch / Branch.fetch.
318
              fetch_spec=None, mapped_refs=None, lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
319
        if mapped_refs is not None:
320
            stop_revisions = mapped_refs
321
        elif revision_id is not None:
322
            stop_revisions = [(None, revision_id)]
323
        elif fetch_spec is not None:
324
            recipe = fetch_spec.get_recipe()
325
            if recipe[0] in ("search", "proxy-search"):
326
                stop_revisions = [(None, revid) for revid in recipe[1]]
327
            else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
328
                raise AssertionError(
329
                    "Unsupported search result type %s" % recipe[0])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
330
        else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
331
            stop_revisions = [(None, revid)
332
                              for revid in self.source.all_revision_ids()]
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
333
        self._warn_slow()
334
        try:
7490.148.1 by Jelmer Vernooij
Different names for functions with a different signature.:
335
            revidmap = self.fetch_revs(stop_revisions, lossy=lossy)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
336
        except NoPushSupport:
337
            raise NoRoundtrippingSupport(self.source, self.target)
7455.2.1 by Jelmer Vernooij
Return a FetchResult object from Repository.fetch / Branch.fetch.
338
        return FetchResult(revidmap)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
339
340
    @staticmethod
341
    def is_compatible(source, target):
342
        """Be compatible with GitRepository."""
343
        return (not isinstance(source, GitRepository) and
344
                isinstance(target, LocalGitRepository))
345
346
347
class InterToRemoteGitRepository(InterToGitRepository):
348
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
349
    def fetch_refs(self, update_refs, lossy, overwrite=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
350
        """Import the gist of the ancestry of a particular revision."""
351
        if not lossy and not self.mapping.roundtripping:
352
            raise NoPushSupport(self.source, self.target, self.mapping)
353
        unpeel_map = UnpeelMap.from_repository(self.source)
354
        revidmap = {}
7143.15.2 by Jelmer Vernooij
Run autopep8.
355
7103.2.1 by Jelmer Vernooij
Don't attempt to retrieve peeled SHAs.
356
        def git_update_refs(old_refs):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
357
            ret = {}
7131.14.3 by Jelmer Vernooij
Merge trunk.
358
            self.old_refs = {
7479.2.1 by Jelmer Vernooij
Drop python2 support.
359
                k: (v, None) for (k, v) in old_refs.items()}
7131.14.2 by Jelmer Vernooij
Make sure resulting branch is correct too.
360
            new_refs = update_refs(self.old_refs)
7479.2.1 by Jelmer Vernooij
Drop python2 support.
361
            for name, (gitid, revid) in new_refs.items():
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
362
                if gitid is None:
363
                    git_sha = self.source_store._lookup_revision_sha1(revid)
7143.15.2 by Jelmer Vernooij
Run autopep8.
364
                    gitid = unpeel_map.re_unpeel_tag(
365
                        git_sha, old_refs.get(name))
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
366
                if not overwrite:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
367
                    if remote_divergence(
368
                            old_refs.get(name), gitid, self.source_store):
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
369
                        raise DivergedBranches(self.source, self.target)
370
                ret[name] = gitid
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
371
            return ret
372
        self._warn_slow()
373
        with self.source_store.lock_read():
7490.60.1 by Jelmer Vernooij
Add support for new push interface in Dulwich >= 0.20.4.
374
            result = self.target.send_pack(
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
375
                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.
376
            if result is not None and not isinstance(result, dict):
377
                for ref, error in result.ref_status.items():
378
                    if error:
379
                        raise RemoteGitError(
380
                            'unable to update ref %r: %s' % (ref, error))
381
                new_refs = result.refs
382
            else:  # dulwich < 0.20.3
383
                new_refs = result
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
384
        # FIXME: revidmap?
7131.14.2 by Jelmer Vernooij
Make sure resulting branch is correct too.
385
        return revidmap, self.old_refs, new_refs
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
386
387
    @staticmethod
388
    def is_compatible(source, target):
389
        """Be compatible with GitRepository."""
390
        return (not isinstance(source, GitRepository) and
391
                isinstance(target, RemoteGitRepository))
392
393
6989.2.5 by Jelmer Vernooij
More test fixes.
394
class GitSearchResult(object):
395
396
    def __init__(self, start, exclude, keys):
397
        self._start = start
398
        self._exclude = exclude
399
        self._keys = keys
400
401
    def get_keys(self):
402
        return self._keys
403
404
    def get_recipe(self):
405
        return ('search', self._start, self._exclude, len(self._keys))
406
407
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
408
class InterFromGitRepository(InterRepository):
409
410
    _matching_repo_format = GitRepositoryFormat()
411
412
    def _target_has_shas(self, shas):
413
        raise NotImplementedError(self._target_has_shas)
414
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
415
    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.
416
        wants = set(wants)
7143.15.2 by Jelmer Vernooij
Run autopep8.
417
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
418
        def determine_wants(refs):
7143.12.1 by Jelmer Vernooij
Support cloning revisions referenced only by an annotated tag.
419
            unpeel_lookup = {}
7479.2.1 by Jelmer Vernooij
Drop python2 support.
420
            for k, v in refs.items():
7143.12.1 by Jelmer Vernooij
Support cloning revisions referenced only by an annotated tag.
421
                if k.endswith(ANNOTATED_TAG_SUFFIX):
422
                    unpeel_lookup[v] = refs[k[:-len(ANNOTATED_TAG_SUFFIX)]]
423
            potential = set([unpeel_lookup.get(w, w) for w in wants])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
424
            if include_tags:
7479.2.1 by Jelmer Vernooij
Drop python2 support.
425
                for k, sha in refs.items():
7103.2.1 by Jelmer Vernooij
Don't attempt to retrieve peeled SHAs.
426
                    if k.endswith(ANNOTATED_TAG_SUFFIX):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
427
                        continue
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
428
                    try:
429
                        tag_name = ref_to_tag_name(k)
430
                    except ValueError:
431
                        continue
432
                    if tag_selector and not tag_selector(tag_name):
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
                    if sha == ZERO_SHA:
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
435
                        continue
7143.12.1 by Jelmer Vernooij
Support cloning revisions referenced only by an annotated tag.
436
                    potential.add(sha)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
437
            return list(potential - self._target_has_shas(potential))
438
        return determine_wants
439
440
    def determine_wants_all(self, refs):
441
        raise NotImplementedError(self.determine_wants_all)
442
443
    @staticmethod
444
    def _get_repo_format_to_test():
445
        return None
446
447
    def copy_content(self, revision_id=None):
448
        """See InterRepository.copy_content."""
449
        self.fetch(revision_id, find_ghosts=False)
450
451
    def search_missing_revision_ids(self,
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
452
                                    find_ghosts=True, revision_ids=None,
453
                                    if_present_ids=None, limit=None):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
454
        if limit is not None:
455
            raise FetchLimitUnsupported(self)
6989.2.7 by Jelmer Vernooij
hackishly fix search_missing_revision_ids.
456
        if revision_ids is None and if_present_ids is None:
6989.2.5 by Jelmer Vernooij
More test fixes.
457
            todo = set(self.source.all_revision_ids())
6989.2.7 by Jelmer Vernooij
hackishly fix search_missing_revision_ids.
458
        else:
459
            todo = set()
460
            if revision_ids is not None:
461
                for revid in revision_ids:
462
                    if not self.source.has_revision(revid):
463
                        raise NoSuchRevision(revid, self.source)
464
                todo.update(revision_ids)
465
            if if_present_ids is not None:
466
                todo.update(if_present_ids)
467
        result_set = todo.difference(self.target.all_revision_ids())
7479.2.1 by Jelmer Vernooij
Drop python2 support.
468
        result_parents = set(itertools.chain.from_iterable(
469
            self.source.get_graph().get_parent_map(result_set).values()))
6989.2.7 by Jelmer Vernooij
hackishly fix search_missing_revision_ids.
470
        included_keys = result_set.intersection(result_parents)
471
        start_keys = result_set.difference(included_keys)
472
        exclude_keys = result_parents.difference(result_set)
473
        return GitSearchResult(start_keys, exclude_keys, result_set)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
474
475
476
class InterGitNonGitRepository(InterFromGitRepository):
477
    """Base InterRepository that copies revisions from a Git into a non-Git
478
    repository."""
479
480
    def _target_has_shas(self, shas):
481
        revids = {}
482
        for sha in shas:
483
            try:
484
                revid = self.source.lookup_foreign_revision_id(sha)
485
            except NotCommitError:
486
                # Commit is definitely not present
487
                continue
488
            else:
489
                revids[revid] = sha
490
        return set([revids[r] for r in self.target.has_revisions(revids)])
491
492
    def determine_wants_all(self, refs):
493
        potential = set()
7479.2.1 by Jelmer Vernooij
Drop python2 support.
494
        for k, v in refs.items():
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
495
            # For non-git target repositories, only worry about peeled
496
            if v == ZERO_SHA:
497
                continue
498
            potential.add(self.source.controldir.get_peeled(k) or v)
499
        return list(potential - self._target_has_shas(potential))
500
501
    def _warn_slow(self):
0.418.1 by Jelmer Vernooij
Support suppressing slow intervcs warnings.
502
        if not config.GlobalConfig().suppress_warning('slow_intervcs_push'):
503
            trace.warning(
504
                'Fetching from Git to Bazaar repository. '
505
                'For better performance, fetch into a Git repository.')
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
506
507
    def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
508
        """Fetch objects from a remote server.
509
510
        :param determine_wants: determine_wants callback
511
        :param mapping: BzrGitMapping to use
512
        :param limit: Maximum number of commits to import.
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
513
        :return: Tuple with pack hint, last imported revision id and remote
514
            refs
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
515
        """
516
        raise NotImplementedError(self.fetch_objects)
517
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
518
    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.
519
        wants = set()
520
        for revid in set(revids):
521
            if self.target.has_revision(revid):
522
                continue
523
            git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
524
            wants.add(git_sha)
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
525
        return self.get_determine_wants_heads(
526
            wants, include_tags=include_tags, tag_selector=tag_selector)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
527
528
    def fetch(self, revision_id=None, find_ghosts=False,
7455.2.1 by Jelmer Vernooij
Return a FetchResult object from Repository.fetch / Branch.fetch.
529
              mapping=None, fetch_spec=None, include_tags=False, lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
530
        if mapping is None:
531
            mapping = self.source.get_mapping()
532
        if revision_id is not None:
533
            interesting_heads = [revision_id]
534
        elif fetch_spec is not None:
535
            recipe = fetch_spec.get_recipe()
536
            if recipe[0] in ("search", "proxy-search"):
537
                interesting_heads = recipe[1]
538
            else:
539
                raise AssertionError("Unsupported search result type %s" %
7143.15.2 by Jelmer Vernooij
Run autopep8.
540
                                     recipe[0])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
541
        else:
542
            interesting_heads = None
543
544
        if interesting_heads is not None:
545
            determine_wants = self.get_determine_wants_revids(
546
                interesting_heads, include_tags=include_tags)
547
        else:
548
            determine_wants = self.determine_wants_all
549
7455.2.1 by Jelmer Vernooij
Return a FetchResult object from Repository.fetch / Branch.fetch.
550
        (pack_hint, _, remote_refs) = self.fetch_objects(
551
            determine_wants, mapping, lossy=lossy)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
552
        if pack_hint is not None and self.target._format.pack_compresses:
553
            self.target.pack(hint=pack_hint)
7455.2.2 by Jelmer Vernooij
Fix git tests.
554
        result = FetchResult()
555
        result.refs = remote_refs
556
        return result
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
557
558
559
class InterRemoteGitNonGitRepository(InterGitNonGitRepository):
560
    """InterRepository that copies revisions from a remote Git into a non-Git
561
    repository."""
562
563
    def get_target_heads(self):
564
        # FIXME: This should be more efficient
565
        all_revs = self.target.all_revision_ids()
566
        parent_map = self.target.get_parent_map(all_revs)
567
        all_parents = set()
7479.2.1 by Jelmer Vernooij
Drop python2 support.
568
        for values in parent_map.values():
7045.4.5 by Jelmer Vernooij
Don't use map as a way to avoid loops.
569
            all_parents.update(values)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
570
        return set(all_revs) - all_parents
571
572
    def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
573
        """See `InterGitNonGitRepository`."""
574
        self._warn_slow()
575
        store = get_object_store(self.target, mapping)
576
        with store.lock_write():
577
            heads = self.get_target_heads()
578
            graph_walker = ObjectStoreGraphWalker(
579
                [store._lookup_revision_sha1(head) for head in heads],
580
                lambda sha: store[sha].parents)
581
            wants_recorder = DetermineWantsRecorder(determine_wants)
582
7143.22.1 by Jelmer Vernooij
use context on progress bars.
583
            with ui.ui_factory.nested_progress_bar() as pb:
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
584
                objects_iter = self.source.fetch_objects(
0.405.1 by Jelmer Vernooij
Use same logic for interpreting progress reports everywhere.
585
                    wants_recorder, graph_walker, store.get_raw)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
586
                trace.mutter("Importing %d new revisions",
587
                             len(wants_recorder.wants))
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
588
                (pack_hint, last_rev) = import_git_objects(
589
                    self.target, mapping, objects_iter, store,
590
                    wants_recorder.wants, pb, limit)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
591
                return (pack_hint, last_rev, wants_recorder.remote_refs)
592
593
    @staticmethod
594
    def is_compatible(source, target):
595
        """Be compatible with GitRepository."""
596
        if not isinstance(source, RemoteGitRepository):
597
            return False
598
        if not target.supports_rich_root():
599
            return False
600
        if isinstance(target, GitRepository):
601
            return False
602
        if not getattr(target._format, "supports_full_versioned_files", True):
603
            return False
604
        return True
605
606
607
class InterLocalGitNonGitRepository(InterGitNonGitRepository):
608
    """InterRepository that copies revisions from a local Git into a non-Git
609
    repository."""
610
611
    def fetch_objects(self, determine_wants, mapping, limit=None, lossy=False):
612
        """See `InterGitNonGitRepository`."""
613
        self._warn_slow()
614
        remote_refs = self.source.controldir.get_refs_container().as_dict()
615
        wants = determine_wants(remote_refs)
616
        target_git_object_retriever = get_object_store(self.target, mapping)
7143.22.1 by Jelmer Vernooij
use context on progress bars.
617
        with ui.ui_factory.nested_progress_bar() as pb:
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
618
            target_git_object_retriever.lock_write()
619
            try:
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
620
                (pack_hint, last_rev) = import_git_objects(
621
                    self.target, mapping, self.source._git.object_store,
622
                    target_git_object_retriever, wants, pb, limit)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
623
                return (pack_hint, last_rev, remote_refs)
624
            finally:
625
                target_git_object_retriever.unlock()
626
627
    @staticmethod
628
    def is_compatible(source, target):
629
        """Be compatible with GitRepository."""
630
        if not isinstance(source, LocalGitRepository):
631
            return False
632
        if not target.supports_rich_root():
633
            return False
634
        if isinstance(target, GitRepository):
635
            return False
636
        if not getattr(target._format, "supports_full_versioned_files", True):
637
            return False
638
        return True
639
640
641
class InterGitGitRepository(InterFromGitRepository):
642
    """InterRepository that copies between Git repositories."""
643
0.404.5 by Jelmer Vernooij
Check for diverged branches during push.
644
    def fetch_refs(self, update_refs, lossy, overwrite=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
645
        if lossy:
646
            raise LossyPushToSameVCS(self.source, self.target)
647
        old_refs = self.target.controldir.get_refs_container()
648
        ref_changes = {}
7143.15.2 by Jelmer Vernooij
Run autopep8.
649
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
650
        def determine_wants(heads):
7143.15.2 by Jelmer Vernooij
Run autopep8.
651
            old_refs = dict([(k, (v, None))
7479.2.1 by Jelmer Vernooij
Drop python2 support.
652
                             for (k, v) in heads.as_dict().items()])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
653
            new_refs = update_refs(old_refs)
654
            ref_changes.update(new_refs)
7479.2.1 by Jelmer Vernooij
Drop python2 support.
655
            return [sha1 for (sha1, bzr_revid) in new_refs.values()]
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
656
        self.fetch_objects(determine_wants, lossy=lossy)
7479.2.1 by Jelmer Vernooij
Drop python2 support.
657
        for k, (git_sha, bzr_revid) in ref_changes.items():
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
658
            self.target._git.refs[k] = git_sha
659
        new_refs = self.target.controldir.get_refs_container()
660
        return None, old_refs, new_refs
661
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
662
    def fetch_objects(self, determine_wants, mapping=None, limit=None,
663
                      lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
664
        raise NotImplementedError(self.fetch_objects)
665
666
    def _target_has_shas(self, shas):
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
667
        return set(
668
            [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.
669
670
    def fetch(self, revision_id=None, find_ghosts=False,
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
671
              mapping=None, fetch_spec=None, branches=None, limit=None,
7455.2.1 by Jelmer Vernooij
Return a FetchResult object from Repository.fetch / Branch.fetch.
672
              include_tags=False, lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
673
        if mapping is None:
674
            mapping = self.source.get_mapping()
675
        if revision_id is not None:
676
            args = [revision_id]
677
        elif fetch_spec is not None:
678
            recipe = fetch_spec.get_recipe()
679
            if recipe[0] in ("search", "proxy-search"):
680
                heads = recipe[1]
681
            else:
682
                raise AssertionError(
683
                    "Unsupported search result type %s" % recipe[0])
684
            args = heads
685
        if branches is not None:
7413.4.2 by Jelmer Vernooij
Factor out determine wants for branches.
686
            determine_wants = self.get_determine_wants_branches(
687
                branches, include_tags=include_tags)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
688
        elif fetch_spec is None and revision_id is None:
689
            determine_wants = self.determine_wants_all
690
        else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
691
            determine_wants = self.get_determine_wants_revids(
692
                args, include_tags=include_tags)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
693
        wants_recorder = DetermineWantsRecorder(determine_wants)
7455.2.1 by Jelmer Vernooij
Return a FetchResult object from Repository.fetch / Branch.fetch.
694
        self.fetch_objects(wants_recorder, mapping, limit=limit, lossy=lossy)
7455.2.2 by Jelmer Vernooij
Fix git tests.
695
        result = FetchResult()
696
        result.refs = wants_recorder.remote_refs
697
        return result
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
698
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
699
    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.
700
        wants = set()
701
        for revid in set(revids):
702
            if revid == NULL_REVISION:
703
                continue
704
            git_sha, mapping = self.source.lookup_bzr_revision_id(revid)
705
            wants.add(git_sha)
7489.4.2 by Jelmer Vernooij
Plumb through tag_selector.
706
        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.
707
7413.4.2 by Jelmer Vernooij
Factor out determine wants for branches.
708
    def get_determine_wants_branches(self, branches, include_tags=False):
709
        def determine_wants(refs):
710
            ret = []
7479.2.1 by Jelmer Vernooij
Drop python2 support.
711
            for name, value in refs.items():
7413.4.2 by Jelmer Vernooij
Factor out determine wants for branches.
712
                if value == ZERO_SHA:
713
                    continue
714
715
                if name.endswith(ANNOTATED_TAG_SUFFIX):
716
                    continue
717
718
                if name in branches or (include_tags and is_tag(name)):
719
                    ret.append(value)
720
            return ret
721
        return determine_wants
722
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
723
    def determine_wants_all(self, refs):
7103.2.1 by Jelmer Vernooij
Don't attempt to retrieve peeled SHAs.
724
        potential = set([
725
            v for k, v in refs.items()
726
            if not v == ZERO_SHA and not k.endswith(ANNOTATED_TAG_SUFFIX)])
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
727
        return list(potential - self._target_has_shas(potential))
728
729
730
class InterLocalGitLocalGitRepository(InterGitGitRepository):
731
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
732
    def fetch_objects(self, determine_wants, mapping=None, limit=None,
733
                      lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
734
        if lossy:
735
            raise LossyPushToSameVCS(self.source, self.target)
736
        if limit is not None:
737
            raise FetchLimitUnsupported(self)
7131.6.1 by Jelmer Vernooij
Add progress bar for local git->git fetches.
738
        from .remote import DefaultProgressReporter
7143.22.1 by Jelmer Vernooij
use context on progress bars.
739
        with ui.ui_factory.nested_progress_bar() as pb:
740
            progress = DefaultProgressReporter(pb).progress
7131.6.1 by Jelmer Vernooij
Add progress bar for local git->git fetches.
741
            refs = self.source._git.fetch(
7143.15.2 by Jelmer Vernooij
Run autopep8.
742
                self.target._git, determine_wants,
743
                progress=progress)
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
744
        return (None, None, refs)
745
746
    @staticmethod
747
    def is_compatible(source, target):
748
        """Be compatible with GitRepository."""
749
        return (isinstance(source, LocalGitRepository) and
750
                isinstance(target, LocalGitRepository))
751
752
753
class InterRemoteGitLocalGitRepository(InterGitGitRepository):
754
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
755
    def fetch_objects(self, determine_wants, mapping=None, limit=None,
756
                      lossy=False):
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
757
        if lossy:
758
            raise LossyPushToSameVCS(self.source, self.target)
759
        if limit is not None:
760
            raise FetchLimitUnsupported(self)
761
        graphwalker = self.target._git.get_graph_walker()
7143.15.3 by Jelmer Vernooij
Fix pep8 issues in breezy.git.
762
        if (CAPABILITY_THIN_PACK in
763
                self.source.controldir._client._fetch_capabilities):
0.405.1 by Jelmer Vernooij
Use same logic for interpreting progress reports everywhere.
764
            # TODO(jelmer): Avoid reading entire file into memory and
765
            # only processing it after the whole file has been fetched.
766
            f = BytesIO()
767
768
            def commit():
769
                if f.tell():
770
                    f.seek(0)
771
                    self.target._git.object_store.move_in_thin_pack(f)
772
773
            def abort():
774
                pass
775
        else:
776
            f, commit, abort = self.target._git.object_store.add_pack()
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
777
        try:
0.405.1 by Jelmer Vernooij
Use same logic for interpreting progress reports everywhere.
778
            refs = self.source.controldir.fetch_pack(
779
                determine_wants, graphwalker, f.write)
780
            commit()
781
            return (None, None, refs)
782
        except BaseException:
783
            abort()
784
            raise
0.401.2 by Jelmer Vernooij
Move all InterRepository implementations into interrepo.
785
786
    @staticmethod
787
    def is_compatible(source, target):
788
        """Be compatible with GitRepository."""
789
        return (isinstance(source, RemoteGitRepository) and
790
                isinstance(target, LocalGitRepository))
7490.145.1 by Jelmer Vernooij
Add InterLocalGitRemoteGitRepository.
791
792
793
794
class InterLocalGitRemoteGitRepository(InterToGitRepository):
795
796
    def fetch_refs(self, update_refs, lossy=False, overwrite=False):
797
        """Import the gist of the ancestry of a particular revision."""
798
        if lossy:
799
            raise LossyPushToSameVCS(self.source, self.target)
800
801
        def git_update_refs(old_refs):
802
            ret = {}
803
            self.old_refs = {
804
                k: (v, None) for (k, v) in viewitems(old_refs)}
805
            new_refs = update_refs(self.old_refs)
806
            for name, (gitid, revid) in viewitems(new_refs):
807
                if gitid is None:
808
                    gitid = self.source_store._lookup_revision_sha1(revid)
809
                if not overwrite:
810
                    if remote_divergence(
811
                            old_refs.get(name), gitid, self.source_store):
812
                        raise DivergedBranches(self.source, self.target)
813
                ret[name] = gitid
814
            return ret
815
        new_refs = self.target.send_pack(
816
            git_update_refs,
817
            self.source._git.generate_pack_data)
818
        return None, self.old_refs, new_refs
819
820
    @staticmethod
821
    def is_compatible(source, target):
822
        return (isinstance(source, LocalGitRepository) and
823
                isinstance(target, RemoteGitRepository))