For secret reasons,
instead of using a Git forge like Github for one of my projects,
I am using a regular Linux virtual server running in a major hosting provider.
I push to a bare Git repository1,
which has a post-commit
hook2 that checks out the commit I just pushed,
builds the project and runs a few sanity checks,
and then deploys the result.
This was the first time I had set such a system up from scratch myself,
and I fell into a common pitfall: GIT_DIR
.
Here’s the punchline:
When checking out a repository from a Git hook,
make sure to unset Git environment variables like GIT_DIR
,
or the actions will fail with strange errors.
What that looks like in practice is pushing to the Git repository and seeing an error like this:
remote: fatal: Not a git repository: '.'
The Git documentation actually calls this out, but I didn’t see it until I ran into problems.
What’s happening is that the post-commit hook sets variables like GIT_DIR
,
which has special value to subsequent git
commands.
GIT_DIR
in particular is set to .
,
which is where the error message Not a git repository: '.'
comes from.
You can use unset $(git rev-parse --local-env-vars)
in a hook
to unset all such variables.
Implementation details
My build script was doing something like this (heavily paraphrased):
#!/bin/sh
set -eux
# $newrev is the revision that was just pushed
# $PWD is the bare git repo path
local_git_url="file://$PWD"
while read oldrev newrev refname; do
# We can't clone a repo and check out a specific commit in a single command.
git clone --no-checkout "$local_git_url" /tmp/checkout
cd /tmp/checkout
git checkout "$newrev"
done
The git clone
would succeed – I guess a bad value for GIT_DIR
doesn’t break that command –
but git checkout
would fail.
Of course, running these commands in an interactive SSH session worked just fine,
because GIT_DIR
hadn’t been set.
This had me tearing my hair out for a bit until I found
the exact error message
on StackOverflow.
After that, I discovered that the githooks
manual addresses this problem as quoted above,
notes that there are other variables aside from GIT_DIR
that may cause similar problems,
and includes the unset $(git rev-parse --local-env-vars)
example to handle all of them.
(I added an answer to the SO post that covers this.)
-
As described in Pro Git chapter 4.2, a bare repository is a repository that doesn’t contain a working directory. You can’t interact with the files in the repo directly. In the past, pushing to non-bare repositories was actively discouraged; now, Git makes allowances for doing that in some use cases. Regardless, a bare repository is the best plan for my specific use case. ↩︎
-
Git hooks are scripts that run automatically at certain stages, like on the client before pushing a commit, or on a server after receiving a commit. See the
githooks
manual for official documentation, and https://githooks.com/ for unofficial but very nice documentation. Developers may be familiar with thepre-commit
project, which is a package manager for client-side hooks. ↩︎