mercurial/filemerge.py
changeset 48981 f3aafd785e65
parent 48971 4057563ebc6b
child 48982 9dfbea54b680
--- 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)