contrib/simplemerge
author FUJIWARA Katsunori <foozy@lares.dti.ne.jp>
Tue, 04 Jul 2017 23:13:46 +0900
changeset 33278 87bca10a06ed
parent 30576 541949a10a68
child 33895 aed91971d88c
permissions -rwxr-xr-x
transaction: avoid file stat ambiguity only for files in blacklist Advancing mtime by os.utime() fails for EPERM, if the target file is owned by another. bff5ccbe5ead and related changes made some code paths give advancing mtime up in such case, to fix issue5418. This causes file stat ambiguity (again), if it is owned by another. https://www.mercurial-scm.org/wiki/ExactCacheValidationPlan To avoid file stat ambiguity in such case, especially for .hg/dirstate, ed66ec39933f made vfs.rename() copy the target file, and advance mtime of renamed one again, if EPERM (see issue5584 for detail). But straightforward "copy if EPERM" isn't reasonable for truncation of append-only files at rollbacking, because rollbacking might cost much for truncation of many filelogs, even though filelogs aren't filecache-ed. Therefore, this patch introduces blacklist "checkambigfiles", and avoids file stat ambiguity only for files specified in this blacklist. This patch consists of two parts below, which should be applied at once in order to avoid regression. - specify 'checkambig=True' at vfs.open(mode='a') in _playback() according to checkambigfiles - invoke _playback() with checkambigfiles - add transaction.__init__() checkambigfiles argument, for _abort() - make localrepo instantiate transaction with _cachedfiles - add rollback() checkambigfiles argument, for "hg rollback/recover" - make localrepo invoke rollback() with _cachedfiles After this patch, straightforward "copy if EPERM" will be reasonable at closing the file opened with checkambig=True, because this policy is applied only on files, which are listed in blacklist "checkambigfiles".

#!/usr/bin/env python

from mercurial import demandimport
demandimport.enable()

import getopt
import sys
from mercurial.i18n import _
from mercurial import error, simplemerge, fancyopts, util, ui

options = [('L', 'label', [], _('labels to use on conflict markers')),
           ('a', 'text', None, _('treat all files as text')),
           ('p', 'print', None,
            _('print results instead of overwriting LOCAL')),
           ('', 'no-minimal', None, _('no effect (DEPRECATED)')),
           ('h', 'help', None, _('display help and exit')),
           ('q', 'quiet', None, _('suppress output'))]

usage = _('''simplemerge [OPTS] LOCAL BASE OTHER

    Simple three-way file merge utility with a minimal feature set.

    Apply to LOCAL the changes necessary to go from BASE to OTHER.

    By default, LOCAL is overwritten with the results of this operation.
''')

class ParseError(Exception):
    """Exception raised on errors in parsing the command line."""

def showhelp():
    sys.stdout.write(usage)
    sys.stdout.write('\noptions:\n')

    out_opts = []
    for shortopt, longopt, default, desc in options:
        out_opts.append(('%2s%s' % (shortopt and '-%s' % shortopt,
                                    longopt and ' --%s' % longopt),
                         '%s' % desc))
    opts_len = max([len(opt[0]) for opt in out_opts])
    for first, second in out_opts:
        sys.stdout.write(' %-*s  %s\n' % (opts_len, first, second))

try:
    for fp in (sys.stdin, sys.stdout, sys.stderr):
        util.setbinary(fp)

    opts = {}
    try:
        args = fancyopts.fancyopts(sys.argv[1:], options, opts)
    except getopt.GetoptError as e:
        raise ParseError(e)
    if opts['help']:
        showhelp()
        sys.exit(0)
    if len(args) != 3:
            raise ParseError(_('wrong number of arguments'))
    sys.exit(simplemerge.simplemerge(ui.ui.load(), *args, **opts))
except ParseError as e:
    sys.stdout.write("%s: %s\n" % (sys.argv[0], e))
    showhelp()
    sys.exit(1)
except error.Abort as e:
    sys.stderr.write("abort: %s\n" % e)
    sys.exit(255)
except KeyboardInterrupt:
    sys.exit(255)