Perl -- commit-msg 钩子在退出时不停止提交 1

Perl -- commit-msg hook doesn't stop the commit when exiting 1

上下文:

我正在使用一些 git-hooks 在 Perl 脚本上自动应用一些格式化选项。在预提交挂钩上,我使用 perltidy 清理并重新格式化我的脚本。我想检查用户在提交消息上放了什么,如果它是空的或者它等同于 "abort",我们想阻止提交并撤消格式修改。

问题:

我删除了 git 挂钩的 .sample 扩展名并使用 chmod u+x .git/hooks/commit-msg 使其可执行,但是当我的脚本 exit 1 提交没有像它应该的那样停止。

commit-msg

This hook is invoked by git-commit[1] and git-merge[1], and can be bypassed with the --no-verify option. It takes a single parameter, the name of the file that holds the proposed commit log message. Exiting with a non-zero status causes the command to abort.

来源: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" ,它会打印 "We shouldn't commit the modifications" 但提交它们会丢弃出口 1...

希望有人能够提供帮助!我是 git-hook 的新手,找不到解决方案。也许我在 Whosebug 上遗漏了一些东西,但我没有找到 post 回答这个问题。

最佳,

安托万

编辑: 我不承诺使用:--no-verify

当我第一次安装你的钩子时,我无法让它提交任何东西,但那是因为钩子有太多的底片。你的语言老师关于避免双重否定的警告也会帮助你编写软件。如果一行看起来不错,钩子会尝试通过在负面意义上进行测试来寻找有效条件,如果是这样,则将 $boolean 设置为 1,然后仅当 $boolean 时才将 exit 0 (表示成功)是 0.

非描述性名称 $boolean 可能部分负责。您可能已经忘记了设置它和您想要产生的退出状态之间的预期含义。此外,只要提交消息的最后一行有效,您的逻辑背后的意图就会失败。

下面的代码在 git 2.17.1.

中以您想要的方式运行
#! /usr/bin/perl -w

use strict;
use warnings;

die "Usage: [=10=] 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 "[=10=]: 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)shift.

@ARGV 中提取参数

(3)总是,总是总是检查return来自 open 的值。请注意,如果失败,错误消息包含出错程序的名称 ([=20=])、它试图执行的操作 ("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: [=10=] <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