3

I have been following this post on how to use Winmerge as a diff tool in git. Initially it doesn't work (ok now, therefore irrelevant to my question here). One key difference is I'm using git provided inside MSYS2, not the msysgit under native Windows environment. The relevant git config fragment is:

[diff]
        guitool = winmerge

[difftool "winmerge"]
        cmd = \"????/WinMergeU.exe\" /e /s /u /r /wl \"$LOCAL\" \"$REMOTE\"

To my shock, some of the command line options of winmerge have been converted to drive letters upon execution, causing failure. The real command executed after git difftool -g is:

"????\WinMergeU.exe" /e S:/ U:/ R:/ D:/msys64/wl <file1> <file2>

Where D:/msys64 is my MSYS2 installation prefix. Note the inconsistency in errors — /e unconverted, /wl converted to path component, and all others to drive letters. A solution is found later by using - instead of / as prefix of all options:

[difftool "winmerge"]
        cmd = \"????/WinMergeU.exe\" -e -s -u -r -wl \"$LOCAL\" \"$REMOTE\"

My question is:

  1. Why the inconsistent behavior? Is there any reference on how cygpath performs the path translation?
  2. Luckily WinMerge accepts using - as option indicator. For those GUI tools where only / is accepted, is there any workaround?
Abel Cheung
  • 417
  • 5
  • 12
  • Did you try it with a recent git for windows (based on msys2) in https://github.com/git-for-windows/git/releases/? – VonC May 27 '15 at 05:40
  • @VonC Thanks, I'll definitely give it a try, nice to see there's alternative effort to msysgit that brings git up to date, though my OP is about running the version of prepackaged git **inside** MSYS2 (which is version 2.4.1 right now). – Abel Cheung May 27 '15 at 06:21

2 Answers2

1

I believe MSYS2 tried to follow the same behavior in MSYS to translate the unix path arguments to Windows path for executable not depending on the msys2 dll.

In this case, you can escape the arguments starting with single slash with double slash, i.e.

\"????/WinMergeU.exe\" //e //s //u //r //wl \"$LOCAL\" \"$REMOTE\"

The rules (extracted from the linked page):

  • Arguments starting with a drive specifier (eg C:) are considered to be Windows paths and are not converted.
  • Arguments containing a ; are considered to be Windows path lists and are not converted.
  • An argument starting with 2 or more / is considered an escaped Windows style switch and will be passed with the leading / removed and all \ changed to /.
    • Except that if there is a / following the leading block of /, the argument is considered to be a UNC path and the leading / is not removed.
  • If an argument has a leading / followed by a drive specfier, the / is removed and all \ are changed to /.
  • If an argument has a = it is considered a variable assignment. The right hand side is converted according to these rules and the left hand side has all \ converted to /, unless the right hand side starts with a drive specifier.
  • An argument that doesn't start with -, ", ', or @ and contains a : followed by /, :, or .; and a / is considered a POSIX path list. Every :-separated element is converted according to these rules, and the : are replaced with ;. Any / are converted to \.
    • Unless the : is followed by //: then it is considered a URL and not converted.
  • An argument with a leading / is converted up to the first /...
  • If an argument has a leading - and a ,, the part after the , is converted according to these rules and the whole argument has \ converted to / unless the path starts with a drive specifier.
  • If an argument has a leading - and the second character is a /, the part from the / onward is converted according to these rules.
  • An argument with a leading @ is considered a response file argument and the text following the @ is converted according to these rules.
  • An argument inside ' or " is processed according to these rules with the quotes ignored.
user2172816
  • 1,200
  • 8
  • 11
  • The mingw.org link in this post is now invalid - it looks like the domain was allowed to expire in late 2020 or early 2021. Archive link: https://web.archive.org/web/20201112005258/http://www.mingw.org/wiki/Posix_path_conversion - and I will come back and edit it in when the moderation strike is over. – AJM Jul 11 '23 at 13:57
1

What is the rule of MSYS/MinGW path translation?

The rules are officially documented with msys2 "Filesystem Paths".

See for instance "Automatic Unix ⟶ Windows Path Conversion"

Process Arguments

When calling native executable from the context of Cygwin then all the arguments that look like Unix paths will get auto converted to Windows.

For example when calling native Python from the context of bash:

$ python3 -c "import sys; print(sys.argv)" --dir=/foo ['-c',
'--dir=C:/msys64/foo'] $ python3 -c "import sys; print(sys.argv)"
--dir=/foo:/bla ['-c', '--dir=C:\\msys64\\foo;C:\\msys64\\bla']

While this is helpful in many cases it's also not perfect and in corner cases converts arguments that look like Unix paths while they are not, or detects lists of Unix paths where there are none.
For these cases you can exclude certain arguments via the MSYS2_ARG_CONV_EXCL environment variable:

$ MSYS2_ARG_CONV_EXCL='--dir=' python3 -c "import sys;
print(sys.argv)" --dir=/foo ['-c', '--dir=/foo']

MSYS2_ARG_CONV_EXCL can either be * to mean exclude everything, or a list of one or more arguments prefixes separated by ;, like MSYS2_ARG_CONV_EXCL=--dir=;--bla=;/test.
It matches the prefix against the whole argument string.


This is also illustrated with Git 2.40 (Q1 2023), which illustrates those rules in the context of a test fix:

See commit 95494c6 (06 Dec 2022) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit 26f8123, 14 Dec 2022)

t0021: use Windows-friendly pwd

Signed-off-by: Johannes Schindelin

In Git for Windows, when passing paths from shell scripts to regular Win32 executables, thanks to the MSYS2 runtime a somewhat magic path conversion happens that lets the shell script think that there is a file at /git/Makefile and the Win32 process it spawned thinks that the shell script said C:/git-sdk-64/git/Makefile instead.

This conversion is documented in detail over here: https://www.msys2.org/docs/filesystem-paths/#automatic-unix-windows-path-conversion

As all automatic conversions, there are gaps.
For example, to avoid mistaking command-line options like /LOG=log.txt (which are quite common in the Windows world) from being mistaken for a Unix-style absolute path, the MSYS2 runtime specifically exempts arguments containing a = character from that conversion.

We are about to change test_cmp to use git diff --no-index(man), which involves spawning precisely such a Win32 process.

In combination, this would cause a failure in t0021-conversion.sh where we pass an absolute path containing an equal character to the test_cmp function.

Seeing as the Unix tools like cp and diff that are used by Git's test suite in the Git for Windows SDK (thanks to the MSYS2 project) understand both Unix-style as well as Windows-style paths, we can stave off this problem by simply switching to Windows-style paths and side-stepping the need for any automatic path conversion.

Note: The PATH variable is obviously special, as it is colon-separated in the MSYS2 Bash used by Git for Windows, and therefore cannot contain absolute Windows-style paths, lest the colon after the drive letter is mistaken for a path separator.
Therefore, we need to be careful to keep the Unix-style when modifying the PATH variable.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250