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

Posted

技术标签:

【中文标题】Git 预提交挂钩:更改/添加的文件【英文标题】:Git pre-commit hook : changed/added files 【发布时间】:2011-01-25 15:05:03 【问题描述】:

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

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

我尝试过使用git diffgit ls-files,但我想我需要帮助。

【问题讨论】:

phpadvent.org/2008/dont-commit-that-error-by-travis-swicegood 这很好。但是,它不处理部分暂存的文件。请参阅我对@LarryH 回答的评论。 【参考方案1】:

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(最近的自定义版本)。不确定这是否是预期行为。 这听起来很奇怪。 “--cached”开关应该让它只显示已暂存的文件:虽然我正在用 1.6.5 测试它,但它似乎会改变......自己展示未分级的变化? 经过一些调试后,我能够将其追溯到其他原因。非常感谢! @igorw,我很感兴趣,但链接已失效。 请注意,如果唯一需要的是文件名,则存在--name-only 而不是--name-status。可以减少额外的 awk 箍。【参考方案2】:

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

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

请参阅 Building a better pre-commit hook for Git 了解其他实现。

【讨论】:

实际上可以将文件内容通过管道传送到php -l。这就是我们最终的结果。见这里:github.com/phpbb/phpbb3/blob/develop-olympus/git-tools/hooks/… 要检查暂存文件的语法,您可以使用git show :FILENAME | php -l --diff-filter 应该是“ACMR”,因为重命名的文件 (R) 也可以有变化。【参考方案3】:

这是我用于 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)

【讨论】:

相当不错,但不适用于部分暂存的文件,因为它会读取整个文件。 谢谢!我修改了您的代码,并在完成后放置了 不能再次编辑我的评论,所以 synthax 实际上是 '***.com/a/7390610/5203829【参考方案4】:

如果使用 -a 标志指定了提交调用,则 git diff --cached 是不够的,并且无法确定该标志是否已被抛出钩子。如果提交的参数应该可供钩子检查,这将有所帮助。

【讨论】:

git diff --cached 似乎就足够了。但是,我相信如果你在你的钩子里运行 git status --porcelain ,所有将被处理的文件都不会有空白或 ?在输出的第一个位置。我还没有完全测试它,但到目前为止,它在我的仓库中的所有条件下都保持不变,混合了新的、添加的、修改过的文件,我尝试提交显式文件,默认文件集,-一切。那么为什么要使用 git status 而不是 git diff 呢?我认为它更容易解析。 git status --porcelain | grep -E -v '^[? ]' git status --porcelain | perl -ane 'print $F[1],qq(\n) if m/^[ACM] /' 是一个更好的答案。它具有使用 --porcelain 选项的优点,保证永远不会改变。如果 perl 对您来说太重,请使用您自己的解析器。【参考方案5】:

这里的答案都不支持带空格的文件名。最好的方法是添加-z 标志和xargs -0

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

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

【讨论】:

以上是关于Git 预提交挂钩:更改/添加的文件的主要内容,如果未能解决你的问题,请参考以下文章

text Git预提交挂钩(.git / hooks / pre-commit)以防止意外提交调试代码(在源注释中添加NOCOMMIT)

如何在预提交挂钩中使用 git diff 的退出代码

预提交挂钩失败

Perforce客户端预提交挂钩

Git 预提交挂钩以在文件中查找文本

预提交/挂钩:没有这样的文件或目录