当 运行 作为子文件夹中 git 挂钩的一部分时,为什么 `git` 命令会失败?

Why does `git` commands fail when run as part of a git hook in a subfolder?

我们最近将一个 Node 项目移动到一个 mono-repo 的子文件夹中,之后我们的提交挂钩(使用 Husky 安装)由于在错​​误的目录中执行而开始失败:

{ Error: Command failed: git rev-parse HEAD
fatal: not a git repository: '.git'

    at ChildProcess.exithandler (child_process.js:297:12)
    at ChildProcess.emit (events.js:197:13)
    at maybeClose (internal/child_process.js:978:16)
    at Socket.stream.socket.on (internal/child_process.js:395:11)
    at Socket.emit (events.js:197:13)
    at Pipe._handle.close (net.js:611:12)
  killed: false,
  code: 128,
  signal: null,
  cmd: 'git rev-parse HEAD' }

我没有解决问题(基本上 cd-ing 到根目录,然后在挂钩中执行任何 git 命令),但我没有得到的是 为什么(未修复的)脚本在命令行上运行 - 无论我从哪个文件夹执行它们?.

脚本的失败部分基本上是:

console.log('PWD', cp.execSync('pwd'));
console.log(cp.execSync('git rev-parse HEAD'));

项目布局

.git/
frontend/
  package.json
backend/

将触发上述错误的示例命令:

git checkout master

除了错误,它还打印工作目录:PWD /tmp/foo/myproj/frontend

如果我手动触发脚本,一切正常:

npm run postcheckout
PWD /tmp/foo/myproj/frontend

82dc6d2089a397f0889addb562ea84ba8d846215

无论是根文件夹还是子文件夹,这都有效,我看不出有什么区别。 Husky 应该找到 .git 文件夹,这似乎证明了这一点,但显然当它是 运行 作为 hook 脚本时,情况发生了变化。自动生成的 Husky 脚本的相关部分是:

cd "frontend"
...
...
npm run postcheckout

TL;DR: 取消设置 $GIT_DIR(在 sh 脚本中,unset GIT_DIR)。

the githooks documentation中有提示:

Before Git invokes a hook, it changes its working directory to either $GIT_DIR in a bare repository or the root of the working tree in a non-bare repository. An exception are hooks triggered during a push (pre-receive, update, post-receive, post-update, push-to-checkout) which are always executed in $GIT_DIR.

加上 the top level Git manual page 中的另外几个:

--git-dir=<path>
Set the path to the repository. This can also be controlled by setting the GIT_DIR environment variable. It can be an absolute path or relative path to current working directory.

GIT_DIR
If the GIT_DIR environment variable is set then it specifies a path to use instead of the default .git for the base of the repository. The --git-dir command-line option also sets this value.

这里发生的是顶层Git 程序$GIT_DIR 设置为.git 目录所在的路径成立。由于挂钩已将其工作目录更改为 work-tree,因此 Git 程序已将 $GIT_DIR 设置为 .git。如果你在别处 chdir,你必须适当地调整 $GIT_DIR——如果你 chdir 完全远离主存储库并希望 Git discover $GIT_DIR 再次,您必须 unset $GIT_DIR.