hgext/narrow/narrowmerge.py
author Augie Fackler <augie@google.com>
Mon, 29 Jan 2018 16:19:33 -0500
changeset 36079 a2a6e724d61a
child 36165 53fe5a1a92bd
permissions -rw-r--r--
narrow: import experimental extension from narrowhg revision cb51d673e9c5 Adjustments: * renamed src to hgext/narrow * marked extension experimental * added correct copyright header where it was missing * updated hgrc extension enable line in library.sh * renamed library.sh to narrow-library.sh * dropped all files from repo root as they're not interesting * dropped test-pyflakes.t, test-check-code.t and test-check-py3-compat.t * renamed remaining tests to all be test-narrow-* when they didn't already * fixed test-narrow-expanddirstate.t to refer to narrow and not narrowhg * fixed tests that wanted `update -C .` instead of `merge --abort` * corrected a two-space indent in narrowspec.py * added a missing _() in narrowcommands.py * fixed imports to pass the import checker * narrow only adds its --include and --exclude to clone if sparse isn't enabled to avoid breaking test-duplicateoptions.py. This is a kludge, and we'll need to come up with a better solution in the future. These were more or less the minimum to import something that would pass tests and not create a bunch of files we'll never use. Changes I intend to make as followups: * rework the test-narrow-*-tree.t tests to use the new testcases functionality in run-tests.py * remove lots of monkeypatches of core things Differential Revision: https://phab.mercurial-scm.org/D1974

# narrowmerge.py - extensions to mercurial merge module to support narrow clones
#
# Copyright 2017 Google, Inc.
#
# 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 mercurial.i18n import _
from mercurial import (
    copies,
    error,
    extensions,
    merge,
    util,
)

def setup():
    def _manifestmerge(orig, repo, wctx, p2, pa, branchmerge, *args, **kwargs):
        """Filter updates to only lay out files that match the narrow spec."""
        actions, diverge, renamedelete = orig(
            repo, wctx, p2, pa, branchmerge, *args, **kwargs)

        if not util.safehasattr(repo, 'narrowmatch'):
            return actions, diverge, renamedelete

        nooptypes = set(['k']) # TODO: handle with nonconflicttypes
        nonconflicttypes = set('a am c cm f g r e'.split())
        narrowmatch = repo.narrowmatch()
        for f, action in actions.items():
            if narrowmatch(f):
                pass
            elif not branchmerge:
                del actions[f] # just updating, ignore changes outside clone
            elif action[0] in nooptypes:
                del actions[f] # merge does not affect file
            elif action[0] in nonconflicttypes:
                raise error.Abort(_('merge affects file \'%s\' outside narrow, '
                                    'which is not yet supported') % f,
                                  hint=_('merging in the other direction '
                                         'may work'))
            else:
                raise error.Abort(_('conflict in file \'%s\' is outside '
                                    'narrow clone') % f)

        return actions, diverge, renamedelete

    extensions.wrapfunction(merge, 'manifestmerge', _manifestmerge)

    def _checkcollision(orig, repo, wmf, actions):
        if util.safehasattr(repo, 'narrowmatch'):
            narrowmatch = repo.narrowmatch()
            wmf = wmf.matches(narrowmatch)
            if actions:
                narrowactions = {}
                for m, actionsfortype in actions.iteritems():
                    narrowactions[m] = []
                    for (f, args, msg) in actionsfortype:
                        if narrowmatch(f):
                            narrowactions[m].append((f, args, msg))
                actions = narrowactions
        return orig(repo, wmf, actions)

    extensions.wrapfunction(merge, '_checkcollision', _checkcollision)

    def _computenonoverlap(orig, repo, *args, **kwargs):
        u1, u2 = orig(repo, *args, **kwargs)
        if not util.safehasattr(repo, 'narrowmatch'):
            return u1, u2

        narrowmatch = repo.narrowmatch()
        u1 = [f for f in u1 if narrowmatch(f)]
        u2 = [f for f in u2 if narrowmatch(f)]
        return u1, u2
    extensions.wrapfunction(copies, '_computenonoverlap', _computenonoverlap)