Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between -C and --git-dir when manipulating repo in git hook

Tags:

git

githooks

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.

like image 752
Will Richardson Avatar asked Feb 17 '17 10:02

Will Richardson


1 Answers

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 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.

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.

like image 117
torek Avatar answered Oct 06 '22 01:10

torek