首页 > 解决方案 > Perl -- commit-msg 钩子在退出时不会停止提交 1

问题描述

语境 :

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

问题 :

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

提交消息

这个钩子由 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
}

该脚本是可执行的并且有效!如果我在提交某些内容时输入“中止”,它会打印“我们不应该提交修改”,但提交它们会丢弃退出 1...

我希望有人能提供帮助!我是 git-hook 的新手,找不到解决方案。也许我错过了 Stackoverflow 上的一些东西,但我没有找到回答这个问题的帖子。

最好的,

安托万

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

标签: gitperlgithooksgit-commit

解决方案


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

非描述性名称$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从with中提取论点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模式末尾的开关用于多线模式。它使^and$锚点匹配目标中行的开头和结尾,而不是只匹配最左边和最右边的字符。

(7)以自然阅读的方式$valid_commit_msg用作布尔值。

if ($valid_commit_msg) { ... }

更可取,if ($valid_commit_msg == 0) { ... }因为 0 值是错误的,重复好的值是多余的,最后挂出的值很容易被忽略。


推荐阅读