git - 如何配置 git difftool 在 WSL 上正常工作?

标签 git windows-subsystem-for-linux

我的 git diff 配置是:

mark@L-R910LPKW:~/.kube$ git config --list | grep diff
diff.tool=bc3
diff.guitool=bc3
difftool.prompt=false
difftool.bc3.path=/mnt/c/Program Files/Beyond Compare 4/BComp.com
mark@L-R910LPKW:~/.kube$

当我运行git difftool时从 WSL 我得到这样的信息: enter image description here

显然,当我运行 git diff 时我在控制台上看到了预期的输出。

现在,根据ps实际的命令行是 /tools/init /mnt/c/Program Files/Beyond Compare 4/BComp.com /tmp/maHQTa_config config

所以它看起来像config已成功转换为 WSL 外部“理解”的 WSL 完整路径 - \\wsl.localhost\Ubuntu-20.04\home\mark.kube\config。但是,对于 /tmp/maHQTa_config 来说,情况并非如此,即使它映射到 \\wsl.localhost\Ubuntu-20.04\tmp\maHQTa_config

如果有的话如何解决这个问题?

编辑 1

尽管我只展示了比较的 git 配置,但我也涵盖了 merge :

mark@L-R910LPKW:~/.kube [master ? +1 ~1 -0 !]$ git config --list | grep merge
merge.tool=bc3
merge.guitool=bc3
mergetool.prompt=false
mergetool.keepbackup=false
mergetool.bc3.path=/mnt/c/Program Files/Beyond Compare 4/BComp.com
mark@L-R910LPKW:~/.kube [master ? +1 ~1 -0 !]$

最佳答案

为了解决这个 Windows 烦恼,我最终编写了一个快速程序来翻译文件 args。 我使用 Windows p4merge.exe 作为我的 merge 工具,但这应该适用于 bc3 或几乎任何东西。

(我在 Win 10 下的 WSL 2 下运行 Ubuntu。如果您运行不同的版本,此代码可能需要一些小的更改。我注意到对我来说,WSL 文件会转换为\Wsl$...但是对于你是\Wsl.localhost)

//  Problem:
//      It's difficult to launch a Windows GUI program from WSL.
//  Take p4merge for example.
//  I have the Windows version of p4merge installed, and I would
//  like to set that as my git difftool, for comparing versions
//  of source files.
//  If I install it as my git difftool
//      git config --global --add diff.tool p4merge
//  Git will try to launch it, but it won't work because the
//  file paths passed as part of the command are WSL paths,
//  that the Winodws app can't open.
//  p4merge will fail with an error, e.g.:
//      '/tmp/vT1r3J_foo.cpp' is (or points to) an invalid file.
//
//  Solution:
//      This helper program can be installed on the WSL Ubuntu
//  $PATH as p4merge.  Or you can create a symlink in your
//  $PATH named p4merge that points to this program.
//
//  This will scan all of the command line arguemments and translate
//  any file paths to paths that Windows apps can open, then
//  launch the target with the modified command line.
//
//  You can use this wrapper around any windows GUI program.
//  It will figure out what target to launch from argv[0].
//  HOWEVER!  If this wrapper is in the $PATH and the target
//  is also in the $PATH, they CANNOT HAVE THE SAME NAME!
//  That would cause an infinite recursion where the wrapper
//  kept launching itself.
//  Recommended usage:  Have "program" (without extension) in
//  your path pointing to the wrapper, and "program.exe" in
//  your path pointing to the Windows GUI target.
//  This wrapper will automatically append the ".exe" suffix
//  to the name in argv[0], and will quit without launching
//  if argv[0] already contains an ".exe" extension.
//
//  Note:  When the linux shell launches a program via a symlink,
//  argv[0] will have the name of the link, not the link target.
//
//  Any argument on the command line that looks like a file or
//  path will be translated if necessary.   A path that is 
//  already in Windows format will just be copied.
//
//  The WSL file system is not directly part of your computer's
//  Windows namespace.  The WSL file system is accessed as if
//  it was a network file.
//  A path that starts with "/" -- the WSL root directory translates
//  to \\wsl$\${WSL_DISTRO_NAME}\
//  A path that starts with "~" -- translates to 
//  \\wsl$\${WSL_DISTRO_NAME}\${HOME}
//  And of course paths that start with "." translate to
//  \\wsl$\${WSL_DISTRO_NAME}\${PWD}
//  
//  The Windows file system is accessable from linux as mounted
//  devices.   A path like /mnt/<drive-letter>/... translates to
//  <drive-letter>:\...
//
//  All forward-slashes are translated to back-slashes.
//

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>

#include <string>
#include <sstream>

//  This really oughta be in libc
//
char * stristr (const char * text, const char * match)
{
    char * strText = strdup (text);
    for (char * p = strText; !!(*p); ++p) {
        *p = tolower (*p);
    }
    char * strMatch = strdup (match);
    for (char * p = strMatch; !!(*p); ++p) {
        *p = tolower (*p);
    }
    char * result = strstr (strText, strMatch);
    if (result) {
        result = (char *) text + (result - strText);
    }
    free (strText);
    free (strMatch);
    return result;
}


int main (int argc, char ** argv) {
    for (int i = 0; i < argc; ++i) {
        fprintf (stdout, "arg[%2d] : %s\n", i, argv[i]);
    }
    fflush (stdout);

    struct _local {
        const char * env_WSL_DISTRO_NAME;
        const char * env_HOME;
        const char * env_PWD;
        char * target;
        std::stringstream command;

        _local()
        :   env_WSL_DISTRO_NAME (nullptr)
        ,   env_HOME (nullptr)
        ,   env_PWD (nullptr)
        ,   target (nullptr)
        {}
        ~_local() {
            if (target) {
                auto ptr = target;
                target = nullptr;
                free (ptr);
            }
        }

        static void get_env (const char * & member, const char * name) {
            member = getenv (name);
            fprintf (stdout, "%s=%s\n", (const char *) name,
                member ? member : "");
        }

        void get_target (const char * me) {
            const char * tmp = basename (me);
            size_t n = strlen (tmp) + 5;
            n = (n + 32) & 0x1f;
            target = (char *) malloc (n);
            memset (target, 0, n);
            --n;
            strncpy (target, tmp, n);
            n -= strlen (target);
            strncat (target, ".exe", n);
            command << target;
        }

        void translate (const char * arg) {
            if (! arg)  return;
            char c = *(arg++);
            if ('~' == c) {
                translate (env_HOME);
            } else if ('.' == c) {
                translate (env_PWD);
                ++arg;
            } else if ('/' == c) {
                if (0 == strncmp ("mnt/", arg, 4)) {
                    c = toupper (*(arg += 4));
                    command << c;
                    command << ":";
                    arg+=2;
                } else {
                    command << "\\\\\\\\wsl$\\\\";
                    command << env_WSL_DISTRO_NAME;
                }
            }
            for (c = arg[-1]; !!c; c = *(arg++)) {
                if ('/' == c) {
                    command << "\\\\";
                } else {
                    command << c;
                }
            }
        }

        void do_arg (const char * arg) {
            command << ' ';
            translate (arg);
        }
        
    } local;


    local.get_env (local.env_WSL_DISTRO_NAME, "WSL_DISTRO_NAME");
    local.get_env (local.env_HOME,            "HOME");
    local.get_env (local.env_PWD,             "PWD");

    if (stristr (argv[0], ".exe")) {
        fprintf (stderr, "argv[0] = \"%s\"\n", (const char *) argv[0]);
        fputs ("The wsl_wrapper has the same name as the implied target.\n"
            "This would trigger infinite launch recursion!\n", stderr);
        return 1;
    }
    local.get_target (argv[0]);
    for (int i = 1; i < argc; ++i) {
        local.do_arg (argv[i]);
    }

    fputs ("\033[1;33m", stdout);   // YELLOW
    fputs (local.command.str().c_str(), stdout);
    fputs ("\033[0m\n", stdout);
    fflush (stdout);

    int result = system (local.command.str().c_str());
    if (result == -1) {
        fputs ("\033[1;31m", stdout);   // RED
        perror ("Failed to launch:");
        fputs ("\033[0m\n", stdout);
    }

//  fputc ('\n', stdout);
    return result;
}

关于git - 如何配置 git difftool 在 WSL 上正常工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74406523/

相关文章:

git - 在 Git 中仅提交文件的部分更改

macos - 将两个 SSH key 与 GitHub 以及连接多路复用一起使用

git - 如果没有冲突则自动远程 merge

ubuntu - python pip 默认为用户安装,因为普通站 pip 包不可写

c++ - WSL 中的 g++ 创建了一个不可删除的文件

c++ - 在 Windows 上从 C++ 代码运行 WSL 命令

git mergetool 失败

git - Git 是否将添加/删除视为重命名?

node.js - 'sudo npm install -g npm' 失败

git fatal error : Unsupported SSL backend 'schannel'