【问题标题】:Perl -- commit-msg hook doesn't stop the commit when exiting 1Perl -- commit-msg 钩子在退出时不会停止提交 1
【发布时间】:2019-07-19 01:46:00
【问题描述】:

上下文:

我正在使用一些 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

【问题讨论】:

  • 您确定没有启用选项--no-verify
  • 100% 确定。我只是打电话:git commit 然后我输入我的提交信息。
  • 你确定它提交了什么吗?对我来说,它总是中止提交,因为每一行至少包含最后一个换行符,所以它永远不会“中止”或“”。
  • 为什么是git statusgit log -p 说什么?
  • 逻辑是不是反过来了? “中止”不会导致阻止提交...

标签: git perl githooks git-commit


【解决方案1】:

当我第一次安装你的钩子时,我无法让它提交任何东西,但那是因为钩子有太多的负面因素。您的语言老师避免双重否定的警告也会在您编写软件时帮助您。如果一行看起来不错,则钩子会尝试通过在否定意义上测试来寻找有效条件,如果是,则将 $boolean 设置为 1,但只有当 $boolean 为 0 时,exit 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 中提取参数shift

(3) 总是,总是总是检查来自open 的返回值。请注意,如果确实失败,则错误消息包含出现错误的程序的名称 ($0)、它尝试执行的操作 ("open $commit_file") 和错误 ($!)。养成这个习惯。总有一天它会为你省去很多挫折。

(4) 与其将行复制到数组中,不如将它们全部连接成一个标量。使用while (&lt;$fh&gt;) { ... } 来查看$_ 中的每一行,这更符合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 值是错误的,重复好的值是多余的,最后挂出的值很容易被忽略。

【讨论】:

  • 您好!谢谢你的回答!我想了解更多信息: 1. 为什么在这里使用 shift 比 ARGV 更可取? (2)
  • @Antoine 它可以说是更惯用或更优雅的 Perl,我们倾向于对使用 $array[$i] 的单个数组索引或使用 substr 的字符串索引进行索引略有偏差。另一种写法是my($commit_file) = @ARGV;。请注意,my 列表周围的括号很重要,因为它们传递了列表上下文,这样$commit_file 会获取@ARGV 中第一个元素的值。
【解决方案2】:

我认为您的脚本中的逻辑是相反的,即单词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

【讨论】:

    猜你喜欢
    • 2015-08-17
    • 2020-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-11
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    相关资源
    最近更新 更多