问题
I am writing a git post-receive hook that will clone a separate repo as part of a deployment. It clones the repo to a certain folder and uses the -C
option in subsequent git commands to set the directory to that of the checked out repo (as documented in the man page).
The hook works as expected when run manually from the command line, however when the hook is run by git (ie when a push is received) the command fails with fatal: Not a git repository: '.'
. When I swap out the -C
for a --git-dir
it works.
This is fairly simple to reproduce, create a bare repo git init --bare
and make an executable hook with the contents:
#!/bin/bash
set -xe
SOME_REPO_URL=???? # Some repo that is not this one
repopath=/tmp/somerepo
git clone $SOME_REPO_URL $repopath
# 1: This fails when run through the git hook
git -C $repopath checkout -b somebranch HEAD~1
# 2: This works every time
# git --git-dir $repopath/.git checkout -b somebranch HEAD~1
Running the script from the command line will work as expected, but when you push to the repo the hook will fail. Commenting 1
and uncommenting 2
will work in both cases.
I can't find any documentation that would indicate that this is intended behaviour - an explanation would be appreciated.
This is git 2.7.4 on Ubuntu 16.04.
回答1:
The literal difference between:
git -C directory git-sub-command ...
and:
git --git-dir directory git-sub-command ...
is that the front end setup program git
uses an OS-level "change directory to" operation (os.chdir
from Python, chdir()
from C, etc) for the first, and sets an environment variable $GIT_DIR
for the second. In either case it then locates the sub-command and runs it. (Note that you can in fact do both.) This is documented, including the effects of multiple -C
options and the interaction between -C
and --git-dir
.
That, however, just pushes the problem down one level: now you need to know what git-checkout
(found in the git --exec-path
directory) does differently with $GIT_DIR
than it does with the current working directory. The direct answer is in the top level git command documentation, under the ENVIRONMENT VARIABLES section:
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.
This is where Jan Krüger's comment comes in. When you write a Git hook, you must be aware of the fact that Git may set some environment variables for you. If $GIT_DIR
is set to a relative path name, and you do not override it, and you do change directories, you will change the way all the various Git sub-commands locate the repository. Therefore you must either un-set it (to get the default $GIT_DIR
-not-set behavior), or explicitly set it to an absolute path (to preserve it across directory changes), or explicitly set it to the path—relative or absolute—of some other repository, depending on what behavior you want.
Note that --work-tree
sets $GIT_WORK_TREE
, and there are other similar variables, but—at least in all Git versions to date—$GIT_DIR
is the only one "pre-set for you" (or "for your annoyance" :-) ) in Git hooks.
来源:https://stackoverflow.com/questions/42295050/difference-between-c-and-git-dir-when-manipulating-repo-in-git-hook