I am using Mercurial (on a Debian system) with Diffuse as the merge tool of choice for handling 3-way merges. However, with the default setup from thg's mergetools.rc (at https://foss.heptapod.net/mercurial/tortoisehg/thg/-/blob/8d587fe951d9dd9ad41f532151a0d17372535d01/contrib/mergetools.rc#L243 ), Mercurial passes $local in the first pane, $base in the second pane, and $other in the third pane. $local is the actual file in the repository, both of the others are files in /tmp with names like "FILENAME~base.XXXXXX.EXT" respectively "FILENAME~other.XXXXXX.EXT".
Now from how I understand 3-way merges generally and Diffuse specifically, including the fact that Diffuse defaults to focus the middle pane on startup, it seems to me I'm supposed to pull changes from the (left←) first pane (local) and (right→) last pane (other) into the middle pane (base), then save that as the target file in the actual repo.
This target file, as mentioned, is actually passed by hg into the first pane. Therefore, to do that with the default setup, I need to "Save File As..." the middle pane and then manually enter the pathname of the target file. At least, that's what I have done until today. This is obviously uncomfortable.
Some other merge tools seem to allow specifying an output (target) filename with an "-o" switch, but Diffuse doesn't have such a switch. So, my question is, how am I supposed to set up and use Diffuse with Mercurial to avoid this uncomfortable Save File As dance?
I created a script that swaps the files and then calls Diffuse with the names that (to hg) are $base $local $other, in that order. This means the middle pane receives the "$local" filename (really BASE then), which is the target that I want to save the middle pane into. It's at https://hg.pushbx.org/ecm/diffuse-swap/rev/fdd8357ab5a1fb7e1743e169fa11c3ec7f635e97 (This link now requires signing in as username anonymous, any nonempty password.)
Script in full:
#! /bin/bash
# Script to call diffuse with swapped $local and $base files.
# Needed because Mercurial passes the actual file to be modified
# in the first parameter, $local, but we want to pull changes
# into the middle pane, which is $base. We swap the files
# then pass $base $local $other to diffuse.
# 2019, by C. Masloch
# Usage of the works is permitted provided that this
# instrument is retained with the works, so that any entity
# that uses the works is notified of this instrument.
#
# DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
: <<'END_COMMENT'
This is what needs to be put into .hgrc to use this script:
Based on diffuse entry in thg 8d587fe951d9 contrib/mergetools.rc
[merge-tools]
diffuse-swap.priority=-2
diffuse-swap.executable=diffuse-swap.sh
diffuse-swap.args=3WAYMERGE $local $base $other
diffuse-swap.gui=True
diffuse-swap.diffargs=$parent $child
diffuse-swap.diff3args=$parent1 $child $parent2
diffuse-swap.dirdiff=False
END_COMMENT
# From this reply to the question "Shortest way to swap two files in bash":
# https://stackoverflow.com/a/11075489/738287
function swap()
{
tmpfile=$(mktemp $(dirname "$1")/XXXXXX)
mv "$1" "$tmpfile" && mv "$2" "$1" && mv "$tmpfile" "$2"
}
if [[ "$1" == 3WAYMERGE ]]; then {
basename="$(basename "$2")"
swap "$2" "$3" && diffuse \
-L "LOCAL-$basename" "$3" \
-L "BASE-$basename" "$2" \
-L "OTHER-$basename" "$4"
} else {
diffuse "$@"
} fi
(I'd pondered asking the question here before settling on this solution. I didn't find anyone else having this problem on the web, so I want to provide my question and answer for anyone else with the same problem. That's why I am answering my own question.)