Reputation: 28259
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.
Upvotes: 98
Views: 79588
Reputation: 1908
A slightly neater way of obtaining the same list is:
git diff --cached --name-only --diff-filter=ACMR
This will return the list of files that need to be checked by a pre-commit hook (A=Added, C=Copied, M=Modified, R=Renamed).
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.
Upvotes: 120
Reputation: 2200
If anyone is looking for pre-push
check, then
git diff --name-only @{push}..
can be used.
Example:
monitored_folder="API/API.Site/Controllers"
changed_files=$(git diff --name-only @{push}..)
for file in $changed_files; do
if [[ $file == $monitored_folder* ]]; then
echo "Changes detected in '$monitored_folder'. Aborting push."
exit 1
else
echo $file;
fi
done
Upvotes: 0
Reputation: 1329082
eddygeek's answer cites the .git/hooks/pre-commit.sample
example.
git diff --cached --name-only --diff-filter=ACM -z | xargs -0 ...
That example just changed with Git 2.44 (Q1 2024): the sample pre-commit hook that tries to catch the introduction of new paths that use potentially non-portable characters did not notice an existing path getting renamed to such a problematic path when rename detection was enabled.
See commit d9fd71f (30 Nov 2023) by Julian Prein (druckdev
).
(Merged by Junio C Hamano -- gitster
-- in commit 145336e, 20 Dec 2023)
hooks--pre-commit
: detect non-ASCII when renamingSigned-off-by: Julian Prein
When diff.renames is turned on, the diff-filter will not return renamed files (or copied ones with diff.renames=copy) and potential non-ASCII characters would not be caught by this hook.
Use the plumbing command diff-index instead of the porcelain one to not be affected by
diff.rename
.
So instead of:
git diff --cached --name-only --diff-filter=A -z $against
You now have, using git diff-index
instead:
git diff-index --cached --name-only --diff-filter=A -z $against
Upvotes: 0
Reputation: 11
to understand that the files have changed in a specific folder, I do this:
modifiedFrontendFiles=$(git diff --cached --name-status --relative=frontend)
if [ -n "$modifiedFrontendFiles" ]; then
npm run lint
npm run lint-css
npm run format
git add .
fi
in my case i check that the changes are in the frontend folder
Upvotes: 1
Reputation: 451
Here is what I use for my Perl checks:
#!/bin/bash
while read st file; do
# skip deleted files
if [ "$st" == 'D' ]; then continue; fi
# do a check only on the perl files
if [[ "$file" =~ "(.pm|.pl)$" ]] && ! perl -c "$file"; then
echo "Perl syntax check failed for file: $file"
exit 1
fi
done < <(git diff --cached --name-status)
for PHP it will look like this:
#!/bin/bash
while read st file; do
# skip deleted files
if [ "$st" == 'D' ]; then continue; fi
# do a check only on the php files
if [[ "$file" =~ ".php$" ]] && ! php -l "$file"; then
echo "PHP syntax check failed for file: $file"
exit 1
fi
done < <(git diff --cached --name-status)
Upvotes: 17
Reputation: 133792
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, to check only files that weren't removed:
steve@arise:~/src/git <master>$ git diff --cached --name-status | awk '$1 != "D" { 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)
Upvotes: 59
Reputation: 4510
None of the answers here support filenames with spaces. The best way for that is to add the -z
flag in combination with xargs -0
git diff --cached --name-only --diff-filter=ACM -z | xargs -0 ...
This is what is given by git in built-in samples (see .git/hooks/pre-commit.sample)
Upvotes: 27
Reputation: 833
git diff --cached is not sufficient if the commit call was specified with the -a flag, and there is no way to determine if that flag has been thrown in the hook. It would help if the arguments to commit should be available to the hook for examination.
Upvotes: 2