# [PATCH v2] mergetools: add winmerge as a builtin tool

14 messages from 2015-05-13 to 2015-05-20. Participants: David Aguilar, Johannes Schindelin, Sebastian Schuberth, SZEDER Gábor.
Thread: https://gitlist.dev/t/39318

## David Aguilar, 2015-05-13 02:00

Subject: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <1431482407-63642-1-git-send-email-davvid@gmail.com>
URL: https://gitlist.dev/e/1431482407-63642-1-git-send-email-davvid%40gmail.com

```
Add a winmerge scriptlet with the commands described in [1] so
that users can use winmerge without needing to perform any
additional configuration.

[1] http://thread.gmane.org/gmane.comp.version-control.git/268631

Helped-by: Philip Oakley <philipoakley@iee.org>
Helped-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: David Aguilar <davvid@gmail.com>
---

Changes since v1: we now honor the $ProgramW6432 environment variable.

 mergetools/winmerge | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 mergetools/winmerge

diff --git a/mergetools/winmerge b/mergetools/winmerge
new file mode 100644
index 0000000..db0b060
--- /dev/null
+++ b/mergetools/winmerge
@@ -0,0 +1,40 @@
+diff_cmd () {
+	"$merge_tool_path" -u -e "$LOCAL" "$REMOTE"
+	return 0
+}
+
+merge_cmd () {
+	# mergetool.winmerge.trustExitCode is implicitly false.
+	# touch $BACKUP so that we can check_unchanged.
+	touch "$BACKUP"
+	"$merge_tool_path" -u -e -dl Local -dr Remote \
+		"$LOCAL" "$REMOTE" "$MERGED"
+	check_unchanged
+}
+
+translate_merge_tool_path() {
+	# Use WinMergeU.exe if it exists in $PATH
+	if type WinMergeU.exe >/dev/null 2>&1
+	then
+		echo WinMergeU.exe
+		return
+	fi
+
+	# Look for WinMergeU.exe in the typical locations
+	winmerge_exe="WinMerge/WinMergeU.exe"
+	if test -n "$ProgramW6432" && test -x "$ProgramW6432/$winmerge_exe"
+	then
+		printf '%s' "$ProgramW6432/$winmerge_exe"
+	elif test -n "$PROGRAMFILES" && test -x "$PROGRAMFILES/$winmerge_exe"
+	then
+		printf '%s' "$PROGRAMFILES/$winmerge_exe"
+	elif test -x "/c/Program Files (x86)/$winmerge_exe"
+	then
+		printf '%s' "/c/Program Files (x86)/$winmerge_exe"
+	elif test -x "/c/Program Files/$winmerge_exe"
+	then
+		printf '%s' "/c/Program Files/$winmerge_exe"
+	else
+		echo WinMergeU.exe
+	fi
+}
-- 
1.8.5.3

```

## Johannes Schindelin, 2015-05-13 08:10

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <96de4a8c540c041733fd47a4cb1af782@www.dscho.org>
URL: https://gitlist.dev/e/96de4a8c540c041733fd47a4cb1af782%40www.dscho.org
In-Reply-To: <1431482407-63642-1-git-send-email-davvid@gmail.com>

```
Hi David,

On 2015-05-13 04:00, David Aguilar wrote:

> +	if test -n "$ProgramW6432" && test -x "$ProgramW6432/$winmerge_exe"
> +	then
> +		printf '%s' "$ProgramW6432/$winmerge_exe"
> +	elif test -n "$PROGRAMFILES" && test -x "$PROGRAMFILES/$winmerge_exe"
> +	then
> +		printf '%s' "$PROGRAMFILES/$winmerge_exe"

I am terribly sorry that I confused you... What I *meant* was to test in the following order: $PROGRAMFILES, $ProgramW6432, /c/Program Files, /c/Program Files (x86).

While I am already nitpicking, I would also like to throw out the idea to write that in a loop instead (a little bit more DRY):

for directory in "$PROGRAMFILES" "$ProgramW6432" '/c/Program Files' '/c/Program Files (x86)'
do
    test -n "$directory" &&
    test -x "$directory/$winmerge_exe" &&
    echo "$directory/$winmerge_exe" &&
    break
done

Ciao,
Dscho

```

## Sebastian Schuberth, 2015-05-13 13:19

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <55534F4E.60402@gmail.com>
URL: https://gitlist.dev/e/55534F4E.60402%40gmail.com
In-Reply-To: <1431482407-63642-1-git-send-email-davvid@gmail.com>

```
On 13.05.2015 04:00, David Aguilar wrote:

> +	if test -n "$ProgramW6432" && test -x "$ProgramW6432/$winmerge_exe"
> +	then
> +		printf '%s' "$ProgramW6432/$winmerge_exe"

I don't think it makes sense to check "$ProgramW6432". The content of 
that variable depends on the bitness of the process requesting the 
environment. Just checking "$PROGRAMFILES" and "$PROGRAMFILES(X86)" 
should be sufficient and more clear.

Also, note that you should use all upper case names when referring to 
Windows environment variables. While it's true that on plain Windows 
environment variable names are case-insensitive, MSYS1/2 converts all 
variable names to upper case and *is* case sensitive. I.e. while "echo 
$PROGRAMFILES" works as expected from a Git Bash on Windows, "echo 
$ProgramFiles" results in an empty string.

 > +	elif test -x "/c/Program Files (x86)/$winmerge_exe"
 > +	then
 > +		printf '%s' "/c/Program Files (x86)/$winmerge_exe"
 > +	elif test -x "/c/Program Files/$winmerge_exe"
 > +	then
 > +		printf '%s' "/c/Program Files/$winmerge_exe"

I also think these fallbacks can simply go away. It is *very* unlikely 
that "$PROGRAMFILES" / "$PROGRAMFILES(X86)" point to something else but 
WinMerge still is installed there.

-- 
Sebastian Schuberth

```

## Johannes Schindelin, 2015-05-13 15:22

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <3d7e3b09b89c46c39befca7564f5c1d6@www.dscho.org>
URL: https://gitlist.dev/e/3d7e3b09b89c46c39befca7564f5c1d6%40www.dscho.org
In-Reply-To: <55534F4E.60402@gmail.com>

```
Hi,

On 2015-05-13 15:19, Sebastian Schuberth wrote:
> On 13.05.2015 04:00, David Aguilar wrote:
> 
>> +	if test -n "$ProgramW6432" && test -x "$ProgramW6432/$winmerge_exe"
>> +	then
>> +		printf '%s' "$ProgramW6432/$winmerge_exe"
> 
> I don't think it makes sense to check "$ProgramW6432". The content of
> that variable depends on the bitness of the process requesting the
> environment. Just checking "$PROGRAMFILES" and "$PROGRAMFILES(X86)"
> should be sufficient and more clear.

In my tests, "$PROGRAMFILES(X86)" did *not* work:

    $ echo "$PROGRAMFILES(X86)"
    bash: syntax error near unexpected token `('

> Also, note that you should use all upper case names when referring to
> Windows environment variables. While it's true that on plain Windows
> environment variable names are case-insensitive, MSYS1/2 converts all
> variable names to upper case and *is* case sensitive. I.e. while "echo
> $PROGRAMFILES" works as expected from a Git Bash on Windows, "echo
> $ProgramFiles" results in an empty string.

Exactly. In my tests, "$ProgramW6432" worked, while "$PROGRAMW6432" did not.

FWIW I think that the idea to test for a WinMerge executable of another bitness makes sense, because we can execute an executable of another bitness (unlike a .dll of another bitness).

Ciao,
Johannes

```

## Sebastian Schuberth, 2015-05-13 15:33

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <CAHGBnuPDSdCyVNM+Gagang1Cf9yw0Tyios45i6pnZSSzaJJC2Q@mail.gmail.com>
URL: https://gitlist.dev/e/CAHGBnuPDSdCyVNM%2BGagang1Cf9yw0Tyios45i6pnZSSzaJJC2Q%40mail.gmail.com
In-Reply-To: <3d7e3b09b89c46c39befca7564f5c1d6@www.dscho.org>

```
On Wed, May 13, 2015 at 5:22 PM, Johannes Schindelin
<johannes.schindelin@gmx.de> wrote:

> In my tests, "$PROGRAMFILES(X86)" did *not* work:
>
>     $ echo "$PROGRAMFILES(X86)"
>     bash: syntax error near unexpected token `('

Interesting. In both MSYS1/2 Git Bashes I get on Windows 7 64-bit:

$ echo "$PROGRAMFILES(X86)"
C:\Program Files (x86)(X86)

So it seems to resolve only the $PROGRAMFILES part and appending the
literal "(X86)". Not sure how to tell Bash that "(X86)" is part of the
variable name.

> Exactly. In my tests, "$ProgramW6432" worked, while "$PROGRAMW6432" did not.

Very odd indeed that for me it's the exact opposite.

> FWIW I think that the idea to test for a WinMerge executable of another bitness makes sense, because we can execute an executable of another bitness (unlike a .dll of another bitness).

I agree it makes sense to check for both 64-bit and 32-bit WinMerge
installations. But IMO it does not make sense to mix in the bitness of
the process requesting the environment as part of running the
mergetools/winmerge script, which is what we do by querying
"$ProgramW6432" / "$PROGRAMW6432" .

-- 
Sebastian Schuberth

```

## Sebastian Schuberth, 2015-05-13 20:00

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <5553AD43.9010807@gmail.com>
URL: https://gitlist.dev/e/5553AD43.9010807%40gmail.com
In-Reply-To: <CAHGBnuPDSdCyVNM+Gagang1Cf9yw0Tyios45i6pnZSSzaJJC2Q@mail.gmail.com>

```
On 13.05.2015 17:33, Sebastian Schuberth wrote:

>> In my tests, "$PROGRAMFILES(X86)" did *not* work:
>>
>>      $ echo "$PROGRAMFILES(X86)"
>>      bash: syntax error near unexpected token `('
> 
> Interesting. In both MSYS1/2 Git Bashes I get on Windows 7 64-bit:
> 
> $ echo "$PROGRAMFILES(X86)"
> C:\Program Files (x86)(X86)
> 
> So it seems to resolve only the $PROGRAMFILES part and appending the
> literal "(X86)". Not sure how to tell Bash that "(X86)" is part of the
> variable name.
> 
>> Exactly. In my tests, "$ProgramW6432" worked, while "$PROGRAMW6432" did not.
> 
> Very odd indeed that for me it's the exact opposite.

So how about something like this which hopefully covers all cases (including case-sensitivity issues regarding environment variable names and the problem of querying a variable that contains parentheses as part of its name):

for directory in "$(env | sed -nr 's/^PROGRAM(FILES(\(X86\))?|W6432)=//pI')"
do
    test -n "$directory" &&
    test -x "$directory/$winmerge_exe" &&
    echo "$directory/$winmerge_exe" &&
    break
done

sed's "I" seems to be a GNU extension that's not available with OS X' BSD sed. This shouldn't be an issue as WinMerge is Windows only anyway. If it still turns out to be an issue we probably should come up with an equivalent Perl expression.

-- 
Sebastian Schuberth

```

## Sebastian Schuberth, 2015-05-13 20:36

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <5553B5BF.7040408@gmail.com>
URL: https://gitlist.dev/e/5553B5BF.7040408%40gmail.com
In-Reply-To: <5553AD43.9010807@gmail.com>

```
On 13.05.2015 22:00, Sebastian Schuberth wrote:

> So how about something like this which hopefully covers all cases (including case-sensitivity issues regarding environment variable names and the problem of querying a variable that contains parentheses as part of its name):
> 
> for directory in "$(env | sed -nr 's/^PROGRAM(FILES(\(X86\))?|W6432)=//pI')"
> do
>      test -n "$directory" &&
>      test -x "$directory/$winmerge_exe" &&
>      echo "$directory/$winmerge_exe" &&
>      break
> done
> 
> sed's "I" seems to be a GNU extension that's not available with OS X' BSD sed. This shouldn't be an issue as WinMerge is Windows only anyway. If it still turns out to be an issue we probably should come up with an equivalent Perl expression.

Or even better, as that does not rely on GNU sed, sorts matches so that "C:\Program Files" for sure comes before "C:\Program Files (x86)" and removes any duplicates:

for directory in "$(env | grep -E '^PROGRAM(FILES(\(X86\))?|W6432)=' | cut -d '=' -f 2 | sort -u)"
do
     test -n "$directory" &&
     test -x "$directory/$winmerge_exe" &&
     echo "$directory/$winmerge_exe" &&
     break
done

-- 
Sebastian Schuberth

```

## Sebastian Schuberth, 2015-05-13 20:37

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <5553B61D.7050204@gmail.com>
URL: https://gitlist.dev/e/5553B61D.7050204%40gmail.com
In-Reply-To: <5553B5BF.7040408@gmail.com>

```
On 13.05.2015 22:36, Sebastian Schuberth wrote:

> for directory in "$(env | grep -E '^PROGRAM(FILES(\(X86\))?|W6432)=' | cut -d '=' -f 2 | sort -u)"

Sorry, that should have been "grep -Ei" in there.

-- 
Sebastian Schuberth

```

## SZEDER Gábor, 2015-05-14 21:24

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <1431638653-9356-1-git-send-email-szeder@ira.uka.de>
URL: https://gitlist.dev/e/1431638653-9356-1-git-send-email-szeder%40ira.uka.de
In-Reply-To: <CAHGBnuPDSdCyVNM+Gagang1Cf9yw0Tyios45i6pnZSSzaJJC2Q@mail.gmail.com>

```

> On Wed, May 13, 2015 at 5:22 PM, Johannes Schindelin <johannes.schindelin <at> gmx.de> wrote:
> 
> > In my tests, "$PROGRAMFILES(X86)" did *not* work:
> >
> >     $ echo "$PROGRAMFILES(X86)"
> >     bash: syntax error near unexpected token `('
> 
> Interesting. In both MSYS1/2 Git Bashes I get on Windows 7 64-bit:
> 
> $ echo "$PROGRAMFILES(X86)"
> C:\Program Files (x86)(X86)
> 
> So it seems to resolve only the $PROGRAMFILES part and appending the
> literal "(X86)". Not sure how to tell Bash that "(X86)" is part of the
> variable name.

It would be ${PROGRAMFILES(X86)}, but POSIX says that variable names can
only contain alphanumeric characters and underscores, and Bash adheres
when it complains about it in Johannes' example above.  Not sure whether
there is a clever escaping that could make it work, I couldn't find any.


G�bor

```

## SZEDER Gábor, 2015-05-14 21:48

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <1431640110-8484-1-git-send-email-szeder@ira.uka.de>
URL: https://gitlist.dev/e/1431640110-8484-1-git-send-email-szeder%40ira.uka.de
In-Reply-To: <5553B61D.7050204@gmail.com>

```
> for directory in "$(env | grep -E '^PROGRAM(FILES(\(X86\))?|W6432)=' | cut -d '=' -f 2 | sort -u)"
> do
>      test -n "$directory" &&
>      test -x "$directory/$winmerge_exe" &&
>      echo "$directory/$winmerge_exe" &&
>      break
> done

This ain't gonna work, because the output of the pipe won't be split
because of the quotes around the command substitution, so $directory will
hold everything.  Simply loosing those quotes is not good either, because
the output will be split on the spaces as well, leading to 'C:\Program',
'Files' and '(x86)', unless the space is already removed from IFS.  IIRC
Windows doesn't allow newlines in pathnames, so IFS=$'\n' and removing the
quotes around the command substitution should do the trick.

Pathnames can contain '=' characters, so, while I think it's outrageous,
the paths in those variables can also contain an '=' or two.  Well,
theoretically, at least, and in that case the 'cut -d= -f2' will cut off
the end of the pathname.  I don't know whether this is something to be
worth worrying about, but in that case it should be 'cut -d= -f2-'.


G�bor

```

## Sebastian Schuberth, 2015-05-15 05:40

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <CAHGBnuNvTZ27CCquHHtC5bQFkG2k-+SDYw-6U2TRzzHyFiFwJw@mail.gmail.com>
URL: https://gitlist.dev/e/CAHGBnuNvTZ27CCquHHtC5bQFkG2k-%2BSDYw-6U2TRzzHyFiFwJw%40mail.gmail.com
In-Reply-To: <1431638653-9356-1-git-send-email-szeder@ira.uka.de>

```
On Thu, May 14, 2015 at 11:24 PM, SZEDER Gábor <szeder@ira.uka.de> wrote:

>> $ echo "$PROGRAMFILES(X86)"
>> C:\Program Files (x86)(X86)
>>
>> So it seems to resolve only the $PROGRAMFILES part and appending the
>> literal "(X86)". Not sure how to tell Bash that "(X86)" is part of the
>> variable name.
>
> It would be ${PROGRAMFILES(X86)}, but POSIX says that variable names can
> only contain alphanumeric characters and underscores, and Bash adheres
> when it complains about it in Johannes' example above.  Not sure whether
> there is a clever escaping that could make it work, I couldn't find any.

Right, I did already try that curly-braces-style of quoting, and
MSYS1/2 both give:

$ echo ${PROGRAMFILES(X86)}
sh.exe": ${PROGRAMFILES(X86)}: bad substitution

I couldn't find any other style of quoting that works, either.

-- 
Sebastian Schuberth

```

## Sebastian Schuberth, 2015-05-15 05:46

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <CAHGBnuN-sw8Q_boB2SSNKTQP6M4jifiketFWJ23bknAdCVw_Uw@mail.gmail.com>
URL: https://gitlist.dev/e/CAHGBnuN-sw8Q_boB2SSNKTQP6M4jifiketFWJ23bknAdCVw_Uw%40mail.gmail.com
In-Reply-To: <1431640110-8484-1-git-send-email-szeder@ira.uka.de>

```
On Thu, May 14, 2015 at 11:48 PM, SZEDER Gábor <szeder@ira.uka.de> wrote:

>> for directory in "$(env | grep -E '^PROGRAM(FILES(\(X86\))?|W6432)=' | cut -d '=' -f 2 | sort -u)"
>> do
>>      test -n "$directory" &&
>>      test -x "$directory/$winmerge_exe" &&
>>      echo "$directory/$winmerge_exe" &&
>>      break
>> done
>
> This ain't gonna work, because the output of the pipe won't be split
> because of the quotes around the command substitution, so $directory will
> hold everything.  Simply loosing those quotes is not good either, because

That's what I thought at first, too, but simply trying it out proved
me wrong (again, in both MSYS1/2):

$ for d in "$(env | grep -Ei '^PROGRAM(FILES(\(X86\))?|W6432)=' | cut
-d '=' -f 2- | sort -u)"; do echo "$d"; done
C:\Program Files
C:\Program Files (x86)

Note that the path, although they are containing spaces, are on separate lines.

-- 
Sebastian Schuberth

```

## SZEDER Gábor, 2015-05-15 09:01

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <20150515110117.Horde.HyhfKlrEun2r-3mHUG3GSA1@webmail.informatik.kit.edu>
URL: https://gitlist.dev/e/20150515110117.Horde.HyhfKlrEun2r-3mHUG3GSA1%40webmail.informatik.kit.edu
In-Reply-To: <CAHGBnuN-sw8Q_boB2SSNKTQP6M4jifiketFWJ23bknAdCVw_Uw@mail.gmail.com>

```

Quoting Sebastian Schuberth <sschuberth@gmail.com>:

> On Thu, May 14, 2015 at 11:48 PM, SZEDER Gábor <szeder@ira.uka.de> wrote:
>
>>> for directory in "$(env | grep -E
>>> '^PROGRAM(FILES(\(X86\))?|W6432)=' | cut -d '=' -f 2 | sort -u)"
>>> do
>>>     test -n "$directory" &&
>>>     test -x "$directory/$winmerge_exe" &&
>>>     echo "$directory/$winmerge_exe" &&
>>>     break
>>> done
>>
>> This ain't gonna work, because the output of the pipe won't be split
>> because of the quotes around the command substitution, so $directory will
>> hold everything.  Simply loosing those quotes is not good either, because
>
> That's what I thought at first, too, but simply trying it out proved
> me wrong (again, in both MSYS1/2):
>
> $ for d in "$(env | grep -Ei '^PROGRAM(FILES(\(X86\))?|W6432)=' | cut
> -d '=' -f 2- | sort -u)"; do echo "$d"; done
> C:\Program Files
> C:\Program Files (x86)
>
> Note that the path, although they are containing spaces, are on
> separate lines.

Yeah, exactly the behavior I described in the part you quoted.
Although it looks good, it's not what you want.
Putting 'echo "** $d **"' in the loop gives me:

** C:\Program Files
C:\Program Files (x86) **

(1.9.5 MSysGit)


Gábor

```

## David Aguilar, 2015-05-20 04:10

Subject: Re: [PATCH v2] mergetools: add winmerge as a builtin tool
Message-ID: <20150520041017.GA91560@gmail.com>
URL: https://gitlist.dev/e/20150520041017.GA91560%40gmail.com
In-Reply-To: <CAHGBnuNvTZ27CCquHHtC5bQFkG2k-+SDYw-6U2TRzzHyFiFwJw@mail.gmail.com>

```
On Fri, May 15, 2015 at 07:40:53AM +0200, Sebastian Schuberth wrote:
> On Thu, May 14, 2015 at 11:24 PM, SZEDER Gábor <szeder@ira.uka.de> wrote:
> 
> >> $ echo "$PROGRAMFILES(X86)"
> >> C:\Program Files (x86)(X86)
> >>
> >> So it seems to resolve only the $PROGRAMFILES part and appending the
> >> literal "(X86)". Not sure how to tell Bash that "(X86)" is part of the
> >> variable name.
> >
> > It would be ${PROGRAMFILES(X86)}, but POSIX says that variable names can
> > only contain alphanumeric characters and underscores, and Bash adheres
> > when it complains about it in Johannes' example above.  Not sure whether
> > there is a clever escaping that could make it work, I couldn't find any.
> 
> Right, I did already try that curly-braces-style of quoting, and
> MSYS1/2 both give:
> 
> $ echo ${PROGRAMFILES(X86)}
> sh.exe": ${PROGRAMFILES(X86)}: bad substitution
> 
> I couldn't find any other style of quoting that works, either.

How about simply "$PROGRAMFILES (x86)"?

Let the shell handle the programfiles part, and then it'll
concat the (x86).  That's what's on the filesystem anyways, so
it should DTRT.  I'll send this in patch form shortly.
-- 
David

```
