We recently moved a Node project into a subfolder of a mono-repo, and after that our commit hooks (installed using Husky) started failing due to being executed in the wrong directory:
{ 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' }
I didn't have problems fixing the issue (basically cd
-ing to the root directory before executing any git
commands in the hooks), but what I don't get is why does the (unfixed) scripts work on the command line - regardless of which folder I execute them from?.
The failing parts of the script is basically:
console.log('PWD', cp.execSync('pwd'));
console.log(cp.execSync('git rev-parse HEAD'));
Project layout
.git/
frontend/
package.json
backend/
Example command that will trigger the error above:
git checkout master
Apart from the error, it prints the working directory: PWD /tmp/foo/myproj/frontend
If I manually trigger the script, everything works fine:
npm run postcheckout
PWD /tmp/foo/myproj/frontend
82dc6d2089a397f0889addb562ea84ba8d846215
This works no matter if the root folder or a sub-folder, and I cannot really see the difference. Husky is supposed to find the .git
folder, and this seems to prove it, but obviously something changes when it is run as a hook script. The relevant parts of the auto-generated Husky script is:
cd "frontend"
...
...
npm run postcheckout
TL;DR: unset $GIT_DIR
(in sh script, unset GIT_DIR
).
There is a hint in 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.
plus another few in the top level Git manual page:
--git-dir=<path>
Set the path to the repository. This can also be controlled by setting theGIT_DIR
environment variable. It can be an absolute path or relative path to current working directory.
GIT_DIR
If theGIT_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.
What happens here is that the top level Git program sets $GIT_DIR
to the path by which the .git
directory is to be found. Since the hook has changed its working directory to the work-tree, the Git program has set $GIT_DIR
to .git
. If you chdir
elsewhere, you must adjust $GIT_DIR
appropriately—and if you chdir completely away from the main repository and wish to have Git discover $GIT_DIR
again, you must unset $GIT_DIR
.