knub
knub

Reputation: 4032

Find out staged files in git `pre-commit` hook when using `git commit -a`

I'm using a git pre-commit hook to automatically format all of my staged files. I'm determining the staged files via git diff --name-only --cached and then call a script on those files. This all works well in the standard use case, however it doesn't work when I commit via

git commit -a ..

because the files are not yet staged.

Is there some way to:

  1. Run the -a effect (add files to staging area) before the pre-commit hook?

  2. Find out that the pre-commit runs in a -a commit?

There must be a way to handle this.

Upvotes: 6

Views: 7495

Answers (1)

torek
torek

Reputation: 488183

because the files are not yet staged

This actually isn't quite true. But it's not quite false either.

Here is a (silly, meant for illustration only) pre-commit hook to demonstrate the problem:

$ cat .git/hooks/pre-commit
#! /bin/sh
echo \$GIT_INDEX_FILE = $GIT_INDEX_FILE
git diff-index --cached --name-only HEAD
exit 1

This hook uses the correct (from a reliability perspective anyway) command, git diff-index --cached HEAD, to find the names of staged files. First, though, it prints the name of the index that is being used to commit the files. (Last, it prevents the commit, since I don't really want to commit any of this.)

I made this executable (in a Git repository for Git itself), and modified a few files without git adding them:

$ git status --short
 M Makefile
 M wt-status.c

(note that the Ms are in the second column). Then:

$ git commit
$GIT_INDEX_FILE = .git/index
$ git commit -a
$GIT_INDEX_FILE = [redacted]/.git/index.lock
Makefile
wt-status.c

The first hook invocation's echo tells us that we're using the real (main) index, and its git diff-index produces no output.

The second invocation tells us that we're using an alternate index file, named .git/index.lock (I trimmed away my source path). It shows two staged files.

Let's go on to do one more thing: I'll git add the modified Makefile and make a second change to Makefile. Now we have:

$ git status --short
MM Makefile
 M wt-status.c

The first line shows us that HEAD:Makefile (in the commit, frozen) differs from :Makefile (in the index, staged) which differs from Makefile (in the work-tree, unstaged), and indeed, we can see the three files are different:

$ git show HEAD:Makefile | head -2
# The default target of this Makefile is...
all::
$ git show :Makefile | head -2
#
# The default target of this Makefile is...
$ head -2 Makefile
# different
# The default target of this Makefile is...

Running git commit vs git commit -a now produces:

$ git commit
$GIT_INDEX_FILE = .git/index
Makefile
$ git commit -a
$GIT_INDEX_FILE = [redacted]/.git/index.lock
Makefile
wt-status.c

If I had not prevented the non--a version of git commit, what would have been committed by git commit is the version of Makefile in the (main / real / .git/index) index, not the version in the work-tree. Hence, if you want to inspect files that would be committed, you should look in the index. You can use git checkout-index to extract files from the index, but be careful not to clobber work-tree versions, which may differ.

What would have been committed by git commit -a is the version of the Makefile in the work-tree, which Git already added to the (non-standard, temporary) .git/index.lock index. Once the git commit -a finished, that non-standard temporary index would become the real index, destroying my intermediate, special staged copy of Makefile. Again, to inspect files that would be committed, look in the index—using the redirected index, as Git will automatically, for both git diff-index and git checkout-index.

(Since I don't know quite what your script needs, I can't make particular recommendations for exactly how to use git checkout-index to extract the files of interest. Consider using --work-tree= and a temporary directory, though.)

(See also my answer to Skip past (full) staging area and commit file or patch directly?, which discusses just what -a, --only, and --include really do internally.)

Upvotes: 12

Related Questions