git 我们如何验证推送的提交消息?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2524101/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do we verify commit messages for a push?
提问by Dale Forester
Coming from CVS, we have a policy that commit messages should be tagged with a bug number (simple suffix "... [9999]"). A CVS script checks this during commits and rejects the commit if the message does not conform.
来自 CVS,我们有一个策略,即提交消息应该标记一个错误号(简单的后缀“... [9999]”)。CVS 脚本在提交期间检查这一点,如果消息不符合则拒绝提交。
The git hook commit-msg does this on the developer side but we find it helpful to have automated systems check and remind us of this.
git hook commit-msg 在开发人员端执行此操作,但我们发现让自动系统检查并提醒我们这一点很有帮助。
During a git push, commit-msg isn't run. Is there another hook during push that could check commit messages?
在 git push 期间,不会运行 commit-msg。推送期间是否有另一个钩子可以检查提交消息?
How do we verify commit messages during a git push?
我们如何在 git push 期间验证提交消息?
采纳答案by Cascabel
Using the update hook
使用更新钩子
You know about hooks - please, read the documentationabout them! The hook you probably want is update, which is run once per ref. (The pre-receive hook is run once for the entire push) There are tons and tons of questions and answers about these hooks already on SO; depending on what you want to do, you can probably find guidance about how to write the hook if you need it.
您了解钩子 - 请阅读有关它们的文档!您可能想要的钩子是 update,它每个 ref 运行一次。(预接收钩子在整个推送过程中运行一次)SO 上已经有大量关于这些钩子的问题和答案;根据您想要做什么,如果需要,您可能会找到有关如何编写钩子的指南。
To emphasize that this really is possible, a quote from the docs:
为了强调这确实是可能的,引自文档:
This hook can be used to prevent forced update on certain refs by making sure that the object name is a commit object that is a descendant of the commit object named by the old object name. That is, to enforce a "fast-forward only" policy.
It could also be used to log the old..new status.
通过确保对象名称是提交对象,该对象是由旧对象名称命名的提交对象的后代,此钩子可用于防止对某些引用进行强制更新。也就是说,强制执行“仅快进”策略。
它也可以用于记录旧的..新的状态。
And the specifics:
以及具体情况:
The hook executes once for each ref to be updated, and takes three parameters:
- the name of the ref being updated,
- the old object name stored in the ref,
- and the new objectname to be stored in the ref.
该钩子为每个要更新的 ref 执行一次,并采用三个参数:
- 正在更新的 ref 的名称,
- 存储在 ref 中的旧对象名称,
- 以及要存储在 ref 中的新对象名。
So, for example, if you want to make sure that none of the commit subjects are longer than 80 characters, a very rudimentary implementation would be:
因此,例如,如果您想确保没有一个提交主题超过 80 个字符,一个非常基本的实现将是:
#!/bin/bash
long_subject=$(git log --pretty=%s .. | egrep -m 1 '.{81}')
if [ -n "$long_subject" ]; then
echo "error: commit subject over 80 characters:"
echo " $long_subject"
exit 1
fi
Of course, that's a toy example; in the general case, you'd use a log output containing the full commit message, split it up per-commit, and call your verification code on each individual commit message.
当然,这是一个玩具示例;在一般情况下,您将使用包含完整提交消息的日志输出,将其拆分为每次提交,并在每个单独的提交消息上调用您的验证码。
Why you want the update hook
为什么你想要更新钩子
This has been discussed/clarified in the comments; here's a summary.
这已在评论中讨论/澄清;这是一个总结。
The update hook runs once per ref. A ref is a pointer to an object; in this case, we're talking about branches and tags, and generally just branches (people don't push tags often, since they're usually just for marking versions).
每个 ref 更新挂钩运行一次。ref 是指向对象的指针;在这种情况下,我们谈论的是分支和标签,通常只是分支(人们不经常推送标签,因为它们通常只是用于标记版本)。
Now, if a user is pushing updates to two branches, master and experimental:
现在,如果用户将更新推送到两个分支,主分支和实验分支:
o - o - o (origin/master) - o - X - o - o (master)
\
o - o (origin/experimental) - o - o (experimental)
Suppose that X is the "bad" commit, i.e. the one which would fail the commit-msg hook. Clearly we don't want to accept the push to master. So, the update hook rejects that. But there's nothing wrong with the commits on experimental! The update hook accepts that one. Therefore, origin/master stays unchanged, but origin/experimental gets updated:
假设 X 是“坏”提交,即会使 commit-msg 挂钩失败的提交。显然,我们不想接受推送到掌握。因此,更新挂钩拒绝了这一点。但是实验性的提交并没有错!更新钩子接受那个。因此, origin/master 保持不变,但 origin/experimental 得到更新:
o - o - o (origin/master) - o - X - o - o (master)
\
o - o - o - o (origin/experimental, experimental)
The pre-receive hook runs only once, just before beginning to update refs (before the first time the update hook is run). If you used it, you'd have to cause the whole push to fail, thus saying that because there was a bad commit message on master, you somehow no longer trust that the commits on experimental are good even though their messages are fine!
pre-receive 钩子只运行一次,就在开始更新 refs 之前(在第一次运行更新钩子之前)。如果你使用它,你将不得不导致整个推送失败,因此说因为 master 上有一个错误的提交消息,你不知何故不再相信实验的提交是好的,即使他们的消息很好!
回答by Greg Bacon
You could do it with the following pre-receive
hook. As the other answers have noted, this is a conservative, all-or-nothing approach. Note that it protects only the master branch and places no constraints on commit messages on topic branches.
您可以使用以下pre-receive
hook 来完成。正如其他答案所指出的,这是一种保守的、全有或全无的方法。请注意,它仅保护 master 分支,并且对主题分支上的提交消息没有限制。
#! /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;
It requires all commits in a push to have a bug number somewhere in their respective commit messages, not just the tip. For example:
它要求推送中的所有提交在其各自的提交消息中的某处都有一个错误编号,而不仅仅是提示。例如:
$ 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'
Say we fix the problem by squashing the two commits together and pushing the result:
假设我们通过将两个提交压缩在一起并推送结果来解决问题:
$ 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
回答by Walty Yeung
This is a python version of pre-receive
, which took me a while to finish, hope it could help others. I mainly use it with Trac, but it could be easily modified for other purposes.
这是 的python 版本pre-receive
,我花了一段时间才完成,希望它可以帮助其他人。我主要将它与 Trac 一起使用,但它可以很容易地修改用于其他目的。
I have also put down the instructions to modify back the historical commit message, which is a little more complicated than I thought.
我也放下了修改回历史提交消息的说明,这比我想象的要复杂一些。
#!/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()
回答by Ferenc Kiss
You didn't mention what is your bug tracker, but if it is JIRA, then the add-on named Commit Policycan do this for without any programming.
你没有提到你的错误跟踪器是什么,但如果它是JIRA,那么名为Commit Policy的附加组件可以在没有任何编程的情况下做到这一点。
You can set up a commit condition which requires the commit message to match a regular expression. If it doesn't, the push is rejected, and the developer must amend (fix) the commit message, then push again.
您可以设置提交条件,要求提交消息与正则表达式匹配。如果没有,推送将被拒绝,开发人员必须修改(修复)提交消息,然后再次推送。
回答by shingara
You need made a script on your pre-receive.
您需要在预收件上制作一个脚本。
In this script you receive the old and new revision. You can check all commit and return false if one of this is bad.
在此脚本中,您将收到旧版本和新版本。如果其中之一不好,您可以检查所有提交并返回 false。