view mercurial/scmposix.py @ 51637:3cf9e52f5e27 stable

inline-changelog: fix a critical bug in write_pending that delete data Since a93e52f0b6ff we no longer use inline-revlog for the changelog. The goal there was to solve the lack of testing for the two variants (inline vs split) and reduce the complexity of the interaction with "diverted-write" on the changelog level. However many existing repository still have inline-changelog and we automatically move them to normal revlog as soon as we have the chances. Unfortunately This conversion is buggy and can result in the destruction of the changelog.i if hook triggers the "write pending" mechanism. The bugs comes from the "revlog splitting" logic and the "write_pending" logic stepping over each other. Ironically the change in a93e52f0b6ff aims at no longer having this kind of problem. This changesets fix this issue and add associated tests. Fixing this reveal that the transaction hooks end up not seeing the pending transaction content, because the name is not right ("changelog.i.s.a" instead of "changelog.i.s") we fix this in the next changeset.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Wed, 12 Jun 2024 02:15:20 +0200
parents 9d3721552b6c
children a4b3b8dee0a8
line wrap: on
line source

import array
import errno
import fcntl
import os
import sys
import typing

from typing import (
    List,
    Tuple,
)

from . import (
    encoding,
    pycompat,
    util,
)

if typing.TYPE_CHECKING:
    from . import ui as uimod

# BSD 'more' escapes ANSI color sequences by default. This can be disabled by
# $MORE variable, but there's no compatible option with Linux 'more'. Given
# OS X is widely used and most modern Unix systems would have 'less', setting
# 'less' as the default seems reasonable.
fallbackpager = b'less'


def _rcfiles(path: bytes) -> List[bytes]:
    rcs = [os.path.join(path, b'hgrc')]
    rcdir = os.path.join(path, b'hgrc.d')
    try:
        rcs.extend(
            [
                os.path.join(rcdir, f)
                for f, kind in sorted(util.listdir(rcdir))
                if f.endswith(b".rc")
            ]
        )
    except OSError:
        pass
    return rcs


def systemrcpath() -> List[bytes]:
    path = []
    if pycompat.sysplatform == b'plan9':
        root = b'lib/mercurial'
    else:
        root = b'etc/mercurial'
    # old mod_python does not set sys.argv
    if len(getattr(sys, 'argv', [])) > 0:
        p = os.path.dirname(os.path.dirname(pycompat.sysargv[0]))
        if p != b'/':
            path.extend(_rcfiles(os.path.join(p, root)))
    path.extend(_rcfiles(b'/' + root))
    return path


def userrcpath() -> List[bytes]:
    if pycompat.sysplatform == b'plan9':
        return [encoding.environ[b'home'] + b'/lib/hgrc']
    elif pycompat.isdarwin:
        return [os.path.expanduser(b'~/.hgrc')]
    else:
        confighome = encoding.environ.get(b'XDG_CONFIG_HOME')
        if confighome is None or not os.path.isabs(confighome):
            confighome = os.path.expanduser(b'~/.config')

        return [
            os.path.expanduser(b'~/.hgrc'),
            os.path.join(confighome, b'hg', b'hgrc'),
        ]


def termsize(ui: "uimod.ui") -> Tuple[int, int]:
    try:
        import termios

        TIOCGWINSZ = termios.TIOCGWINSZ  # unavailable on IRIX (issue3449)
    except (AttributeError, ImportError):
        return 80, 24

    for dev in (ui.ferr, ui.fout, ui.fin):
        try:
            try:
                fd = dev.fileno()
            except AttributeError:
                continue
            if not os.isatty(fd):
                continue
            arri = fcntl.ioctl(fd, TIOCGWINSZ, b'\0' * 8)
            height, width = array.array('h', arri)[:2]
            if width > 0 and height > 0:
                return width, height
        except ValueError:
            pass
        except IOError as e:
            if e.errno == errno.EINVAL:
                pass
            else:
                raise
    return 80, 24