diff -r c80544aa4971 -r f3aafd785e65 mercurial/filemerge.py --- a/mercurial/filemerge.py Mon Mar 21 10:55:50 2022 +0100 +++ b/mercurial/filemerge.py Tue Jan 18 13:05:21 2022 -0800 @@ -1051,6 +1051,7 @@ markerstyle = internalmarkerstyle if mergetype == fullmerge: + _run_partial_resolution_tools(repo, local, other, base) # conflict markers generated by premerge will use 'detailed' # settings if either ui.mergemarkers or the tool's mergemarkers # setting is 'detailed'. This way tools can have basic labels in @@ -1115,6 +1116,62 @@ backup.remove() +def _run_partial_resolution_tools(repo, local, other, base): + """Runs partial-resolution tools on the three inputs and updates them.""" + ui = repo.ui + # Tuples of (order, name, executable path) + tools = [] + seen = set() + section = b"partial-merge-tools" + for k, v in ui.configitems(section): + name = k.split(b'.')[0] + if name in seen: + continue + patterns = ui.configlist(section, b'%s.patterns' % name, []) + is_match = True + if patterns: + m = match.match(repo.root, b'', patterns) + is_match = m(local.fctx.path()) + if is_match: + order = ui.configint(section, b'%s.order' % name, 0) + executable = ui.config(section, b'%s.executable' % name, name) + tools.append((order, name, executable)) + + if not tools: + return + # Sort in configured order (first in tuple) + tools.sort() + + files = [ + (b"local", local.fctx.path(), local.text()), + (b"base", base.fctx.path(), base.text()), + (b"other", other.fctx.path(), other.text()), + ] + + with _maketempfiles(files) as temppaths: + localpath, basepath, otherpath = temppaths + + for order, name, executable in tools: + cmd = procutil.shellquote(executable) + # TODO: Allow the user to configure the command line using + # $local, $base, $other. + cmd = b'%s %s %s %s' % (cmd, localpath, basepath, otherpath) + r = ui.system(cmd, cwd=repo.root, blockedtag=b'partial-mergetool') + if r: + raise error.StateError( + b'partial merge tool %s exited with code %d' % (name, r) + ) + local_text = util.readfile(localpath) + other_text = util.readfile(otherpath) + if local_text == other_text: + # No need to run other tools if all conflicts have been resolved + break + + local.set_text(local_text) + base.set_text(util.readfile(basepath)) + other.set_text(other_text) + + def _haltmerge(): msg = _(b'merge halted after failed merge (see hg resolve)') raise error.InterventionRequired(msg)