我们如何验证推送提交消息?

来自CVS,我们有一个政策,提交的消息应该用一个错误号(简单的后缀“... [9999]”)标记。 CVS脚本在提交期间检查它,如果消息不符合,则拒绝提交。

git hook commit-msg在开发者方面做到了这一点,但我们发现让自动系统检查并提醒我们这很有帮助。

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

我们如何在git推送过程中验证提交消息?


使用更新挂钩

你了解钩子 - 请阅读关于它们的文档! 你可能想要的钩子是更新,每个参数运行一次。 (预接收挂钩在整个推送过程中只运行一次)SO上已经有大量的问题和答案, 取决于你想要做什么,你可能会找到有关如何编写钩子的指导,如果你需要它。

为了强调这确实是可能的,来自文档的引用:

通过确保对象名称是一个提交对象,它是由旧对象名称命名的提交对象的后代,可以使用此钩子来防止强制更新某些引用。 也就是说,强制执行“仅快进”策略。

它也可以用来记录旧的......新状态。

具体细节:

该钩子为每个要更新的引用执行一次,并且需要三个参数:

  • 正在更新的参考名称,
  • 旧的对象名称存储在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
    

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

    为什么你想要更新钩子

    这已在评论中讨论/澄清; 这里是一个总结。

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

    现在,如果用户将更新推送到两个分支,即master和experimental:

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

    假设X是“不好”的提交,即那个会失败commit-msg钩子的提交。 显然,我们不想接受推动掌握。 所以,更新钩拒绝了。 但是实验中的提交没有任何问题! 更新挂钩接受那个。 因此,起源/主人保持不变,但起源/实验得到更新:

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

    预接收钩子只在开始更新引用之前运行一次(在第一次运行更新钩子之前)。 如果你使用它,你将不得不导致整个推动失败,因此说,因为主人有一个错误的提交信息,你不知何故不再相信即使他们的信息很好,实验的提交是好的!


    你可以用下面的pre-receive钩子来完成。 正如其他答案所指出的那样,这是一种保守的,全有或全无的方法。 请注意,它仅保护主分支,并且对主题分支上的提交消息没有限制。

    #! /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 failedn";
        ++$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:nn" . $msg . "n";
          ++$errors, next;
        }
      }
    }
    
    exit $errors == 0 ? 0 : 1;
    

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

    $ git log --pretty=oneline origin/master..HEAD
    354d783efd7b99ad8666db45d33e30930e4c8bb7 second [123]
    aeb73d00456fc73f5e33129fb0dcb16718536489 no bug number
    
    $ git push origin master
    Counting objects: 6, done.
    Delta compression using up to 2 threads.
    Compressing objects: 100% (4/4), done.
    Writing objects: 100% (5/5), 489 bytes, done.
    Total 5 (delta 0), reused 0 (delta 0)
    Unpacking objects: 100% (5/5), done.
    No bug number in aeb73d00456fc73f5e33129fb0dcb16718536489:
    
    no bug number
    
    To file:///tmp/bare.git
     ! [remote rejected] master -> master (pre-receive hook declined)
    error: failed to push some refs to 'file:///tmp/bare.git'

    假设我们通过压缩两个提交并推动结果来解决问题:

    $ git rebase -i origin/master
    [...]
    
    $ git log --pretty=oneline origin/master..HEAD
    74980036dbac95c97f5c6bfd64a1faa4c01dd754 second [123]
    
    $ git push origin master
    Counting objects: 4, done.
    Delta compression using up to 2 threads.
    Compressing objects: 100% (2/2), done.
    Writing objects: 100% (3/3), 279 bytes, done.
    Total 3 (delta 0), reused 0 (delta 0)
    Unpacking objects: 100% (3/3), done.
    To file:///tmp/bare.git
       8388e88..7498003  master -> master

    这是一个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()
    
    链接地址: http://www.djcxy.com/p/23441.html

    上一篇: How do we verify commit messages for a push?

    下一篇: How to fix "corrupted" interactive rebase?