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

时间:2019-02-25 15:45:41

标签: git perl githooks git-commit

上下文:

我正在使用一些git-hooks自动在Perl脚本上应用某些格式设置选项。在预提交的钩子上,我使用perltidy清理并重新格式化了脚本。我想检查用户在提交消息上输入了什么内容,如果它为空或等于“中止”,我们要阻止提交并撤消格式修改。

问题:

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

  

commit-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上的一些东西,但没有找到回答此问题的帖子。

最好

Antoine

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

2 个答案:

答案 0 :(得分:3)

当我第一次安装钩子时,我无法使其执行任何事情,但这是因为钩子的底片太多。您的语言老师为避免双重否定而提倡的建议也将帮助您编写软件。挂钩尝试通过否定的测试来检验有效条件,如果行看起来不错,则将$boolean设置为1,但只有在exit 0时将$boolean设置为1(表示成功)。是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应该在日志消息中提供文件名,但是要进行完整性检查,以防代码被复制或以其他方式安装在错误的钩子中。 / p>

(2)@ARGVshift中提取参数。

(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;

一些注意事项:

  • !~运算符会反转正则表达式匹配的含义, ie $commit_msg必须包含abort
  • 模式结尾处的/m开关用于多行模式。它使^$锚在目标中的行的开头和结尾处匹配,而不仅仅是最左边和最右边的字符。

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

if ($valid_commit_msg) { ... }

if ($valid_commit_msg == 0) { ... }更可取,因为0值是错误的值,重复良好的值是多余的,并且结尾处闲散的值很容易被忽略。

答案 1 :(得分: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