views:

454

answers:

3

I am writing a pre-commit hook. I want to run php -l against all files with .php extension. However I am stuck.

I need to obtain a list of new/changed files that are staged. deleted files should be excluded.

I have tried using git diff and git ls-files, but I think I need a hand here.

+1  A: 

git diff --cached --name-status will show a summary of what's staged, so you can easily exclude removed files, e.g.:

M       wt-status.c
D       wt-status.h

This indicates that wt-status.c was modified and wt-status.h was removed in the staging area (index). So:

steve@arise:~/src/git <master>$ git diff --cached --name-status | awk '$1 != "R" { print $2 }'
wt-status.c
wt-status.h

You will have to jump through extra hoops to deal with filenames with spaces in though (-z option to git diff and some more interesting parsing)

araqnid
Thanks, that's a good start. However, if I change a file without staging it, it's still displayed. I am running git version 1.7.0.1.147.g6d84b (recent custom build). Not sure if this is intended behavior.
igorw
That sounds odd. The "--cached" switch should make it only show files that have been staged: although I'm testing this with 1.6.5, it seems surprising that that would have changed... does "git diff --cached" on its own show the unstaged changes?
araqnid
After some debugging I was able to track it back to an other cause. Thanks a lot!
igorw
In case anyone is interested in the result: http://github.com/evil3/phpbb3/compare/git-tools
igorw
A: 

A slightly neater way of obtaining the same list is:

git diff --cached --name-only --diff-filter=ACM

This will return the list of files that need to be checked.

But just running php -l on your working copy may not be the right thing to do. If you are doing a partial commit i.e. just selecting a subset of the differences between your current working set and the HEAD for the commit, then the test will be run on your working set, but will be certifying a commit that has never existed on your disk.

To do it right you should extract the whole staged image to a temp area and perform the test there .

rm -rf $TEMPDIR
mkdir -p $TEMPDIR
git checkout-index --prefix=$TEMPDIR/ -af
git diff --cached --name-only --diff-filter=ACM | xargs -n 1 -I '{}' \bin\echo TEMPDIR/'{}' | grep \\.php | xargs -n 1 php -l

See Building a better pre-commit hook for Git for another implementation.

LarryH
It is actually possible to pipe the file contents to `php -l`. And that's what we ended up with. See here: http://github.com/phpbb/phpbb3/blob/develop-olympus/git-tools/hooks/pre-commit
igorw