`git diff` does not work when run from a git pre-commit hook

落花浮王杯 提交于 2019-12-24 00:54:49

问题


I have a git pre-commit hook that does some style checking on any modified files before committing.

The implementation is irrelevant, but it starts by calling git diff. Here's what i have in (repo)/.git/hooks/pre-commit.

#!/bin/sh


echo "=== Running script..."
git diff
echo "=== Done running script..."

# Other stuf
# ....

# Always exit with 1 so pre-commit hook always fails.
# Useful for testing
exit 1

When I actually try committing something, the pre-commit hook correctly fires, but the git diff command doesn't output anything (there are definitely modified files)

> git commit --all -m "foo"
=== Running script...
=== Done running script...

However if I run the pre-commit hook script directly/manually, it does work

> ./.git/hooks/pre-commit
=== Running script...
(... outputs git diff ...)
=== Done running script...

What's different about git calling the hook versus me manually calling it? It runs as the same user either way (my username)

I've also tried suggestions from this thread, but unset GIT_DIR, --git-dir=, and work-tree= didn't fix anything.

Thanks!


回答1:


You need to use git diff --cached because the changes are already staged.




回答2:


As ishegg said, you will need git diff --cached here, but that's not necessarily the whole story.

There are two traps to be wary of here. The first is what git diff does: it compares two trees (or tree-like things). The second has to do with the very phrase the index.

The index, and selecting trees for git diff

Quite often, the two trees you diff are the trees associated with two specific commits:

git diff <hash1> <hash2>

(or the same with <hash1>..<hash2>, which—unlike most Git commands—doesn't treat the two hashes the way the two-dot .. operation is described in the SPECIFYING RANGES section of the gitrevisions documentation).

It's also pretty common to have one of the two trees be your work-tree, which is what happens when you run:

git diff HEAD

for instance: this compares the commit named by HEAD—i.e., the current branch-tip commit—to the current work-tree.

Using:

git diff --cached

tells Git to compare the HEAD commit to the tree represented by your index. Git's index, also called the staging area or the cache, is where Git builds up the next commit to make. It's why you must run git add before committing: the git add command copies files from the work-tree to the index.

Using:

git diff

with no arguments at all chooses the index as the first tree, and the work-tree as the second tree. Once everything has been copied from the work-tree into the index, this particular diff will be empty, which is what you are seeing here.

The index vs an index

The second trap lies in the fact that you are using git commit --all.

While Git talks about the index, and there is in fact one particular, distinguished index to go with each work-tree, Git actually allows for more than one index, using exactly one at a time.

When you use git commit --all or pass file names to git commit, Git makes a temporary index that it uses during the pre-committing process. To indicate that there is a temporary index in play, Git sets the environment variable GIT_INDEX_FILE. This temporary index gets used instead of the ordinary index, up until either the commit is allowed and committed, or until the commit is rejected.

If the commit is rejected, Git just removes the temporary index, and everything goes back to the way it was before. If things were not git add-ed, they are still not git add-ed.

If the commit is accepted, the temporary index becomes the "real" or "main" index, more or less. Here things can get complicated, because you can stage some items in the real index, run git commit --all or git commit --include <paths> or git commit --only <paths>, and if the commit succeeds, the difference between the initial (main or real) index and the temporary index matters.

If you plan to do special tricks with files in the work-tree and/or their copies in the index, you may want to simply reject any attempt to work with a temporary index, so as to avoid these complications. However, that will preclude the use of --all.



来源:https://stackoverflow.com/questions/45721633/git-diff-does-not-work-when-run-from-a-git-pre-commit-hook

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!