背景:
我正在使用一些git-hooks在Perl脚本上自动应用一些格式化选项。在预提交钩子上,我使用perltidy清理并重新格式化我的脚本。我想检查用户放置提交消息的内容,如果它是空的或者等同于“abort”,我们希望阻止提交并撤消格式修改。
问题:
我删除了git hook的.sample扩展并使用chmod u+x .git/hooks/commit-msg
使其可执行但是当我的脚本exit 1
时,提交不会像它应该停止。
提交-MSG
这个钩子由git-commit [1]和git-merge [1]调用,可以用--no-verify选项绕过。它需要一个参数,即包含建议的提交日志消息的文件的名称。以非零状态退出会导致命令中止。
来源:https://git-scm.com/docs/githooks#_commit_msg
#!/usr/bin/perl -w
use strict;
use warnings;
# Get the path to the files in which we have the commit message
my $commit_file = $ARGV[0];
# Read the file and extract the commit message (lines which don't start with #)
my @commit_msg;
open(my $fh, "<", "$commit_file");
while (my $line = <$fh>) {
if (substr($line, 0, 1) ne "#") {
push(@commit_msg, $line);
}
}
# Check the message isn't empty or we don't have a "abort" line
my $boolean = 0;
foreach my $line (@commit_msg) {
if ($line ne "abort" && $line ne "") {
$boolean = 1;
}
}
if ($boolean == 0) {
print "We should commit the modifications\n";
exit 0; # Don't prevent commit
}
else {
print "We shouldn't commit the modifications\n";
exit 1; # Prevent commit
}
该脚本是可执行的并且有效!如果我在提交一些东西时输入“abort”,它会打印“我们不应该提交修改”,但是让它们丢弃退出1 ...
我希望有人能够提供帮助!我是git-hook的新手,无法找到解决方案。也许我错过了Stackoverflow上的一些内容,但我找不到回复此问题的帖子。
最好,
安托万
编辑:我不提交使用:--no-verify
当我第一次安装你的钩子时,我无法让它提交任何东西,但这是因为钩子有太多的负面因素。您的语言教师在避免双重否定方面的警告也会在编写软件时帮助您。钩子试图通过在负面意义上测试线条看起来是否正常来寻找有效条件,如果是这样,则将$boolean
设置为1,但是仅当exit 0
为0时才设置$boolean
(表示成功)。
非描述性名称$boolean
可能部分负责。在设置它和您想要生成的退出状态之间,您可能已经忘记了预期的意义。此外,只要提交消息的最后一行有效,您在逻辑背后的意图就会失败。
下面的代码以git 2.17.1的方式运行。
#! /usr/bin/perl -w
use strict;
use warnings;
die "Usage: $0 commit-log-message\n" unless @ARGV == 1; # (1)
# Get the path to the files in which we have the commit message
my $commit_file = shift; # (2)
# Read the file and extract the commit message (lines which don't start with #)
my $commit_msg = "";
open my $fh, "<", $commit_file or die "$0: open $commit_file: $!"; # (3)
while (<$fh>) { # (4)
next if /^#/; # (5)
$commit_msg .= $_;
}
# Check the message isn't empty or we don't have an "abort" line
my $valid_commit_msg = $commit_msg ne "" && $commit_msg !~ /^abort$/m; # (6)
if ($valid_commit_msg) { # (7)
print "We should commit the modifications\n";
exit 0; # Don't prevent commit
}
else {
print "We shouldn't commit the modifications\n";
exit 1; # Prevent commit
}
(1)是的,git应该提供带有日志消息的文件名,但是如果代码被复制或以其他方式安装在错误的钩子中,则应对其进行健全性检查。
(2)用@ARGV
从shift
中获取论据。
(3)始终始终检查open
的返回值。请注意,如果失败的错误消息包含有错误的程序的名称($0
),它尝试做什么("open $commit_file"
)和错误($!
)。养成这个习惯。有一天它会为你节省很多挫折。
(4)不是将行复制到数组中,而是将它们连接成一个标量。使用while (<$fh>) { ... }
查看$_
中的每一行,这是更惯用的Perl并使您的代码变得清晰。
(5)跳过评论行然后变成一个简单的next if /^#/;
。
(6)说出你的意思。而不是机制($boolean
)命名你的意图。您想知道提交消息在通过之前是有效的。有效的提交消息必须满足两个条件:
abort
。在Perl中呈现,这是
my $valid_commit_msg = $commit_msg ne "" && $commit_msg !~ /^abort$/m;
几个笔记:
!~
运算符反转正则表达式匹配的意义,即$commit_msg
不得包含abort
。/m
开关用于多线模式。它使得^
和$
锚点在目标内的行的开头和结尾处匹配,而不是仅与最左边和最右边的字符匹配。(7)以自然读取的方式使用$valid_commit_msg
作为布尔值。
if ($valid_commit_msg) { ... }
优于if ($valid_commit_msg == 0) { ... }
,因为0值是错误的,重复好的值是多余的,并且在末尾挂出的值很容易被忽略。
我认为你的脚本中的逻辑是相反的,即单词abort
不会导致退出代码1。
我认为以下应该有效:
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
my($commit_msg_file) = @ARGV
or die "usage: $0 <commit message file>\n";
open(my $fh, '<', $commit_msg_file);
my $do_commit;
while (<$fh>) {
# skip comment or empty lines
next if /^#/ || /^\s*$/;
# check for the word "abort" on its own line
last if (/^abort$/);
# at least one non-empty non-comment line detected
$do_commit++;
last;
}
close($fh);
if ($do_commit) {
print "We should commit the modifications\n";
exit 0; # Don't prevent commit
}
print "We shouldn't commit the modifications\n";
exit 1; # Prevent commit