{"thread":{"id":"53529","subject":"libexec/git-core on PATH in editors and other subprocesses (and a fix for vim)","startedAt":"2020-05-21T20:31:51Z","lastAt":"2020-05-21T20:31:51Z","messageCount":1,"participants":["D. Ben Knoble"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"398398","messageId":"CALnO6CDtGRRav8zK2GKi1oHTZWrHFTxZNmnOWu64-ab+oY3_Lw@mail.gmail.com","threadId":"53529","inReplyTo":null,"subject":"libexec/git-core on PATH in editors and other subprocesses (and a fix for vim)","fromName":"D. Ben Knoble","fromEmail":"ben.knoble@gmail.com","sentAt":"2020-05-21T20:31:35Z","receivedAt":"2020-05-21T20:31:51Z","isPatch":false,"sender":{"key":"ben.knoble@gmail.com","avatar":"https://avatars.githubusercontent.com/u/22802209?v=4"},"body":"Hello all,\n\nI noticed something odd today--I was working in vim and spawned a new terminal\nwith `:terminal`. My shell startup files, which use the location of the git\nbinary to reconstruct paths to things like\n`etc/bash_completion.d/git-prompt.sh` and `contrib/git-jump`, suddenly choked.\nThe location of the git program was suddenly different than usual!\n\nAfter some debugging, I came to determine that this occurred, not because of\nvim, but because vim was launched from a git-invoked process (in this case,\n`contrib/git-jump/git-jump`).\n\nThe salient git code is `setup_path()` in exec-cmd.c [1] and it's call in git.c\n[2]. They appear to coordinate to prepend the libexec dir to PATH. This causes\nthe location of the git binary to be (e.g.) /usr/local/libexec/git-core/git, and\nnot (e.g.) /usr/local/bin/git; since I traverse up two directories from the\nlocation of git, the former breaks my startup scripts. (You would be well\nwithing your rights to argue that I should not use the location of git to find\nthese scripts. I have my reasons.)\n\nAnyway, the net result is that if I run, say, `git jump grep foo` and then try\nto invoke `:terminal` or `:shell`, my shell complains.\n\nUnfortunately, this modification propagates to all child processes, as this\nsimple test-case demonstrates:\n\n    $ printf '%s\\n' '#!/bin/sh' \"printenv PATH | tr : '\\n' | grep\ngit-core\" > git-show-env\n    $ chmod u+x git-show-env\n    $ PATH=.:$PATH git show-env\n    /usr/local/Cellar/git/2.26.2/libexec/git-core\n\nWhile it would be *nice* if git didn't modify PATH in such a way that it\naffected all subprocesses (i.e., if it was somehow scoped to only processes that\nneed it), I suspect this is at best difficult and at worst highly-error prone or\nlikely to break things.\n\nIn the (possibly eternal) interim, I would like to share some vimscript that\n\"fixes\" $PATH in vim when it detects this case. The easiest use is to drop the\ncode in your vimrc file (usually ~/.vim/vimrc or ~/.vimrc). I've tried to keep\nit portable in terms of path separators and file paths, but I do not have a\nWindows box to test on.\n\nActually, it's a little more aggressive than I suggested; it strips out *all*\nPATH entries ending in libexec/git-core, not just the first. But I never have\nlibexec/git-core on my PATH anyway, so I'm not bothered by that. One could\nmodify this function to only check the first entry (the one git would have\nprepended).\n\nBest,\nBen Knoble\n\nP.S. Does anyone know what the libexec/git-core equivalent is on Windows? [3]\nalludes to libexec\\git-core, which I *think* is handled by my code.\n\nP.P.S. If your vim is old and does not have `const`, you can use `let` instead.\nYou may need to change out the lambda {_, d -> ... } for a \"v:val\" string as\nwell.\n\n[1]: https://github.com/git/git/blob/87680d32efb6d14f162e54ad3bda4e3d6c908559/exec-cmd.c#L304\n[2]: https://github.com/git/git/blob/87680d32efb6d14f162e54ad3bda4e3d6c908559/git.c#L868\n[3]: https://wilsonmar.github.io/git-custom-commands/\n\n--- VIM CODE\n\n\" When git starts up, it prepends it's libexec dir to PATH to allow it to find\n\" external commands.\n\"\n\" Thus, if vim is invoked via a git process (such as the contrib git-jump, my\n\" own git-ed, or any other usage of GIT_EDITOR/VISUAL/EDITOR in git commands, be\n\" they scripts or internals--with the exception of manually invoking the script\n\" yourself, without using git: sh .../git-jump), $PATH will contain something\n\" like libexec/git-core.\n\"\n\" We don't generally want it in vim's $PATH, though, as it is passed down to\n\" *all* subprocesses, including shells started with :terminal or :shell.\nfunction s:fix_git_path() abort\n  const slash = has('win32') ? '\\' : '/'\n  const git_core_base = printf('libexec%sgit-core', slash)\n  \" optimization: early return\n  if $PATH !~# '.*'.git_core_base.'.*'\n    return\n  endif\n  const path_sep = has('win32') ? ';' : ':'\n  const path = split($PATH, path_sep)\n  const path_sans_libexec_git_core = filter(path, {_, d -> d !~#\n'.*'.git_core_base})\n  const new_path = join(path_sans_libexec_git_core, path_sep)\n  let $PATH = new_path\nendfunction\n\naugroup fix_git_path\n  autocmd!\n  autocmd VimEnter * call s:fix_git_path()\naugroup END\n\n--- END VIM CODE\n"}]}