【问题标题】:How do we verify commit messages for a push?我们如何验证推送的提交消息?
【发布时间】:2011-02-01 05:15:23
【问题描述】:

来自 CVS,我们制定了一项政策,即提交消息应使用错误编号(简单的后缀“... [9999]”)进行标记。 CVS 脚本会在提交期间检查这一点,如果消息不符合要求,则拒绝提交。

git hook commit-msg 在开发人员端执行此操作,但我们发现让自动化系统检查并提醒我们这一点很有帮助。

在 git push 期间,commit-msg 没有运行。推送期间是否有另一个钩子可以检查提交消息?

我们如何在 git push 期间验证提交消息?

【问题讨论】:

    标签: git cvs commit push


    【解决方案1】:

    使用更新挂钩

    您了解钩子 - 请阅读有关它们的 documentation!您可能想要的钩子是更新,每个参考运行一次。 (预接收钩子在整个推送过程中运行一次)关于这些钩子的问题和答案已经很多了。取决于你想做什么,如果你需要的话,你可能会找到关于如何编写钩子的指导。

    为了强调这确实是可能的,引用文档中的一句话:

    此钩子可用于防止强制更新某些引用,方法是确保对象名称是提交对象,该提交对象是由旧对象名称命名的提交对象的后代。也就是说,强制执行“仅快进”策略。

    它也可以用来记录 old..new 状态。

    以及细节:

    钩子对每个要更新的 ref 执行一次,并接受三个参数:

    • 正在更新的 ref 的名称,
    • 存储在 ref 中的旧对象名称,
    • 以及要存储在引用中的新对象名。

    因此,例如,如果您想确保所有提交主题都不超过 80 个字符,那么一个非常基本的实现将是:

    #!/bin/bash
    long_subject=$(git log --pretty=%s $2..$3 | egrep -m 1 '.{81}')
    if [ -n "$long_subject" ]; then
        echo "error: commit subject over 80 characters:"
        echo "    $long_subject"
        exit 1
    fi
    

    当然,这是一个玩具示例;在一般情况下,您会使用包含完整提交消息的日志输出,将其拆分为每个提交,然后在每个单独的提交消息上调用您的验证码。

    为什么需要更新挂钩

    这已在 cmets 中讨论/澄清;这是一个摘要。

    更新挂钩每个 ref 运行一次。 ref 是指向对象的指针;在这种情况下,我们谈论的是分支和标签,通常只是分支(人们不经常推送标签,因为它们通常只是用于标记版本)。

    现在,如果用户将更新推送到两个分支,主分支和实验分支:

    o - o - o (origin/master) - o - X - o - o (master)
     \
      o - o (origin/experimental) - o - o (experimental)
    

    假设 X 是“错误的”提交,即提交-msg 钩子失败的提交。显然,我们不想接受对 master 的推动。所以,更新钩子拒绝了。但是实验上的提交并没有错!更新钩子接受那个。因此,origin/master 保持不变,但 origin/experimental 得到更新:

    o - o - o (origin/master) - o - X - o - o (master)
     \
      o - o - o - o (origin/experimental, experimental)
    

    pre-receive 钩子只运行一次,就在开始更新 refs 之前(在第一次运行更新钩子之前)。如果你使用它,你将不得不导致整个推送失败,因此说因为 master 上有一个错误的提交消息,你不知何故不再相信实验上的提交是好的,即使它们的消息很好!

    【讨论】:

    • 我认为 OP 正在寻找的钩子是预接收,因为他/她想根据提交消息拒绝整个推送。但是,AFAIK 既不预先接收也不更新接收提交消息作为输入。所以使用 commit-msg 可能是最好的解决方案。
    • @Can:我很确定 OP 想要更新,而不是预先接收。 “全推”是指对所有分支的推。如果用户尝试将更新推送到三个分支,并且只有一个包含无效的提交消息,那么其他两个仍然应该被接受!
    • @Jefromi » 我不确定我是否同意,但我认为这部分是主观的。 IMO 我会将其视为交易:如果您所做的某件事的任何部分不好,请停止整个事情,以便您纠正错误。
    • @John:那将是最直接和可取的。如果任何一个部分无效,整个事情都应该失败。
    • @John:好吧,你可以自己做出判断。不过,这是我的一般想法。将每个分支都视为一个事务,这与 git 中分支的一般理念是一致的。如果该分支有一个错误的提交,您确实会停止推送该分支,即使它有 500 个新的提交也是如此。但是两个不同的分支是两个不同的东西——不同的主题,不同的功能。如果您在两件事上工作并在一件事情上犯了错误,那么它不应该影响另一件事情。
    【解决方案2】:

    您可以使用以下pre-receive hook 来做到这一点。正如其他答案所指出的,这是一种保守的、全有或全无的方法。请注意,它仅保护主分支,并且对主题分支上的提交消息没有任何限制。

    #! /usr/bin/perl
    
    my $errors = 0;
    while (<>) {
      chomp;
      next unless my($old,$new) =
        m[ ^ ([0-9a-f]+) \s+   # old SHA-1
             ([0-9a-f]+) \s+   # new SHA-1
             refs/heads/master # ref
           \s* $ ]x;
    
      chomp(my @commits = `git rev-list $old..$new`);
      if ($?) {
        warn "git rev-list $old..$new failed\n";
        ++$errors, next;
      }
    
      foreach my $sha1 (@commits) {
        my $msg = `git cat-file commit $sha1`;
        if ($?) {
          warn "git cat-file commit $sha1 failed";
          ++$errors, next;
        }
    
        $msg =~ s/\A.+? ^$ \s+//smx;
        unless ($msg =~ /\[\d+\]/) {
          warn "No bug number in $sha1:\n\n" . $msg . "\n";
          ++$errors, next;
        }
      }
    }
    
    exit $errors == 0 ? 0 : 1;
    

    它要求推送中的所有提交在其各自的提交消息中的某处都有一个错误编号,而不仅仅是提示。例如:

    $ git log --pretty=oneline origin/master..HEAD
    354d783efd7b99ad8666db45d33e30930e4c8bb7 秒 [123]
    aeb73d00456fc73f5e33129fb0dcb16718536489 没有错误编号
    
    $ git push 起源大师
    计数对象:6,完成。
    Delta 压缩最多使用 2 个线程。
    压缩对象:100% (4/4),完成。
    写入对象:100% (5/5),489 字节,完成。
    总计 5(增量 0),重复使用 0(增量 0)
    拆包对象:100% (5/5),完成。
    aeb73d00456fc73f5e33129fb0dcb16718536489 中没有错误编号:
    
    没有错误编号
    
    到文件:///tmp/bare.git
     ! [remote denied] master -> master (pre-receive hook denied)
    错误:未能将一些参考推送到“file:///tmp/bare.git”

    假设我们通过将两个提交压缩在一起并推送结果来解决问题:

    $ git rebase -i origin/master
    [...]
    
    $ git log --pretty=oneline origin/master..HEAD
    74980036dbac95c97f5c6bfd64a1faa4c01dd754 秒 [123]
    
    $ git push 起源大师
    计数对象:4,完成。
    Delta 压缩最多使用 2 个线程。
    压缩对象:100% (2/2),完成。
    写入对象:100% (3/3),279 字节,完成。
    总计 3(增量 0),重复使用 0(增量 0)
    拆包对象:100% (3/3),完成。
    到文件:///tmp/bare.git
       8388e88..7498003 主 -> 主

    【讨论】:

      【解决方案3】:

      这是pre-receive的python版本,我花了一段时间才完成,希望它可以帮助别人。我主要将它与 Trac 一起使用,但它可以很容易地修改用于其他目的。

      我也记下了修改回历史提交消息的说明,这比我想象的要复杂一些。

      #!/usr/bin/env python
      import subprocess
      
      import sys 
      import re
      
      def main():
          input  = sys.stdin.read()
          oldrev, newrev, refname = input.split(" ")
          separator = "----****----"
      
      
          proc = subprocess.Popen(["git", "log", "--format=%H%n%ci%n%s%b%n" + separator, oldrev + ".." +  newrev], stdout=subprocess.PIPE)
          message = proc.stdout.read()
          commit_list = message.strip().split(separator)[:-1] #discard the last line
      
          is_valid = True
      
          print "Parsing message:"
          print message
      
          for commit in commit_list:
              line_list = commit.strip().split("\n")
              hash = line_list[0]
              date = line_list[1]
              content = " ".join(line_list[2:])
              if not re.findall("refs *#[0-9]+", content): #check for keyword
                  is_valid = False
      
          if not is_valid:
              print "Please hook a trac ticket when commiting the source code!!!" 
              print "Use this command to change commit message (one commit at a time): "
              print "1. run: git rebase --interactive " + oldrev + "^" 
              print "2. In the default editor, modify 'pick' to 'edit' in the line whose commit you want to modify"
              print "3. run: git commit --amend"
              print "4. modify the commit message"
              print "5. run: git rebase --continue"
              print "6. remember to add the ticket number next time!"
              print "reference: http://stackoverflow.com/questions/1186535/how-to-modify-a-specified-commit"
      
              sys.exit(1)
      
      main()
      

      【讨论】:

        【解决方案4】:

        你需要在你的预接收上制作一个脚本。

        在此脚本中,您会收到旧版本和新版本。您可以检查所有提交,如果其中一个是错误的,则返回 false。

        【讨论】:

          【解决方案5】:

          您没有提到您的错误跟踪器是什么,但如果是 JIRA,那么名为 Commit Policy 的插件无需任何编程即可完成此操作。

          您可以设置提交条件,要求提交消息与正则表达式匹配。如果不是,则推送被拒绝,开发者必须修改(修复)提交消息,然后再次推送。

          【讨论】:

            猜你喜欢
            • 2015-09-21
            • 1970-01-01
            • 2019-10-25
            • 2016-03-01
            • 1970-01-01
            • 1970-01-01
            • 2013-06-15
            • 2023-03-12
            • 2010-10-02
            相关资源
            最近更新 更多