/brz/remove-bazaar

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