1# Copyright (C) 2005-2011 Canonical Ltd
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
17from .lazy_import import lazy_import
18lazy_import(globals(), """
19import time
20
21from breezy import (
22    config,
23    controldir,
24    debug,
25    graph,
26    osutils,
27    revision as _mod_revision,
28    gpg,
29    )
30from breezy.i18n import gettext
31""")
32
33from . import (
34    errors,
35    registry,
36    ui,
37    )
38from .decorators import only_raises
39from .inter import InterObject
40from .lock import _RelockDebugMixin, LogicalLockResult
41from .trace import (
42    log_exception_quietly, note, mutter, mutter_callsite, warning)
43
44
45# Old formats display a warning, but only once
46_deprecation_warning_done = False
47
48
49class IsInWriteGroupError(errors.InternalBzrError):
50
51    _fmt = "May not refresh_data of repo %(repo)s while in a write group."
52
53    def __init__(self, repo):
54        errors.InternalBzrError.__init__(self, repo=repo)
55
56
57class CannotSetRevisionId(errors.BzrError):
58
59    _fmt = "Repository format does not support setting revision ids."
60
61
62class FetchResult(object):
63    """Result of a fetch operation.
64
65    :ivar revidmap: For lossy fetches, map from source revid to target revid.
66    :ivar total_fetched: Number of revisions fetched
67    """
68
69    def __init__(self, total_fetched=None, revidmap=None):
70        self.total_fetched = total_fetched
71        self.revidmap = revidmap
72
73
74class CommitBuilder(object):
75    """Provides an interface to build up a commit.
76
77    This allows describing a tree to be committed without needing to
78    know the internals of the format of the repository.
79    """
80
81    # all clients should supply tree roots.
82    record_root_entry = True
83    # whether this commit builder will automatically update the branch that is
84    # being committed to
85    updates_branch = False
86
87    def __init__(self, repository, parents, config_stack, timestamp=None,
88                 timezone=None, committer=None, revprops=None,
89                 revision_id=None, lossy=False):
90        """Initiate a CommitBuilder.
91
92        :param repository: Repository to commit to.
93        :param parents: Revision ids of the parents of the new revision.
94        :param timestamp: Optional timestamp recorded for commit.
95        :param timezone: Optional timezone for timestamp.
96        :param committer: Optional committer to set for commit.
97        :param revprops: Optional dictionary of revision properties.
98        :param revision_id: Optional revision id.
99        :param lossy: Whether to discard data that can not be natively
100            represented, when pushing to a foreign VCS
101        """
102        self._config_stack = config_stack
103        self._lossy = lossy
104
105        if committer is None:
106            self._committer = self._config_stack.get('email')
107        elif not isinstance(committer, str):
108            self._committer = committer.decode()  # throw if non-ascii
109        else:
110            self._committer = committer
111
112        self.parents = parents
113        self.repository = repository
114
115        self._revprops = {}
116        if revprops is not None:
117            self._validate_revprops(revprops)
118            self._revprops.update(revprops)
119
120        if timestamp is None:
121            timestamp = time.time()
122        # Restrict resolution to 1ms
123        self._timestamp = round(timestamp, 3)
124
125        if timezone is None:
126            self._timezone = osutils.local_time_offset()
127        else:
128            self._timezone = int(timezone)
129
130        self._generate_revision_if_needed(revision_id)
131
132    def any_changes(self):
133        """Return True if any entries were changed.
134
135        This includes merge-only changes. It is the core for the --unchanged
136        detection in commit.
137
138        :return: True if any changes have occured.
139        """
140        raise NotImplementedError(self.any_changes)
141
142    def _validate_unicode_text(self, text, context):
143        """Verify things like commit messages don't have bogus characters."""
144        # TODO(jelmer): Make this repository-format specific
145        if u'\r' in text:
146            raise ValueError('Invalid value for %s: %r' % (context, text))
147
148    def _validate_revprops(self, revprops):
149        for key, value in revprops.items():
150            # We know that the XML serializers do not round trip '\r'
151            # correctly, so refuse to accept them
152            if not isinstance(value, str):
153                raise ValueError('revision property (%s) is not a valid'
154                                 ' (unicode) string: %r' % (key, value))
155            # TODO(jelmer): Make this repository-format specific
156            self._validate_unicode_text(value,
157                                        'revision property (%s)' % (key,))
158
159    def commit(self, message):
160        """Make the actual commit.
161
162        :return: The revision id of the recorded revision.
163        """
164        raise NotImplementedError(self.commit)
165
166    def abort(self):
167        """Abort the commit that is being built.
168        """
169        raise NotImplementedError(self.abort)
170
171    def revision_tree(self):
172        """Return the tree that was just committed.
173
174        After calling commit() this can be called to get a
175        RevisionTree representing the newly committed tree. This is
176        preferred to calling Repository.revision_tree() because that may
177        require deserializing the inventory, while we already have a copy in
178        memory.
179        """
180        raise NotImplementedError(self.revision_tree)
181
182    def finish_inventory(self):
183        """Tell the builder that the inventory is finished.
184
185        :return: The inventory id in the repository, which can be used with
186            repository.get_inventory.
187        """
188        raise NotImplementedError(self.finish_inventory)
189
190    def _generate_revision_if_needed(self, revision_id):
191        """Create a revision id if None was supplied.
192
193        If the repository can not support user-specified revision ids
194        they should override this function and raise CannotSetRevisionId
195        if _new_revision_id is not None.
196
197        :raises: CannotSetRevisionId
198        """
199        if not self.repository._format.supports_setting_revision_ids:
200            if revision_id is not None:
201                raise CannotSetRevisionId()
202            return
203        if revision_id is None:
204            self._new_revision_id = self._gen_revision_id()
205            self.random_revid = True
206        else:
207            self._new_revision_id = revision_id
208            self.random_revid = False
209
210    def record_iter_changes(self, tree, basis_revision_id, iter_changes):
211        """Record a new tree via iter_changes.
212
213        :param tree: The tree to obtain text contents from for changed objects.
214        :param basis_revision_id: The revision id of the tree the iter_changes
215            has been generated against. Currently assumed to be the same
216            as self.parents[0] - if it is not, errors may occur.
217        :param iter_changes: An iter_changes iterator with the changes to apply
218            to basis_revision_id. The iterator must not include any items with
219            a current kind of None - missing items must be either filtered out
220            or errored-on beefore record_iter_changes sees the item.
221        :return: A generator of (relpath, fs_hash) tuples for use with
222            tree._observed_sha1.
223        """
224        raise NotImplementedError(self.record_iter_changes)
225
226
227class RepositoryWriteLockResult(LogicalLockResult):
228    """The result of write locking a repository.
229
230    :ivar repository_token: The token obtained from the underlying lock, or
231        None.
232    :ivar unlock: A callable which will unlock the lock.
233    """
234
235    def __init__(self, unlock, repository_token):
236        LogicalLockResult.__init__(self, unlock)
237        self.repository_token = repository_token
238
239    def __repr__(self):
240        return "RepositoryWriteLockResult(%s, %s)" % (self.repository_token,
241                                                      self.unlock)
242
243
244class WriteGroup(object):
245    """Context manager that manages a write group.
246
247    Raising an exception will result in the write group being aborted.
248    """
249
250    def __init__(self, repository, suppress_errors=False):
251        self.repository = repository
252        self._suppress_errors = suppress_errors
253
254    def __enter__(self):
255        self.repository.start_write_group()
256        return self
257
258    def __exit__(self, exc_type, exc_val, exc_tb):
259        if exc_type:
260            self.repository.abort_write_group(self._suppress_errors)
261            return False
262        else:
263            self.repository.commit_write_group()
264
265
266######################################################################
267# Repositories
268
269
270class Repository(controldir.ControlComponent, _RelockDebugMixin):
271    """Repository holding history for one or more branches.
272
273    The repository holds and retrieves historical information including
274    revisions and file history.  It's normally accessed only by the Branch,
275    which views a particular line of development through that history.
276
277    See VersionedFileRepository in breezy.vf_repository for the
278    base class for most Bazaar repositories.
279    """
280
281    # Does this repository implementation support random access to
282    # items in the tree, or just bulk fetching/pushing of data?
283    supports_random_access = True
284
285    def abort_write_group(self, suppress_errors=False):
286        """Commit the contents accrued within the current write group.
287
288        :param suppress_errors: if true, abort_write_group will catch and log
289            unexpected errors that happen during the abort, rather than
290            allowing them to propagate.  Defaults to False.
291
292        :seealso: start_write_group.
293        """
294        if self._write_group is not self.get_transaction():
295            # has an unlock or relock occured ?
296            if suppress_errors:
297                mutter(
298                    '(suppressed) mismatched lock context and write group. %r, %r',
299                    self._write_group, self.get_transaction())
300                return
301            raise errors.BzrError(
302                'mismatched lock context and write group. %r, %r' %
303                (self._write_group, self.get_transaction()))
304        try:
305            self._abort_write_group()
306        except Exception as exc:
307            self._write_group = None
308            if not suppress_errors:
309                raise
310            mutter('abort_write_group failed')
311            log_exception_quietly()
312            note(gettext('brz: ERROR (ignored): %s'), exc)
313        self._write_group = None
314
315    def _abort_write_group(self):
316        """Template method for per-repository write group cleanup.
317
318        This is called during abort before the write group is considered to be
319        finished and should cleanup any internal state accrued during the write
320        group. There is no requirement that data handed to the repository be
321        *not* made available - this is not a rollback - but neither should any
322        attempt be made to ensure that data added is fully commited. Abort is
323        invoked when an error has occured so futher disk or network operations
324        may not be possible or may error and if possible should not be
325        attempted.
326        """
327
328    def add_fallback_repository(self, repository):
329        """Add a repository to use for looking up data not held locally.
330
331        :param repository: A repository.
332        """
333        raise NotImplementedError(self.add_fallback_repository)
334
335    def _check_fallback_repository(self, repository):
336        """Check that this repository can fallback to repository safely.
337
338        Raise an error if not.
339
340        :param repository: A repository to fallback to.
341        """
342        return InterRepository._assert_same_model(self, repository)
343
344    def all_revision_ids(self):
345        """Returns a list of all the revision ids in the repository.
346
347        This is conceptually deprecated because code should generally work on
348        the graph reachable from a particular revision, and ignore any other
349        revisions that might be present.  There is no direct replacement
350        method.
351        """
352        if 'evil' in debug.debug_flags:
353            mutter_callsite(2, "all_revision_ids is linear with history.")
354        return self._all_revision_ids()
355
356    def _all_revision_ids(self):
357        """Returns a list of all the revision ids in the repository.
358
359        These are in as much topological order as the underlying store can
360        present.
361        """
362        raise NotImplementedError(self._all_revision_ids)
363
364    def break_lock(self):
365        """Break a lock if one is present from another instance.
366
367        Uses the ui factory to ask for confirmation if the lock may be from
368        an active process.
369        """
370        self.control_files.break_lock()
371
372    @staticmethod
373    def create(controldir):
374        """Construct the current default format repository in controldir."""
375        return RepositoryFormat.get_default_format().initialize(controldir)
376
377    def __init__(self, _format, controldir, control_files):
378        """instantiate a Repository.
379
380        :param _format: The format of the repository on disk.
381        :param controldir: The ControlDir of the repository.
382        :param control_files: Control files to use for locking, etc.
383        """
384        # In the future we will have a single api for all stores for
385        # getting file texts, inventories and revisions, then
386        # this construct will accept instances of those things.
387        super(Repository, self).__init__()
388        self._format = _format
389        # the following are part of the public API for Repository:
390        self.controldir = controldir
391        self.control_files = control_files
392        # for tests
393        self._write_group = None
394        # Additional places to query for data.
395        self._fallback_repositories = []
396
397    @property
398    def user_transport(self):
399        return self.controldir.user_transport
400
401    @property
402    def control_transport(self):
403        return self._transport
404
405    def __repr__(self):
406        if self._fallback_repositories:
407            return '%s(%r, fallback_repositories=%r)' % (
408                self.__class__.__name__,
409                self.base,
410                self._fallback_repositories)
411        else:
412            return '%s(%r)' % (self.__class__.__name__,
413                               self.base)
414
415    def _has_same_fallbacks(self, other_repo):
416        """Returns true if the repositories have the same fallbacks."""
417        my_fb = self._fallback_repositories
418        other_fb = other_repo._fallback_repositories
419        if len(my_fb) != len(other_fb):
420            return False
421        for f, g in zip(my_fb, other_fb):
422            if not f.has_same_location(g):
423                return False
424        return True
425
426    def has_same_location(self, other):
427        """Returns a boolean indicating if this repository is at the same
428        location as another repository.
429
430        This might return False even when two repository objects are accessing
431        the same physical repository via different URLs.
432        """
433        if self.__class__ is not other.__class__:
434            return False
435        return (self.control_url == other.control_url)
436
437    def is_in_write_group(self):
438        """Return True if there is an open write group.
439
440        :seealso: start_write_group.
441        """
442        return self._write_group is not None
443
444    def is_locked(self):
445        return self.control_files.is_locked()
446
447    def is_write_locked(self):
448        """Return True if this object is write locked."""
449        return self.is_locked() and self.control_files._lock_mode == 'w'
450
451    def lock_write(self, token=None):
452        """Lock this repository for writing.
453
454        This causes caching within the repository obejct to start accumlating
455        data during reads, and allows a 'write_group' to be obtained. Write
456        groups must be used for actual data insertion.
457
458        A token should be passed in if you know that you have locked the object
459        some other way, and need to synchronise this object's state with that
460        fact.
461
462        XXX: this docstring is duplicated in many places, e.g. lockable_files.py
463
464        :param token: if this is already locked, then lock_write will fail
465            unless the token matches the existing lock.
466        :returns: a token if this instance supports tokens, otherwise None.
467        :raises TokenLockingNotSupported: when a token is given but this
468            instance doesn't support using token locks.
469        :raises MismatchedToken: if the specified token doesn't match the token
470            of the existing lock.
471        :seealso: start_write_group.
472        :return: A RepositoryWriteLockResult.
473        """
474        locked = self.is_locked()
475        token = self.control_files.lock_write(token=token)
476        if not locked:
477            self._warn_if_deprecated()
478            self._note_lock('w')
479            for repo in self._fallback_repositories:
480                # Writes don't affect fallback repos
481                repo.lock_read()
482            self._refresh_data()
483        return RepositoryWriteLockResult(self.unlock, token)
484
485    def lock_read(self):
486        """Lock the repository for read operations.
487
488        :return: An object with an unlock method which will release the lock
489            obtained.
490        """
491        locked = self.is_locked()
492        self.control_files.lock_read()
493        if not locked:
494            self._warn_if_deprecated()
495            self._note_lock('r')
496            for repo in self._fallback_repositories:
497                repo.lock_read()
498            self._refresh_data()
499        return LogicalLockResult(self.unlock)
500
501    def get_physical_lock_status(self):
502        return self.control_files.get_physical_lock_status()
503
504    def leave_lock_in_place(self):
505        """Tell this repository not to release the physical lock when this
506        object is unlocked.
507
508        If lock_write doesn't return a token, then this method is not supported.
509        """
510        self.control_files.leave_in_place()
511
512    def dont_leave_lock_in_place(self):
513        """Tell this repository to release the physical lock when this
514        object is unlocked, even if it didn't originally acquire it.
515
516        If lock_write doesn't return a token, then this method is not supported.
517        """
518        self.control_files.dont_leave_in_place()
519
520    def gather_stats(self, revid=None, committers=None):
521        """Gather statistics from a revision id.
522
523        :param revid: The revision id to gather statistics from, if None, then
524            no revision specific statistics are gathered.
525        :param committers: Optional parameter controlling whether to grab
526            a count of committers from the revision specific statistics.
527        :return: A dictionary of statistics. Currently this contains:
528            committers: The number of committers if requested.
529            firstrev: A tuple with timestamp, timezone for the penultimate left
530                most ancestor of revid, if revid is not the NULL_REVISION.
531            latestrev: A tuple with timestamp, timezone for revid, if revid is
532                not the NULL_REVISION.
533            revisions: The total revision count in the repository.
534            size: An estimate disk size of the repository in bytes.
535        """
536        with self.lock_read():
537            result = {}
538            if revid and committers:
539                result['committers'] = 0
540            if revid and revid != _mod_revision.NULL_REVISION:
541                graph = self.get_graph()
542                if committers:
543                    all_committers = set()
544                revisions = [r for (r, p) in graph.iter_ancestry([revid])
545                             if r != _mod_revision.NULL_REVISION]
546                last_revision = None
547                if not committers:
548                    # ignore the revisions in the middle - just grab first and last
549                    revisions = revisions[0], revisions[-1]
550                for revision in self.get_revisions(revisions):
551                    if not last_revision:
552                        last_revision = revision
553                    if committers:
554                        all_committers.add(revision.committer)
555                first_revision = revision
556                if committers:
557                    result['committers'] = len(all_committers)
558                result['firstrev'] = (first_revision.timestamp,
559                                      first_revision.timezone)
560                result['latestrev'] = (last_revision.timestamp,
561                                       last_revision.timezone)
562            return result
563
564    def find_branches(self, using=False):
565        """Find branches underneath this repository.
566
567        This will include branches inside other branches.
568
569        :param using: If True, list only branches using this repository.
570        """
571        if using and not self.is_shared():
572            for branch in self.controldir.list_branches():
573                yield branch
574            return
575
576        class Evaluator(object):
577
578            def __init__(self):
579                self.first_call = True
580
581            def __call__(self, controldir):
582                # On the first call, the parameter is always the controldir
583                # containing the current repo.
584                if not self.first_call:
585                    try:
586                        repository = controldir.open_repository()
587                    except errors.NoRepositoryPresent:
588                        pass
589                    else:
590                        return False, ([], repository)
591                self.first_call = False
592                value = (controldir.list_branches(), None)
593                return True, value
594
595        for branches, repository in controldir.ControlDir.find_controldirs(
596                self.user_transport, evaluate=Evaluator()):
597            if branches is not None:
598                for branch in branches:
599                    yield branch
600            if not using and repository is not None:
601                for branch in repository.find_branches():
602                    yield branch
603
604    def search_missing_revision_ids(self, other,
605                                    find_ghosts=True, revision_ids=None, if_present_ids=None,
606                                    limit=None):
607        """Return the revision ids that other has that this does not.
608
609        These are returned in topological order.
610
611        revision_ids: only return revision ids included by revision_id.
612        """
613        with self.lock_read():
614            return InterRepository.get(other, self).search_missing_revision_ids(
615                find_ghosts=find_ghosts, revision_ids=revision_ids,
616                if_present_ids=if_present_ids, limit=limit)
617
618    @staticmethod
619    def open(base):
620        """Open the repository rooted at base.
621
622        For instance, if the repository is at URL/.bzr/repository,
623        Repository.open(URL) -> a Repository instance.
624        """
625        control = controldir.ControlDir.open(base)
626        return control.open_repository()
627
628    def copy_content_into(self, destination, revision_id=None):
629        """Make a complete copy of the content in self into destination.
630
631        This is a destructive operation! Do not use it on existing
632        repositories.
633        """
634        return InterRepository.get(self, destination).copy_content(revision_id)
635
636    def commit_write_group(self):
637        """Commit the contents accrued within the current write group.
638
639        :seealso: start_write_group.
640
641        :return: it may return an opaque hint that can be passed to 'pack'.
642        """
643        if self._write_group is not self.get_transaction():
644            # has an unlock or relock occured ?
645            raise errors.BzrError('mismatched lock context %r and '
646                                  'write group %r.' %
647                                  (self.get_transaction(), self._write_group))
648        result = self._commit_write_group()
649        self._write_group = None
650        return result
651
652    def _commit_write_group(self):
653        """Template method for per-repository write group cleanup.
654
655        This is called before the write group is considered to be
656        finished and should ensure that all data handed to the repository
657        for writing during the write group is safely committed (to the
658        extent possible considering file system caching etc).
659        """
660
661    def suspend_write_group(self):
662        """Suspend a write group.
663
664        :raise UnsuspendableWriteGroup: If the write group can not be
665            suspended.
666        :return: List of tokens
667        """
668        raise errors.UnsuspendableWriteGroup(self)
669
670    def refresh_data(self):
671        """Re-read any data needed to synchronise with disk.
672
673        This method is intended to be called after another repository instance
674        (such as one used by a smart server) has inserted data into the
675        repository. On all repositories this will work outside of write groups.
676        Some repository formats (pack and newer for breezy native formats)
677        support refresh_data inside write groups. If called inside a write
678        group on a repository that does not support refreshing in a write group
679        IsInWriteGroupError will be raised.
680        """
681        self._refresh_data()
682
683    def resume_write_group(self, tokens):
684        if not self.is_write_locked():
685            raise errors.NotWriteLocked(self)
686        if self._write_group:
687            raise errors.BzrError('already in a write group')
688        self._resume_write_group(tokens)
689        # so we can detect unlock/relock - the write group is now entered.
690        self._write_group = self.get_transaction()
691
692    def _resume_write_group(self, tokens):
693        raise errors.UnsuspendableWriteGroup(self)
694
695    def fetch(self, source, revision_id=None, find_ghosts=False, lossy=False):
696        """Fetch the content required to construct revision_id from source.
697
698        If revision_id is None, then all content is copied.
699
700        fetch() may not be used when the repository is in a write group -
701        either finish the current write group before using fetch, or use
702        fetch before starting the write group.
703
704        :param find_ghosts: Find and copy revisions in the source that are
705            ghosts in the target (and not reachable directly by walking out to
706            the first-present revision in target from revision_id).
707        :param revision_id: If specified, all the content needed for this
708            revision ID will be copied to the target.  Fetch will determine for
709            itself which content needs to be copied.
710        :return: A FetchResult object
711        """
712        if self.is_in_write_group():
713            raise errors.InternalBzrError(
714                "May not fetch while in a write group.")
715        # fast path same-url fetch operations
716        # TODO: lift out to somewhere common with RemoteRepository
717        # <https://bugs.launchpad.net/bzr/+bug/401646>
718        if (self.has_same_location(source)
719                and self._has_same_fallbacks(source)):
720            # check that last_revision is in 'from' and then return a
721            # no-operation.
722            if (revision_id is not None and
723                    not _mod_revision.is_null(revision_id)):
724                self.get_revision(revision_id)
725            return 0, []
726        inter = InterRepository.get(source, self)
727        return inter.fetch(
728            revision_id=revision_id, find_ghosts=find_ghosts, lossy=lossy)
729
730    def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
731                           timezone=None, committer=None, revprops=None,
732                           revision_id=None, lossy=False):
733        """Obtain a CommitBuilder for this repository.
734
735        :param branch: Branch to commit to.
736        :param parents: Revision ids of the parents of the new revision.
737        :param config_stack: Configuration stack to use.
738        :param timestamp: Optional timestamp recorded for commit.
739        :param timezone: Optional timezone for timestamp.
740        :param committer: Optional committer to set for commit.
741        :param revprops: Optional dictionary of revision properties.
742        :param revision_id: Optional revision id.
743        :param lossy: Whether to discard data that can not be natively
744            represented, when pushing to a foreign VCS
745        """
746        raise NotImplementedError(self.get_commit_builder)
747
748    @only_raises(errors.LockNotHeld, errors.LockBroken)
749    def unlock(self):
750        if (self.control_files._lock_count == 1 and
751                self.control_files._lock_mode == 'w'):
752            if self._write_group is not None:
753                self.abort_write_group()
754                self.control_files.unlock()
755                raise errors.BzrError(
756                    'Must end write groups before releasing write locks.')
757        self.control_files.unlock()
758        if self.control_files._lock_count == 0:
759            for repo in self._fallback_repositories:
760                repo.unlock()
761
762    def clone(self, controldir, revision_id=None):
763        """Clone this repository into controldir using the current format.
764
765        Currently no check is made that the format of this repository and
766        the bzrdir format are compatible. FIXME RBC 20060201.
767
768        :return: The newly created destination repository.
769        """
770        with self.lock_read():
771            # TODO: deprecate after 0.16; cloning this with all its settings is
772            # probably not very useful -- mbp 20070423
773            dest_repo = self._create_sprouting_repo(
774                controldir, shared=self.is_shared())
775            self.copy_content_into(dest_repo, revision_id)
776            return dest_repo
777
778    def start_write_group(self):
779        """Start a write group in the repository.
780
781        Write groups are used by repositories which do not have a 1:1 mapping
782        between file ids and backend store to manage the insertion of data from
783        both fetch and commit operations.
784
785        A write lock is required around the
786        start_write_group/commit_write_group for the support of lock-requiring
787        repository formats.
788
789        One can only insert data into a repository inside a write group.
790
791        :return: None.
792        """
793        if not self.is_write_locked():
794            raise errors.NotWriteLocked(self)
795        if self._write_group:
796            raise errors.BzrError('already in a write group')
797        self._start_write_group()
798        # so we can detect unlock/relock - the write group is now entered.
799        self._write_group = self.get_transaction()
800
801    def _start_write_group(self):
802        """Template method for per-repository write group startup.
803
804        This is called before the write group is considered to be
805        entered.
806        """
807
808    def sprout(self, to_bzrdir, revision_id=None):
809        """Create a descendent repository for new development.
810
811        Unlike clone, this does not copy the settings of the repository.
812        """
813        with self.lock_read():
814            dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
815            dest_repo.fetch(self, revision_id=revision_id)
816            return dest_repo
817
818    def _create_sprouting_repo(self, a_controldir, shared):
819        if not isinstance(
820                a_controldir._format, self.controldir._format.__class__):
821            # use target default format.
822            dest_repo = a_controldir.create_repository()
823        else:
824            # Most control formats need the repository to be specifically
825            # created, but on some old all-in-one formats it's not needed
826            try:
827                dest_repo = self._format.initialize(
828                    a_controldir, shared=shared)
829            except errors.UninitializableFormat:
830                dest_repo = a_controldir.open_repository()
831        return dest_repo
832
833    def has_revision(self, revision_id):
834        """True if this repository has a copy of the revision."""
835        with self.lock_read():
836            return revision_id in self.has_revisions((revision_id,))
837
838    def has_revisions(self, revision_ids):
839        """Probe to find out the presence of multiple revisions.
840
841        :param revision_ids: An iterable of revision_ids.
842        :return: A set of the revision_ids that were present.
843        """
844        raise NotImplementedError(self.has_revisions)
845
846    def get_revision(self, revision_id):
847        """Return the Revision object for a named revision."""
848        with self.lock_read():
849            return self.get_revisions([revision_id])[0]
850
851    def get_revision_reconcile(self, revision_id):
852        """'reconcile' helper routine that allows access to a revision always.
853
854        This variant of get_revision does not cross check the weave graph
855        against the revision one as get_revision does: but it should only
856        be used by reconcile, or reconcile-alike commands that are correcting
857        or testing the revision graph.
858        """
859        raise NotImplementedError(self.get_revision_reconcile)
860
861    def get_revisions(self, revision_ids):
862        """Get many revisions at once.
863
864        Repositories that need to check data on every revision read should
865        subclass this method.
866        """
867        revs = {}
868        for revid, rev in self.iter_revisions(revision_ids):
869            if rev is None:
870                raise errors.NoSuchRevision(self, revid)
871            revs[revid] = rev
872        return [revs[revid] for revid in revision_ids]
873
874    def iter_revisions(self, revision_ids):
875        """Iterate over revision objects.
876
877        :param revision_ids: An iterable of revisions to examine. None may be
878            passed to request all revisions known to the repository. Note that
879            not all repositories can find unreferenced revisions; for those
880            repositories only referenced ones will be returned.
881        :return: An iterator of (revid, revision) tuples. Absent revisions (
882            those asked for but not available) are returned as (revid, None).
883            N.B.: Revisions are not necessarily yielded in order.
884        """
885        raise NotImplementedError(self.iter_revisions)
886
887    def get_revision_delta(self, revision_id):
888        """Return the delta for one revision.
889
890        The delta is relative to the left-hand predecessor of the
891        revision.
892        """
893        with self.lock_read():
894            r = self.get_revision(revision_id)
895            return list(self.get_revision_deltas([r]))[0]
896
897    def get_revision_deltas(self, revisions, specific_files=None):
898        """Produce a generator of revision deltas.
899
900        Note that the input is a sequence of REVISIONS, not revision ids.
901        Trees will be held in memory until the generator exits.
902        Each delta is relative to the revision's lefthand predecessor.
903
904        specific_files should exist in the first revision.
905
906        :param specific_files: if not None, the result is filtered
907          so that only those files, their parents and their
908          children are included.
909        """
910        from .tree import InterTree
911        # Get the revision-ids of interest
912        required_trees = set()
913        for revision in revisions:
914            required_trees.add(revision.revision_id)
915            required_trees.update(revision.parent_ids[:1])
916
917        trees = {
918            t.get_revision_id(): t
919            for t in self.revision_trees(required_trees)}
920
921        # Calculate the deltas
922        for revision in revisions:
923            if not revision.parent_ids:
924                old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
925            else:
926                old_tree = trees[revision.parent_ids[0]]
927            intertree = InterTree.get(old_tree, trees[revision.revision_id])
928            yield intertree.compare(specific_files=specific_files)
929            if specific_files is not None:
930                specific_files = [
931                    p for p in intertree.find_source_paths(
932                        specific_files).values()
933                    if p is not None]
934
935    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
936        raise NotImplementedError(self.store_revision_signature)
937
938    def add_signature_text(self, revision_id, signature):
939        """Store a signature text for a revision.
940
941        :param revision_id: Revision id of the revision
942        :param signature: Signature text.
943        """
944        raise NotImplementedError(self.add_signature_text)
945
946    def iter_files_bytes(self, desired_files):
947        """Iterate through file versions.
948
949        Files will not necessarily be returned in the order they occur in
950        desired_files.  No specific order is guaranteed.
951
952        Yields pairs of identifier, bytes_iterator.  identifier is an opaque
953        value supplied by the caller as part of desired_files.  It should
954        uniquely identify the file version in the caller's context.  (Examples:
955        an index number or a TreeTransform trans_id.)
956
957        :param desired_files: a list of (file_id, revision_id, identifier)
958            triples
959        """
960        raise NotImplementedError(self.iter_files_bytes)
961
962    def get_rev_id_for_revno(self, revno, known_pair):
963        """Return the revision id of a revno, given a later (revno, revid)
964        pair in the same history.
965
966        :return: if found (True, revid).  If the available history ran out
967            before reaching the revno, then this returns
968            (False, (closest_revno, closest_revid)).
969        """
970        known_revno, known_revid = known_pair
971        partial_history = [known_revid]
972        distance_from_known = known_revno - revno
973        if distance_from_known < 0:
974            raise errors.RevnoOutOfBounds(revno, (0, known_revno))
975        try:
976            _iter_for_revno(
977                self, partial_history, stop_index=distance_from_known)
978        except errors.RevisionNotPresent as err:
979            if err.revision_id == known_revid:
980                # The start revision (known_revid) wasn't found.
981                raise errors.NoSuchRevision(self, known_revid)
982            # This is a stacked repository with no fallbacks, or a there's a
983            # left-hand ghost.  Either way, even though the revision named in
984            # the error isn't in this repo, we know it's the next step in this
985            # left-hand history.
986            partial_history.append(err.revision_id)
987        if len(partial_history) <= distance_from_known:
988            # Didn't find enough history to get a revid for the revno.
989            earliest_revno = known_revno - len(partial_history) + 1
990            return (False, (earliest_revno, partial_history[-1]))
991        if len(partial_history) - 1 > distance_from_known:
992            raise AssertionError('_iter_for_revno returned too much history')
993        return (True, partial_history[-1])
994
995    def is_shared(self):
996        """Return True if this repository is flagged as a shared repository."""
997        raise NotImplementedError(self.is_shared)
998
999    def reconcile(self, other=None, thorough=False):
1000        """Reconcile this repository."""
1001        raise NotImplementedError(self.reconcile)
1002
1003    def _refresh_data(self):
1004        """Helper called from lock_* to ensure coherency with disk.
1005
1006        The default implementation does nothing; it is however possible
1007        for repositories to maintain loaded indices across multiple locks
1008        by checking inside their implementation of this method to see
1009        whether their indices are still valid. This depends of course on
1010        the disk format being validatable in this manner. This method is
1011        also called by the refresh_data() public interface to cause a refresh
1012        to occur while in a write lock so that data inserted by a smart server
1013        push operation is visible on the client's instance of the physical
1014        repository.
1015        """
1016
1017    def revision_tree(self, revision_id):
1018        """Return Tree for a revision on this branch.
1019
1020        `revision_id` may be NULL_REVISION for the empty tree revision.
1021        """
1022        raise NotImplementedError(self.revision_tree)
1023
1024    def revision_trees(self, revision_ids):
1025        """Return Trees for revisions in this repository.
1026
1027        :param revision_ids: a sequence of revision-ids;
1028          a revision-id may not be None or b'null:'
1029        """
1030        raise NotImplementedError(self.revision_trees)
1031
1032    def pack(self, hint=None, clean_obsolete_packs=False):
1033        """Compress the data within the repository.
1034
1035        This operation only makes sense for some repository types. For other
1036        types it should be a no-op that just returns.
1037
1038        This stub method does not require a lock, but subclasses should use
1039        self.write_lock as this is a long running call it's reasonable to
1040        implicitly lock for the user.
1041
1042        :param hint: If not supplied, the whole repository is packed.
1043            If supplied, the repository may use the hint parameter as a
1044            hint for the parts of the repository to pack. A hint can be
1045            obtained from the result of commit_write_group(). Out of
1046            date hints are simply ignored, because concurrent operations
1047            can obsolete them rapidly.
1048
1049        :param clean_obsolete_packs: Clean obsolete packs immediately after
1050            the pack operation.
1051        """
1052
1053    def get_transaction(self):
1054        return self.control_files.get_transaction()
1055
1056    def get_parent_map(self, revision_ids):
1057        """See graph.StackedParentsProvider.get_parent_map"""
1058        raise NotImplementedError(self.get_parent_map)
1059
1060    def _get_parent_map_no_fallbacks(self, revision_ids):
1061        """Same as Repository.get_parent_map except doesn't query fallbacks."""
1062        # revisions index works in keys; this just works in revisions
1063        # therefore wrap and unwrap
1064        query_keys = []
1065        result = {}
1066        for revision_id in revision_ids:
1067            if revision_id == _mod_revision.NULL_REVISION:
1068                result[revision_id] = ()
1069            elif revision_id is None:
1070                raise ValueError('get_parent_map(None) is not valid')
1071            else:
1072                query_keys.append((revision_id,))
1073        vf = self.revisions.without_fallbacks()
1074        for (revision_id,), parent_keys in (
1075                vf.get_parent_map(query_keys).items()):
1076            if parent_keys:
1077                result[revision_id] = tuple([parent_revid
1078                                             for (parent_revid,) in parent_keys])
1079            else:
1080                result[revision_id] = (_mod_revision.NULL_REVISION,)
1081        return result
1082
1083    def _make_parents_provider(self):
1084        if not self._format.supports_external_lookups:
1085            return self
1086        return graph.StackedParentsProvider(_LazyListJoin(
1087            [self._make_parents_provider_unstacked()],
1088            self._fallback_repositories))
1089
1090    def _make_parents_provider_unstacked(self):
1091        return graph.CallableToParentsProviderAdapter(
1092            self._get_parent_map_no_fallbacks)
1093
1094    def get_known_graph_ancestry(self, revision_ids):
1095        """Return the known graph for a set of revision ids and their ancestors.
1096        """
1097        raise NotImplementedError(self.get_known_graph_ancestry)
1098
1099    def get_file_graph(self):
1100        """Return the graph walker for files."""
1101        raise NotImplementedError(self.get_file_graph)
1102
1103    def get_graph(self, other_repository=None):
1104        """Return the graph walker for this repository format"""
1105        parents_provider = self._make_parents_provider()
1106        if (other_repository is not None and
1107                not self.has_same_location(other_repository)):
1108            parents_provider = graph.StackedParentsProvider(
1109                [parents_provider, other_repository._make_parents_provider()])
1110        return graph.Graph(parents_provider)
1111
1112    def set_make_working_trees(self, new_value):
1113        """Set the policy flag for making working trees when creating branches.
1114
1115        This only applies to branches that use this repository.
1116
1117        The default is 'True'.
1118        :param new_value: True to restore the default, False to disable making
1119                          working trees.
1120        """
1121        raise NotImplementedError(self.set_make_working_trees)
1122
1123    def make_working_trees(self):
1124        """Returns the policy for making working trees on new branches."""
1125        raise NotImplementedError(self.make_working_trees)
1126
1127    def sign_revision(self, revision_id, gpg_strategy):
1128        raise NotImplementedError(self.sign_revision)
1129
1130    def verify_revision_signature(self, revision_id, gpg_strategy):
1131        """Verify the signature on a revision.
1132
1133        :param revision_id: the revision to verify
1134        :gpg_strategy: the GPGStrategy object to used
1135
1136        :return: gpg.SIGNATURE_VALID or a failed SIGNATURE_ value
1137        """
1138        raise NotImplementedError(self.verify_revision_signature)
1139
1140    def verify_revision_signatures(self, revision_ids, gpg_strategy):
1141        """Verify revision signatures for a number of revisions.
1142
1143        :param revision_id: the revision to verify
1144        :gpg_strategy: the GPGStrategy object to used
1145        :return: Iterator over tuples with revision id, result and keys
1146        """
1147        with self.lock_read():
1148            for revid in revision_ids:
1149                (result, key) = self.verify_revision_signature(revid, gpg_strategy)
1150                yield revid, result, key
1151
1152    def has_signature_for_revision_id(self, revision_id):
1153        """Query for a revision signature for revision_id in the repository."""
1154        raise NotImplementedError(self.has_signature_for_revision_id)
1155
1156    def get_signature_text(self, revision_id):
1157        """Return the text for a signature."""
1158        raise NotImplementedError(self.get_signature_text)
1159
1160    def check(self, revision_ids=None, callback_refs=None, check_repo=True):
1161        """Check consistency of all history of given revision_ids.
1162
1163        Different repository implementations should override _check().
1164
1165        :param revision_ids: A non-empty list of revision_ids whose ancestry
1166             will be checked.  Typically the last revision_id of a branch.
1167        :param callback_refs: A dict of check-refs to resolve and callback
1168            the check/_check method on the items listed as wanting the ref.
1169            see breezy.check.
1170        :param check_repo: If False do not check the repository contents, just
1171            calculate the data callback_refs requires and call them back.
1172        """
1173        return self._check(revision_ids=revision_ids, callback_refs=callback_refs,
1174                           check_repo=check_repo)
1175
1176    def _check(self, revision_ids=None, callback_refs=None, check_repo=True):
1177        raise NotImplementedError(self.check)
1178
1179    def _warn_if_deprecated(self, branch=None):
1180        if not self._format.is_deprecated():
1181            return
1182        global _deprecation_warning_done
1183        if _deprecation_warning_done:
1184            return
1185        try:
1186            if branch is None:
1187                conf = config.GlobalStack()
1188            else:
1189                conf = branch.get_config_stack()
1190            if 'format_deprecation' in conf.get('suppress_warnings'):
1191                return
1192            warning("Format %s for %s is deprecated -"
1193                    " please use 'brz upgrade' to get better performance"
1194                    % (self._format, self.controldir.transport.base))
1195        finally:
1196            _deprecation_warning_done = True
1197
1198    def supports_rich_root(self):
1199        return self._format.rich_root_data
1200
1201    def _check_ascii_revisionid(self, revision_id, method):
1202        """Private helper for ascii-only repositories."""
1203        # weave repositories refuse to store revisionids that are non-ascii.
1204        if revision_id is not None:
1205            # weaves require ascii revision ids.
1206            if isinstance(revision_id, str):
1207                try:
1208                    revision_id.encode('ascii')
1209                except UnicodeEncodeError:
1210                    raise errors.NonAsciiRevisionId(method, self)
1211            else:
1212                try:
1213                    revision_id.decode('ascii')
1214                except UnicodeDecodeError:
1215                    raise errors.NonAsciiRevisionId(method, self)
1216
1217
1218class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
1219    """Repository format registry."""
1220
1221    def get_default(self):
1222        """Return the current default format."""
1223        return controldir.format_registry.make_controldir('default').repository_format
1224
1225
1226network_format_registry = registry.FormatRegistry()
1227"""Registry of formats indexed by their network name.
1228
1229The network name for a repository format is an identifier that can be used when
1230referring to formats with smart server operations. See
1231RepositoryFormat.network_name() for more detail.
1232"""
1233
1234
1235format_registry = RepositoryFormatRegistry(network_format_registry)
1236"""Registry of formats, indexed by their BzrDirMetaFormat format string.
1237
1238This can contain either format instances themselves, or classes/factories that
1239can be called to obtain one.
1240"""
1241
1242
1243#####################################################################
1244# Repository Formats
1245
1246class RepositoryFormat(controldir.ControlComponentFormat):
1247    """A repository format.
1248
1249    Formats provide four things:
1250     * An initialization routine to construct repository data on disk.
1251     * a optional format string which is used when the BzrDir supports
1252       versioned children.
1253     * an open routine which returns a Repository instance.
1254     * A network name for referring to the format in smart server RPC
1255       methods.
1256
1257    There is one and only one Format subclass for each on-disk format. But
1258    there can be one Repository subclass that is used for several different
1259    formats. The _format attribute on a Repository instance can be used to
1260    determine the disk format.
1261
1262    Formats are placed in a registry by their format string for reference
1263    during opening. These should be subclasses of RepositoryFormat for
1264    consistency.
1265
1266    Once a format is deprecated, just deprecate the initialize and open
1267    methods on the format class. Do not deprecate the object, as the
1268    object may be created even when a repository instance hasn't been
1269    created.
1270
1271    Common instance attributes:
1272    _matchingcontroldir - the controldir format that the repository format was
1273    originally written to work with. This can be used if manually
1274    constructing a bzrdir and repository, or more commonly for test suite
1275    parameterization.
1276    """
1277
1278    # Set to True or False in derived classes. True indicates that the format
1279    # supports ghosts gracefully.
1280    supports_ghosts = None
1281    # Can this repository be given external locations to lookup additional
1282    # data. Set to True or False in derived classes.
1283    supports_external_lookups = None
1284    # Does this format support CHK bytestring lookups. Set to True or False in
1285    # derived classes.
1286    supports_chks = None
1287    # Should fetch trigger a reconcile after the fetch? Only needed for
1288    # some repository formats that can suffer internal inconsistencies.
1289    _fetch_reconcile = False
1290    # Does this format have < O(tree_size) delta generation. Used to hint what
1291    # code path for commit, amongst other things.
1292    fast_deltas = None
1293    # Does doing a pack operation compress data? Useful for the pack UI command
1294    # (so if there is one pack, the operation can still proceed because it may
1295    # help), and for fetching when data won't have come from the same
1296    # compressor.
1297    pack_compresses = False
1298    # Does the repository storage understand references to trees?
1299    supports_tree_reference = None
1300    # Is the format experimental ?
1301    experimental = False
1302    # Does this repository format escape funky characters, or does it create
1303    # files with similar names as the versioned files in its contents on disk
1304    # ?
1305    supports_funky_characters = None
1306    # Does this repository format support leaving locks?
1307    supports_leaving_lock = None
1308    # Does this format support the full VersionedFiles interface?
1309    supports_full_versioned_files = None
1310    # Does this format support signing revision signatures?
1311    supports_revision_signatures = True
1312    # Can the revision graph have incorrect parents?
1313    revision_graph_can_have_wrong_parents = None
1314    # Does this format support setting revision ids?
1315    supports_setting_revision_ids = True
1316    # Does this format support rich root data?
1317    rich_root_data = None
1318    # Does this format support explicitly versioned directories?
1319    supports_versioned_directories = None
1320    # Can other repositories be nested into one of this format?
1321    supports_nesting_repositories = None
1322    # Is it possible for revisions to be present without being referenced
1323    # somewhere ?
1324    supports_unreferenced_revisions = None
1325    # Does this format store the current Branch.nick in a revision when
1326    # creating commits?
1327    supports_storing_branch_nick = True
1328    # Does the format support overriding the transport to use
1329    supports_overriding_transport = True
1330    # Does the format support setting custom revision properties?
1331    supports_custom_revision_properties = True
1332    # Does the format record per-file revision metadata?
1333    records_per_file_revision = True
1334    supports_multiple_authors = True
1335
1336    def __repr__(self):
1337        return "%s()" % self.__class__.__name__
1338
1339    def __eq__(self, other):
1340        # format objects are generally stateless
1341        return isinstance(other, self.__class__)
1342
1343    def __ne__(self, other):
1344        return not self == other
1345
1346    def get_format_description(self):
1347        """Return the short description for this format."""
1348        raise NotImplementedError(self.get_format_description)
1349
1350    def initialize(self, controldir, shared=False):
1351        """Initialize a repository of this format in controldir.
1352
1353        :param controldir: The controldir to put the new repository in it.
1354        :param shared: The repository should be initialized as a sharable one.
1355        :returns: The new repository object.
1356
1357        This may raise UninitializableFormat if shared repository are not
1358        compatible the controldir.
1359        """
1360        raise NotImplementedError(self.initialize)
1361
1362    def is_supported(self):
1363        """Is this format supported?
1364
1365        Supported formats must be initializable and openable.
1366        Unsupported formats may not support initialization or committing or
1367        some other features depending on the reason for not being supported.
1368        """
1369        return True
1370
1371    def is_deprecated(self):
1372        """Is this format deprecated?
1373
1374        Deprecated formats may trigger a user-visible warning recommending
1375        the user to upgrade. They are still fully supported.
1376        """
1377        return False
1378
1379    def network_name(self):
1380        """A simple byte string uniquely identifying this format for RPC calls.
1381
1382        MetaDir repository formats use their disk format string to identify the
1383        repository over the wire. All in one formats such as bzr < 0.8, and
1384        foreign formats like svn/git and hg should use some marker which is
1385        unique and immutable.
1386        """
1387        raise NotImplementedError(self.network_name)
1388
1389    def check_conversion_target(self, target_format):
1390        if self.rich_root_data and not target_format.rich_root_data:
1391            raise errors.BadConversionTarget(
1392                'Does not support rich root data.', target_format,
1393                from_format=self)
1394        if (self.supports_tree_reference
1395                and not getattr(target_format, 'supports_tree_reference', False)):
1396            raise errors.BadConversionTarget(
1397                'Does not support nested trees', target_format,
1398                from_format=self)
1399
1400    def open(self, controldir, _found=False):
1401        """Return an instance of this format for a controldir.
1402
1403        _found is a private parameter, do not use it.
1404        """
1405        raise NotImplementedError(self.open)
1406
1407    def _run_post_repo_init_hooks(self, repository, controldir, shared):
1408        from .controldir import ControlDir, RepoInitHookParams
1409        hooks = ControlDir.hooks['post_repo_init']
1410        if not hooks:
1411            return
1412        params = RepoInitHookParams(repository, self, controldir, shared)
1413        for hook in hooks:
1414            hook(params)
1415
1416
1417# formats which have no format string are not discoverable or independently
1418# creatable on disk, so are not registered in format_registry.  They're
1419# all in breezy.bzr.knitreponow.  When an instance of one of these is
1420# needed, it's constructed directly by the ControlDir.  Non-native formats where
1421# the repository is not separately opened are similar.
1422
1423format_registry.register_lazy(
1424    b'Bazaar-NG Knit Repository Format 1',
1425    'breezy.bzr.knitrepo',
1426    'RepositoryFormatKnit1',
1427    )
1428
1429format_registry.register_lazy(
1430    b'Bazaar Knit Repository Format 3 (bzr 0.15)\n',
1431    'breezy.bzr.knitrepo',
1432    'RepositoryFormatKnit3',
1433    )
1434
1435format_registry.register_lazy(
1436    b'Bazaar Knit Repository Format 4 (bzr 1.0)\n',
1437    'breezy.bzr.knitrepo',
1438    'RepositoryFormatKnit4',
1439    )
1440
1441# Pack-based formats. There is one format for pre-subtrees, and one for
1442# post-subtrees to allow ease of testing.
1443# NOTE: These are experimental in 0.92. Stable in 1.0 and above
1444format_registry.register_lazy(
1445    b'Bazaar pack repository format 1 (needs bzr 0.92)\n',
1446    'breezy.bzr.knitpack_repo',
1447    'RepositoryFormatKnitPack1',
1448    )
1449format_registry.register_lazy(
1450    b'Bazaar pack repository format 1 with subtree support (needs bzr 0.92)\n',
1451    'breezy.bzr.knitpack_repo',
1452    'RepositoryFormatKnitPack3',
1453    )
1454format_registry.register_lazy(
1455    b'Bazaar pack repository format 1 with rich root (needs bzr 1.0)\n',
1456    'breezy.bzr.knitpack_repo',
1457    'RepositoryFormatKnitPack4',
1458    )
1459format_registry.register_lazy(
1460    b'Bazaar RepositoryFormatKnitPack5 (bzr 1.6)\n',
1461    'breezy.bzr.knitpack_repo',
1462    'RepositoryFormatKnitPack5',
1463    )
1464format_registry.register_lazy(
1465    b'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6.1)\n',
1466    'breezy.bzr.knitpack_repo',
1467    'RepositoryFormatKnitPack5RichRoot',
1468    )
1469format_registry.register_lazy(
1470    b'Bazaar RepositoryFormatKnitPack5RichRoot (bzr 1.6)\n',
1471    'breezy.bzr.knitpack_repo',
1472    'RepositoryFormatKnitPack5RichRootBroken',
1473    )
1474format_registry.register_lazy(
1475    b'Bazaar RepositoryFormatKnitPack6 (bzr 1.9)\n',
1476    'breezy.bzr.knitpack_repo',
1477    'RepositoryFormatKnitPack6',
1478    )
1479format_registry.register_lazy(
1480    b'Bazaar RepositoryFormatKnitPack6RichRoot (bzr 1.9)\n',
1481    'breezy.bzr.knitpack_repo',
1482    'RepositoryFormatKnitPack6RichRoot',
1483    )
1484format_registry.register_lazy(
1485    b'Bazaar repository format 2a (needs bzr 1.16 or later)\n',
1486    'breezy.bzr.groupcompress_repo',
1487    'RepositoryFormat2a',
1488    )
1489
1490# Development formats.
1491# Check their docstrings to see if/when they are obsolete.
1492format_registry.register_lazy(
1493    (b"Bazaar development format 2 with subtree support "
1494        b"(needs bzr.dev from before 1.8)\n"),
1495    'breezy.bzr.knitpack_repo',
1496    'RepositoryFormatPackDevelopment2Subtree',
1497    )
1498format_registry.register_lazy(
1499    b'Bazaar development format 8\n',
1500    'breezy.bzr.groupcompress_repo',
1501    'RepositoryFormat2aSubtree',
1502    )
1503
1504
1505class InterRepository(InterObject):
1506    """This class represents operations taking place between two repositories.
1507
1508    Its instances have methods like copy_content and fetch, and contain
1509    references to the source and target repositories these operations can be
1510    carried out on.
1511
1512    Often we will provide convenience methods on 'repository' which carry out
1513    operations with another repository - they will always forward to
1514    InterRepository.get(other).method_name(parameters).
1515    """
1516
1517    _optimisers = []
1518    """The available optimised InterRepository types."""
1519
1520    def copy_content(self, revision_id=None):
1521        """Make a complete copy of the content in self into destination.
1522
1523        This is a destructive operation! Do not use it on existing
1524        repositories.
1525
1526        :param revision_id: Only copy the content needed to construct
1527                            revision_id and its parents.
1528        """
1529        with self.lock_write():
1530            try:
1531                self.target.set_make_working_trees(
1532                    self.source.make_working_trees())
1533            except (NotImplementedError, errors.RepositoryUpgradeRequired):
1534                pass
1535            self.target.fetch(self.source, revision_id=revision_id)
1536
1537    def fetch(self, revision_id=None, find_ghosts=False, lossy=False):
1538        """Fetch the content required to construct revision_id.
1539
1540        The content is copied from self.source to self.target.
1541
1542        :param revision_id: if None all content is copied, if NULL_REVISION no
1543                            content is copied.
1544        :return: FetchResult
1545        """
1546        raise NotImplementedError(self.fetch)
1547
1548    def search_missing_revision_ids(
1549            self, find_ghosts=True, revision_ids=None, if_present_ids=None,
1550            limit=None):
1551        """Return the revision ids that source has that target does not.
1552
1553        :param revision_ids: return revision ids included by these
1554            revision_ids.  NoSuchRevision will be raised if any of these
1555            revisions are not present.
1556        :param if_present_ids: like revision_ids, but will not cause
1557            NoSuchRevision if any of these are absent, instead they will simply
1558            not be in the result.  This is useful for e.g. finding revisions
1559            to fetch for tags, which may reference absent revisions.
1560        :param find_ghosts: If True find missing revisions in deep history
1561            rather than just finding the surface difference.
1562        :param limit: Maximum number of revisions to return, topologically
1563            ordered
1564        :return: A breezy.graph.SearchResult.
1565        """
1566        raise NotImplementedError(self.search_missing_revision_ids)
1567
1568    @staticmethod
1569    def _same_model(source, target):
1570        """True if source and target have the same data representation.
1571
1572        Note: this is always called on the base class; overriding it in a
1573        subclass will have no effect.
1574        """
1575        try:
1576            InterRepository._assert_same_model(source, target)
1577            return True
1578        except errors.IncompatibleRepositories as e:
1579            return False
1580
1581    @staticmethod
1582    def _assert_same_model(source, target):
1583        """Raise an exception if two repositories do not use the same model.
1584        """
1585        if source.supports_rich_root() != target.supports_rich_root():
1586            raise errors.IncompatibleRepositories(source, target,
1587                                                  "different rich-root support")
1588        if source._serializer != target._serializer:
1589            raise errors.IncompatibleRepositories(source, target,
1590                                                  "different serializers")
1591
1592
1593class CopyConverter(object):
1594    """A repository conversion tool which just performs a copy of the content.
1595
1596    This is slow but quite reliable.
1597    """
1598
1599    def __init__(self, target_format):
1600        """Create a CopyConverter.
1601
1602        :param target_format: The format the resulting repository should be.
1603        """
1604        self.target_format = target_format
1605
1606    def convert(self, repo, pb):
1607        """Perform the conversion of to_convert, giving feedback via pb.
1608
1609        :param to_convert: The disk object to convert.
1610        :param pb: a progress bar to use for progress information.
1611        """
1612        with ui.ui_factory.nested_progress_bar() as pb:
1613            self.count = 0
1614            self.total = 4
1615            # this is only useful with metadir layouts - separated repo content.
1616            # trigger an assertion if not such
1617            repo._format.get_format_string()
1618            self.repo_dir = repo.controldir
1619            pb.update(gettext('Moving repository to repository.backup'))
1620            self.repo_dir.transport.move('repository', 'repository.backup')
1621            backup_transport = self.repo_dir.transport.clone(
1622                'repository.backup')
1623            repo._format.check_conversion_target(self.target_format)
1624            self.source_repo = repo._format.open(self.repo_dir,
1625                                                 _found=True,
1626                                                 _override_transport=backup_transport)
1627            pb.update(gettext('Creating new repository'))
1628            converted = self.target_format.initialize(self.repo_dir,
1629                                                      self.source_repo.is_shared())
1630            with converted.lock_write():
1631                pb.update(gettext('Copying content'))
1632                self.source_repo.copy_content_into(converted)
1633            pb.update(gettext('Deleting old repository content'))
1634            self.repo_dir.transport.delete_tree('repository.backup')
1635            ui.ui_factory.note(gettext('repository converted'))
1636
1637
1638def _strip_NULL_ghosts(revision_graph):
1639    """Also don't use this. more compatibility code for unmigrated clients."""
1640    # Filter ghosts, and null:
1641    if _mod_revision.NULL_REVISION in revision_graph:
1642        del revision_graph[_mod_revision.NULL_REVISION]
1643    for key, parents in revision_graph.items():
1644        revision_graph[key] = tuple(parent for parent in parents if parent
1645                                    in revision_graph)
1646    return revision_graph
1647
1648
1649def _iter_for_revno(repo, partial_history_cache, stop_index=None,
1650                    stop_revision=None):
1651    """Extend the partial history to include a given index
1652
1653    If a stop_index is supplied, stop when that index has been reached.
1654    If a stop_revision is supplied, stop when that revision is
1655    encountered.  Otherwise, stop when the beginning of history is
1656    reached.
1657
1658    :param stop_index: The index which should be present.  When it is
1659        present, history extension will stop.
1660    :param stop_revision: The revision id which should be present.  When
1661        it is encountered, history extension will stop.
1662    """
1663    start_revision = partial_history_cache[-1]
1664    graph = repo.get_graph()
1665    iterator = graph.iter_lefthand_ancestry(start_revision,
1666                                            (_mod_revision.NULL_REVISION,))
1667    try:
1668        # skip the last revision in the list
1669        next(iterator)
1670        while True:
1671            if (stop_index is not None and
1672                    len(partial_history_cache) > stop_index):
1673                break
1674            if partial_history_cache[-1] == stop_revision:
1675                break
1676            revision_id = next(iterator)
1677            partial_history_cache.append(revision_id)
1678    except StopIteration:
1679        # No more history
1680        return
1681
1682
1683class _LazyListJoin(object):
1684    """An iterable yielding the contents of many lists as one list.
1685
1686    Each iterator made from this will reflect the current contents of the lists
1687    at the time the iterator is made.
1688
1689    This is used by Repository's _make_parents_provider implementation so that
1690    it is safe to do::
1691
1692      pp = repo._make_parents_provider()      # uses a list of fallback repos
1693      pp.add_fallback_repository(other_repo)  # appends to that list
1694      result = pp.get_parent_map(...)
1695      # The result will include revs from other_repo
1696    """
1697
1698    def __init__(self, *list_parts):
1699        self.list_parts = list_parts
1700
1701    def __iter__(self):
1702        full_list = []
1703        for list_part in self.list_parts:
1704            full_list.extend(list_part)
1705        return iter(full_list)
1706
1707    def __repr__(self):
1708        return "%s.%s(%s)" % (self.__module__, self.__class__.__name__,
1709                              self.list_parts)
1710