Git预提交钩子:更改/添加的文件


71

我正在编写一个预提交的钩子。我想php -l对所有带有.php扩展名的文件运行。但是我被困住了。

我需要获取暂存的新文件/更改文件的列表。删除的文件应排除在外。

我尝试使用git diffgit ls-files,但是我认为我需要帮助。



很好 但是,它不处理部分暂存的文件。请参阅我对@LarryH的回答的评论。
igorw

Answers:


50

git diff --cached --name-status 将显示上演的摘要,因此您可以轻松排除已删除的文件,例如:

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

这表明在暂存区域(索引)中wt-status.c被修改,wt-status.h被删除。因此,仅检查未删除的文件:

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

您将不得不跳过多余的圈来处理带有空格的文件名(git diff的-z选项和一些更有趣的解析)


谢谢,这是一个好的开始。但是,如果我更改文件而不进行暂存,则仍会显示该文件。我正在运行git版本1.7.0.1.147.g6d84b(最近的自定义版本)。不知道这是否是预期的行为。
igorw

听起来很奇怪。“ --cached”开关应使其仅显示已暂存的文件:尽管我正在使用1.6.5进行测试,但令人惊讶的是,这种情况已经改变了……在其上执行了“ git diff --cached”自己显示未进行的更改?
araqnid 2010年

经过一些调试后,我能够将其追溯到其他原因。非常感谢!
igorw

@igorw,我会感兴趣,但链接已死。
西蒙(Simon)

只是要注意,如果只需要文件名,则存在--name-only而不是--name-status。可以减少多余的awk箍。
LP

94

获得相同列表的更简洁的方法是:

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

这将返回需要检查的文件列表。

但是,仅php -l在工作副本上运行可能不是正确的选择。如果您正在执行部分提交,即仅选择当前工作集和HEAD的差异的子集作为提交,则测试将在您的工作集上运行,但将证明您的从未存在过的提交磁盘。

要正确执行此操作,应将整个暂存的图像提取到临时区域,然后在此处进行测试。

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

有关其他实现,请参见为Git构建更好的预提交挂钩


4
实际上,可以将文件内容传输到php -l。这就是我们最终得到的结果。在这里看到:github.com/phpbb/phpbb3/blob/develop-olympus/git-tools/hooks/...
igorw

2
要检查暂存文件的语法,可以使用git show :FILENAME | php -l
Aad Mathijssen 2014年

6
--diff-filter可能应该是“ ACMR”,因为重命名的文件(R)也可以进行更改。
Droopycom '18

14

这里没有答案支持带空格的文件名。最好的方法是将-z标志与xargs -0

git diff --cached --name-only --diff-filter=ACM -z | xargs -0 ...

这是git在内置示例中给出的(请参阅.git / hooks / pre-commit.sample


13

这是我用于Perl检查的内容:

#!/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)

对于PHP,它将如下所示:

#!/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)

2
很好,但是不适用于部分暂存的文件,因为它会读取整个文件。
igorw 2010年

谢谢 !我修改了您的代码,并在完成后放置了<<< $(git diff --cached --name-status)而不是使用管道,这样就不会在循环中启动任何子shell。它允许循环中的变量更新稍后使用。提交答案更新以供审核。最佳
lcetinsoy

无法再次编辑我的评论,所以synthax实际上是'<<$(command)像stackoverflow.com/a/7390610/5203829
lcetinsoy

0

如果使用-a标志指定了提交调用,则git diff --cached是不够的,并且无法确定该标志是否已被挂起。如果提交的参数对于钩子可用,将很有帮助。


git diff --cached DOES似乎足够了。但是,我相信,如果在钩子中运行git status --porcelain,将要处理的所有文件都不会有空格或?。在输出的第一个位置。我尚未对其进行全面测试,但到目前为止,它在我的存储库中处于所有条件下都可以使用,它包含新的,添加的,修改的文件,在这些文件中,我尝试提交显式文件,默认文件集-一个为一切。那么,为什么要使用git status而不是git diff?我认为解析起来更容易。
mpersico '16

git status --porcelain | grep -E -v '^[? ]'
mpersico '16

git status --porcelain | perl -ane 'print $F[1],qq(\n) if m/^[ACM] /' 是一个更好的答案。它具有使用--porcelain选项的优势,可以保证永远不变。如果perl对您来说太重了,请使用自己的解析器。
mpersico '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.