view mercurial/dirstateguard.py @ 45637:ad6ebb6f0dfe

copies: make two version of the changeset centric algorithm They are two main ways to run the changeset-centric copy-tracing algorithm. One fed from data stored in side-data and still in development, and one based on data stored in extra (with a "compatibility" mode). The `extra` based is used in production at Google, but still experimental in code. It is mostly unsuitable for other users because it affects the hash. The side-data based storage and algorithm have been evolving to store more data, cover more cases (mostly around merge, that Google do not really care about) and use lower level storage for efficiency. All this changes make is increasingly hard to maintain de common code base, without impacting code complexity and performance. For example, the compatibility mode requires to keep things at different level than what we need for side-data. So, I am duplicating the involved functions. The newly added `_extra` variants will be kept as today, while I will do some deeper rework of the side data versions. Long terms, the side-data version should be more featureful and performant than the extra based version, so I expect the duplicated `_extra` functions to eventually get dropped. Differential Revision: https://phab.mercurial-scm.org/D9114
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Fri, 25 Sep 2020 14:39:04 +0200
parents 687b865b95ad
children 89a2afe31e82
line wrap: on
line source

# dirstateguard.py - class to allow restoring dirstate after failure
#
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

from __future__ import absolute_import

from .i18n import _

from . import (
    error,
    narrowspec,
    util,
)


class dirstateguard(util.transactional):
    '''Restore dirstate at unexpected failure.

    At the construction, this class does:

    - write current ``repo.dirstate`` out, and
    - save ``.hg/dirstate`` into the backup file

    This restores ``.hg/dirstate`` from backup file, if ``release()``
    is invoked before ``close()``.

    This just removes the backup file at ``close()`` before ``release()``.
    '''

    def __init__(self, repo, name):
        self._repo = repo
        self._active = False
        self._closed = False
        self._backupname = b'dirstate.backup.%s.%d' % (name, id(self))
        self._narrowspecbackupname = b'narrowspec.backup.%s.%d' % (
            name,
            id(self),
        )
        repo.dirstate.savebackup(repo.currenttransaction(), self._backupname)
        narrowspec.savewcbackup(repo, self._narrowspecbackupname)
        self._active = True

    def __del__(self):
        if self._active:  # still active
            # this may occur, even if this class is used correctly:
            # for example, releasing other resources like transaction
            # may raise exception before ``dirstateguard.release`` in
            # ``release(tr, ....)``.
            self._abort()

    def close(self):
        if not self._active:  # already inactivated
            msg = (
                _(b"can't close already inactivated backup: %s")
                % self._backupname
            )
            raise error.Abort(msg)

        self._repo.dirstate.clearbackup(
            self._repo.currenttransaction(), self._backupname
        )
        narrowspec.clearwcbackup(self._repo, self._narrowspecbackupname)
        self._active = False
        self._closed = True

    def _abort(self):
        narrowspec.restorewcbackup(self._repo, self._narrowspecbackupname)
        self._repo.dirstate.restorebackup(
            self._repo.currenttransaction(), self._backupname
        )
        self._active = False

    def release(self):
        if not self._closed:
            if not self._active:  # already inactivated
                msg = (
                    _(b"can't release already inactivated backup: %s")
                    % self._backupname
                )
                raise error.Abort(msg)
            self._abort()