tests/fakedirstatewritetime.py
author Anton Shestakov <av6@dwimlabs.net>
Mon, 31 Jan 2022 18:13:00 +0300
changeset 48789 ef50a62eec40
parent 48398 111098af6356
child 48875 6000f5b25c9b
permissions -rw-r--r--
obsolete: don't use os.stat in repo.obsstore.__nonzero__ if it's static HTTP If a repo is accessed via static HTTP, then we obviously can't use os.stat() to just peek at the file size. Let's download the entire file to check its size. Yes, this feels wasteful, but: 1. If we're cloning or pulling a repo from a static HTTP server, we need the contents of the obsstore anyway. 2. Implementing statichttpvfs.stat() that uses HEAD will result in one more request to a static-only HTTP server, which is already slow. Also parsing a response to a HEAD request to construct os.stat_result is pretty hacky. There's also a question of the remote server properly supporting HEAD method and reporting at least file size. 3. Implementing statichttpvfs.stat() that uses GET is pretty much the same thing as we do here, except we can't even cache the response easily, unlike simply accessing obsstore._data, which is @propertycache'd. Importing statichttprepo locally to avoid circular import. See also: 4507bc001365 and commit message of f8f2ecdde4b5. Differential Revision: https://phab.mercurial-scm.org/D12195

# extension to emulate invoking 'dirstate.write()' at the time
# specified by '[fakedirstatewritetime] fakenow', only when
# 'dirstate.write()' is invoked via functions below:
#
#   - 'workingctx._poststatusfixup()' (= 'repo.status()')
#   - 'committablectx.markcommitted()'

from __future__ import absolute_import

from mercurial import (
    context,
    dirstatemap as dirstatemapmod,
    extensions,
    policy,
    registrar,
)
from mercurial.dirstateutils import timestamp
from mercurial.utils import dateutil

try:
    from mercurial import rustext

    rustext.__name__  # force actual import (see hgdemandimport)
except ImportError:
    rustext = None

configtable = {}
configitem = registrar.configitem(configtable)

configitem(
    b'fakedirstatewritetime',
    b'fakenow',
    default=None,
)

parsers = policy.importmod('parsers')
has_rust_dirstate = policy.importrust('dirstate') is not None


def pack_dirstate(orig, dmap, copymap, pl):
    return orig(dmap, copymap, pl)


def fakewrite(ui, func):
    # fake "now" of 'pack_dirstate' only if it is invoked while 'func'

    fakenow = ui.config(b'fakedirstatewritetime', b'fakenow')
    if not fakenow:
        # Execute original one, if fakenow isn't configured. This is
        # useful to prevent subrepos from executing replaced one,
        # because replacing 'parsers.pack_dirstate' is also effective
        # in subrepos.
        return func()

    # parsing 'fakenow' in YYYYmmddHHMM format makes comparison between
    # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy
    fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0]
    fakenow = timestamp.timestamp((fakenow, 0, False))

    if has_rust_dirstate:
        # The Rust implementation does not use public parse/pack dirstate
        # to prevent conversion round-trips
        orig_dirstatemap_write = dirstatemapmod.dirstatemap.write
        wrapper = lambda self, tr, st: orig_dirstatemap_write(self, tr, st)
        dirstatemapmod.dirstatemap.write = wrapper

    orig_get_fs_now = timestamp.get_fs_now
    wrapper = lambda *args: pack_dirstate(orig_pack_dirstate, *args)

    orig_module = parsers
    orig_pack_dirstate = parsers.pack_dirstate

    orig_module.pack_dirstate = wrapper
    timestamp.get_fs_now = (
        lambda *args: fakenow
    )  # XXX useless for this purpose now
    try:
        return func()
    finally:
        orig_module.pack_dirstate = orig_pack_dirstate
        timestamp.get_fs_now = orig_get_fs_now
        if has_rust_dirstate:
            dirstatemapmod.dirstatemap.write = orig_dirstatemap_write


def _poststatusfixup(orig, workingctx, status, fixup):
    ui = workingctx.repo().ui
    return fakewrite(ui, lambda: orig(workingctx, status, fixup))


def markcommitted(orig, committablectx, node):
    ui = committablectx.repo().ui
    return fakewrite(ui, lambda: orig(committablectx, node))


def extsetup(ui):
    extensions.wrapfunction(
        context.workingctx, '_poststatusfixup', _poststatusfixup
    )
    extensions.wrapfunction(context.workingctx, 'markcommitted', markcommitted)