我正在写一个git precommit hook,我相信它是由bash执行的。我需要检查修改后的文件,如果内容与模式匹配,则显示错误消息。
[{
fileName: "*.html",
pattern: "\<font\>",
message: "Don't use font. Use style sheet istead!"
},
{
fileName: "/Admin/*",
pattern: "string\.Format\(",
message: "Don't use string.format. Use string interpolation istead!"
},
....
]
我有很多这样的规则。如何在bash中定义这些规则?如果bash不擅长这个,我该如何实现precommit hook?
答案 0 :(得分:2)
在Git方面:只要您的操作系统可以通过exec
样式系统调用运行挂钩,就可以用任何语言编写挂钩。通常,脚本由某些shell运行,通常是/bin/sh
,通常不是 bash,但在类Unix系统上,如果脚本的第一行读取#! /bin/bash
或例如,#! /usr/bin/env bash
,脚本肯定会用bash运行。您可以使用#! /usr/bin/env python
使用Python运行脚本,依此类推。
(同样在Git方面,在预提交钩子中,您可以使用git diff --cached --name-status
来获取提议提交中与HEAD
提交中的文件不同的文件列表,包括精确的方式它们不同。添加明确的--find-renames
或--no-renames
以明确启用或禁用重命名检测,无论用户配置设置如何;或使用git diff-index
代替git diff
- 其余部分选项与忽略用户指定的配置项相同。)
在shell脚本方面,bash有很多功能,但最简单的可能就是通过代码运行文件名,并使用显式的glob样式模式匹配和操作:
gripe() {
echo "$@" 1>&2
GRIPES=$((GRIPES + 1))
}
check_file() {
case "$1" in
*.html) if fgrep "<font>" "$2"; then
gripe "Don't use font. Use style sheet instead!"; fi;;
*/Admin/*) if grep "string\.Format(" "$2"; then gripe ...; fi;;
esac
}
现在我们进入整个操作中最特殊的部分:要提交的文件的形式在索引中存在,而不一定在工作树中。这就是为什么上面check_file()中的代码有两个参数。一个是文件的名称,因为它将出现在提交中;另一个是临时文件的名称,我们将在检查之前将索引内容提取到此文件中。
如果您愿意假设没有人会播放任何尚未出现在工作树中的内容,那么您可以跳过很多这种复杂性。使用--name-only
和--diff-filter
,我们可以获取A
dded或M
odified的文件的文件名列表,并查看其工作树版本:
GRIPES=0
git diff-index --cached --diff-filter=AM HEAD |
while read path; do
check_file "$path" "$path"
done
要做好正确的工作,我们需要将每个文件提取到一个临时文件中(例如,可能使用git checkout-index --temp
)并扫描临时文件,然后删除临时文件。
当我们全部完成后,shell变量$ GRIPES会计算投诉数量,这可以用来阻止提交。