Search code examples
gitenvironment-variablessymlinkmsys2git-for-windows

How to give "git" access to specific EXEs without adding the whole directory to environment $PATH?


I'm using MSYS2/git with a proper installation of git for windows SDK Install git inside MSYS2 proper

The git.exe is in /mingw64/bin directory (which is added to $PATH), but other packages that git needs (specifically like sh.exe and less.exe) are in /usr/bin directory which I don't want to put in $PATH to avoid polluting my environment with other executables like mkdir and dir,...

Currently, some git commands need those packages like git config -l which needs less and any git command that'll open the EDITOR will need sh.exe.

What I want to achieve is adding less and sh to the PATH without adding the whole directory.


I tried adding them using methods HERE and HERE. Most of them allowed me to use sh and less directly from the terminal, but none of them made the dependent git commands work.

For example, creating a symlink of less and sh in the /mingw64/bin directory will make the command prompt recognize these executables, but using git config -l will give no output at all unless the directory of less was added to $PATH.

However, other methods (creating scripts and shortcuts,..) will make git complain "Unable to spawn sh/less", altgough cmd and powershell would recognize less and sh.


Why would the executable work from the terminal, but not with git? Especially the symlink case where git didn't complain about missing EXEs but still didn't function properly?


Solution

  • It sounds like you want to run Git for Windows outside of MSYS2 and you want to avoid putting all of the MSYS2's /usr/bin directory on your PATH. It's also probably a good idea to avoid putting /mingw64/bin on your PATH too.

    I suggest making a wrapper program for git itself. It would know where git.exe is located, and know what directories to put on the PATH before running git.exe. It would simply modify the PATH and then pass all its arguments on to the correct git.exe. Since you're probably using a shell like the Windows Command Prompt that doesn't recognize Bash shell scripts, you'll probably just have to make a statically-linked native Windows executable that uses the Win32 API to do this. Then put that in its own directory, and add that directory to your PATH.

    Here is some code that should work for you. However, you will have to edit MSYS2_DIR to point to the directory where you installed MSYS2, and you will have to edit the target string too. (Most MSYS2 users have git installed with pacman and it lives in /usr/bin, so I didn't want to hardcode something different into this code.)

    #include <windows.h>
    #include <stdio.h>
    
    #define MSYS2_DIR "C:/msys64"
    
    int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
      LPSTR lpCmdLine, int nShowCmd)
    {
      const char * target = MSYS2_DIR "/usr/bin/git.exe";
    
      BOOL success = SetEnvironmentVariable(
        "PATH", MSYS2_DIR "/mingw64/bin;" MSYS2_DIR "/usr/bin/");
      if (!success)
      {
        DWORD error = GetLastError();
        fprintf(stderr, "Failed to set PATH: error %ld.\n", error);
        return 1;
      }
    
      PROCESS_INFORMATION info;
    
      STARTUPINFOA startup_info = {
        sizeof(startup_info),
        .hStdInput = GetStdHandle(STD_INPUT_HANDLE),
        .hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE),
        .hStdError = GetStdHandle(STD_ERROR_HANDLE),
      };
    
      success = CreateProcessA(target, GetCommandLine(),
        NULL, NULL, 1, 0, NULL, NULL, &startup_info, &info);
      if (!success)
      {
        DWORD error = GetLastError();
        fprintf(stderr, "Failed to start git: error %ld.\n", error);
        return 1;
      }
    
      DWORD result = WaitForSingleObject(info.hProcess, INFINITE);
      if (result)
      {
        fprintf(stderr, "Unexpected wait result: 0x%lx\n", result);
        return 1;
      }
    
      DWORD code;
      success = GetExitCodeProcess(info.hProcess, &code);
      if (!success)
      {
        fprintf(stderr, "Failed to get child exit code.\n");
        return 1;
      }
      return code;
    }
    

    You can compile this using either the 32-bit or 64-bit MinGW toolchains that MSYS2 provides. Just run gcc wrapper.c -o git. This should create a native Windows executable with no dependencies on any MSYS2 DLLs.