git pre-receive钩子可以评估传入的提交吗? [英] Can git pre-receive hooks evaluate the incoming commit?

查看:102
本文介绍了git pre-receive钩子可以评估传入的提交吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图编写一个服务器端预接收git钩子,以评估提交被推送时的提交.

I'm trying to write a server side pre-receive git hook to evaluate commits as they are being pushed.

根据此处的答案,应该很容易通过搜索git log并用'format:'过滤掉我想要的东西.

According to the answers here, this should easily be possible by searching git log and filtering out what I want with 'format:'.

我创建了以下预提交脚本.

I've created the following pre-commit script.

#!/bin/bash

set -x #for debugging, TODO: remove
echo "parameters are" $@
echo "1 is " $1

#List of banned users
bannedusers=( root )

author_name=$(git show --pretty=oneline --pretty=format:%an | head -n1)
author_email=$(git show --pretty=oneline --pretty=format:%ae | head -n1)

committer_name=$(git show --pretty=oneline --pretty=format:%cn | head -n1)
committer_email=$(git show --pretty=oneline --pretty=format:%ce | head -n1)

commit_users=( "${author_name}" "${committer_name}" )


  for acommituser in "${commit_users[@]}"
  do
    :
    echo $acommituser #for debugging, TODO: remove
    for abanneduser in "${bannedusers[@]}"
    do
      :
        echo $abanneduser #for debugging, TODO: remove
        if [[ $abanneduser == $acommituser ]]; then
         echo "################################################################"
         echo "Commits from $abanneduser are not allowed"
         echo "git config user.name bob builder --replace-all"
         echo "git config user.email bob@aol.com"
         echo "git commit --amend --reset-author"
         echo "################################################################"
         exit 1
        fi
    done
  done

我发现当我在服务器上运行git showgit log时,结果针对的是 current HEAD,而我想查询 incoming 提交.

I find that when I run git show or git log on the server, the results are for the current HEAD, whereas I want to query the incoming commit.

如何修改此脚本以在尚未收到"提交上运行git loggit show?

How could I modify this script to run git log or git show on the 'not-yet-recieved' commit?

推荐答案

您需要使用标准输入中提供的SHA-1 ID:

You need to use the SHA-1 IDs being supplied on standard input:

while read oldsha newsha refname; do
    ... testing code goes here ...
done

然后,测试代码"需要查看至少一些,也许还有所有三个项,具体取决于要执行的测试.

The "testing code" then needs to look at at least some and maybe all three items, depending on the tests to be performed.

如果建议创建参考名称$refname,则$oldsha中的值将为40 0 s.也就是说,$refname(通常类似于refs/heads/masterrefs/tags/v1.2,但是refs/中的任何名称都可以出现:例如,refs/notes/commits)现在在接收存储库中不存在,但将存在并且将指向如果允许更改,请更改为$newsha.

The value in $oldsha will be 40 0s if the reference name $refname is being proposed to be created. That is, $refname (typically something like refs/heads/master or refs/tags/v1.2, but any name in refs/ can appear: refs/notes/commits, for instance) does not exist now in the receiving repository, but will exist and will point to $newsha if you allow the change.

如果建议删除参考名称$refname,则$newsha中的值将为40 0 s.也就是说,$refname现在确实存在,并且指向对象$oldsha;如果您允许更改,则该引用名称将被删除.

The value in $newsha will be 40 0s if the reference name $refname is being proposed to be deleted. That is, $refname does exist now and points to object $oldsha; if you allow the change, that reference-name will be deleted.

如果建议更新参考名称$refname,则两者的值都将为非零,即,当前它指向git对象$oldsha,如果允许更改,则它将指向新对象$newsha代替.

The values of both will be nonzero if the reference name $refname is being proposed to be updated, i.e., it currently points to git object $oldsha, and if you allow the change, it will point to new object $newsha instead.

如果仅运行git loggit show,则git使用通过运行git rev-parse HEAD找到的SHA-1.在典型的接收存储库中,HEAD是指向refs/heads/master的符号引用(文件HEAD实际上包含字符串ref: refs/heads/master),因此您将在分支master上看到最上面的提交(如观察).

If you just run git log or git show, git uses the SHA-1 it finds by running git rev-parse HEAD. In a typical receiving repository, HEAD is a symbolic reference pointing to refs/heads/master (the file HEAD literally contains the string ref: refs/heads/master), so you will see the top-most commit on branch master (as you observed).

您需要专门查看传入的任何新对象.您如何知道传入的是新对象?这取决于所提供的$refname other 引用名称的情况.

You need to look specifically at any new objects coming in. How do you know which new objects are coming in? That depends on what's happening to the supplied $refname, and possibly other refnames as well.

如果要删除refname,则不会有新内容.是否删除任何底层git objects (收集垃圾)取决于refname是否是对这些对象的最后"引用.例如,假设整个标准输入序列由两个指令组成:

If the refname is to be deleted, nothing new is coming in. Whether any underlying git objects will be deleted (garbage collected) depends on whether that refname is the "last" reference to those objects. For instance, suppose the entire standard input sequence consists of two directives:

  • 删除refs/heads/foo
  • 删除refs/tags/v1.1
  • delete refs/heads/foo
  • delete refs/tags/v1.1

进一步假设refs/heads/foo(分支foo)在此提交图图中指向提交F,标记v1.1指向带注释的标记G:

Suppose further that refs/heads/foo (branch foo) points to commit F in this commit-graph diagram, and tag v1.1 points to annotated tag G:

A - B - C - D   <-- refs/heads/master
      \
        E - F   <-- refs/heads/foo
             \
              G <-- refs/tags/v1.1

删除分支foo是安全的",因为没有注释会消失,因为带注释的标记G将通过v1.1标记保留它们.

Deleting branch foo is "safe" in that no commits will go away because annotated tag G will retain them, via the v1.1 tag.

删除标记v1.1是安全的"(ish),因为不会丢失任何提交,因为分支foo将通过refs/heads/foo引用保留这些提交. (带注释的标记对象本身将消失.是否允许这样做取决于您)

Deleting tag v1.1 is "safe"(ish) in that no commits will go away because branch foo will retain them, via the refs/heads/foo reference. (The annotated tag object itself will go away. It's up to you whether to allow this)

但是,同时删除 都不是安全的:提交EF将变得无法访问并被收集. (是否要允许由您决定).

However, deleting both is not safe: commits E and F will become unreachable and will be collected. (It's up to you whether to allow this anyway.)

另一方面,stdin可能与这两个指令一起包含第三个指令:

On the other hand, it's possible that along with those two directives, stdin contains a third directive:

  • 创建refs/heads/foo2指向提交H,其中提交H指向提交G作为其父项[现在重新阅读此内容时,我注意到一个明显的假设,即G是提交对象而不是标记对象.如果我们假设G是一个提交对象,则下面的其余部分是正确的,但是上面的内容至少有一点点错误.但是,一般的想法(DAG受外部引用的保护)仍然是正确的,这应该很有意义.]
  • create refs/heads/foo2 pointing to commit H, with commit H pointing to commit G as its parent [ on re-reading this now, I notice the glaring assumption that G is a commit object rather than a tag object. If we assume G is a commit object the rest of the below is correct, but the above becomes at least a little wrong. However, the general idea—that the DAG is protected by having external references—is still right, and this should mostly make sense.]

在这种情况下,删除foo毕竟是安全的,因为新分支foo2将保留提交H,这将保留提交G.

in which case deletion of foo is safe after all, as the new branch foo2 will retain commit H which will retain commit G.

进行完整的分析很棘手;通常最好进行分段分析以允许安全"操作(无论您决定采用哪种操作),并强制用户以安全"方式分段推送更新(首先创建分支foo2,然后才删除分支foo作为单独的推送).

Doing a complete analysis is tricky; it's often better to just do a piecewise analysis that allows "safe" operations (whatever you decide these are), and force users to push updates piecewise in a "safe" manner (create branch foo2 first, and only then delete branch foo as a separate push, for instance).

如果您只想查看 new 提交,则对于每个参考更新:

If you only want to look at new commits, then, for each reference update:

  • 如果是删除,则允许它(或使用其他规则).
  • 如果是创建或修改,请查找以前无法访问的可访问的提交对象,然后检查这些提交.

在大多数常规"预接收挂钩中,您将使用以下概述的方法,但是对于此特定任务,我们有另一种选择.

In most "normal" pre-receive hooks you'd use the methods outlined below, but we have an alternative for this particular task.

有一个修改的捷径,可以处理最常见,通常最有趣的情况.假设有人提议将refs/heads/foo1234567...更新为9876543....范围内的某些对象可能已经存在,例如1234567是提交C的ID,而9876543是提交E的ID:

There's a short-cut method for modifications that handles the most common, and usually most interesting, cases. Suppose someone proposes updating refs/heads/foo from 1234567... to 9876543.... It's possible that some objects in the range already existed, e.g., perhaps 1234567 is the ID of commit C and 9876543 is the ID of commit E:

A - B - C           <-- refs/heads/foo
          \
            D - E   <-- refs/heads/bar

在这种情况下,它将检查对象D和E.如果刚刚上传了提交DE但还没有 引用,也是如此,即,建议的更新为添加DE,图形当前如下所示:

in which case this will examine objects D and E. This is also true if commits D and E have just been uploaded but have no references yet, i.e., the proposed update is to add D and E and the graph currently looks like this:

A - B - C           <-- refs/heads/foo
          \
            D - E   [no reference yet]

无论哪种情况,一个简单的方法:

In either case, a simple:

git rev-list $oldsha..$newsha

产生您应该查看的对象ID.

produces the object IDs you should look at.

对于新参考文献,没有捷径.例如,假设我们有与上面显示的相同的五个提交,具有相同的refs/heads/foo,但没有refs/heads/bar,并且实际的建议是创建指向Erefs/heads/bar".在这种情况下,我们应该再次查看提交DE,但是没有明显的方法了解D.

For new references, there's no short-cut. For instance, suppose we have the same five commits shown above, with the same refs/heads/foo but no refs/heads/bar, and the actual proposal is "create refs/heads/bar pointing to E". In this case, we should again look at commits D and E, but there's no obvious way to know about D.

仅在某些情况下有效的非显而易见的方法是找到在建议的创建条件下将可到达的对象,而这些对象目前根本无法到达:

The non-obvious way, which only works in some cases, is to find objects that will be reachable given the proposed creation, that are not currently reachable at all:

git rev-list $newsha --not --all

在这种情况下,这将再次生成DE的ID.

In this particular case, this will again produce the IDs for D and E.

现在让我们考虑您的特殊情况,在此情况下,您要查看所有提议添加的提交.这是处理此问题的一种方法.

Now let's consider your particular case, where you want to look at all commits that are being proposed-to-be-added. Here's a way to handle this one.

对于所有建议的更新:

  • 如果这是一个删除,则我们有一些删除.
  • 如果这是一个创建或更新,则我们有一些新的提交;积累新的SHA.

如果我们有一些删除,我们已经积累了一些SHA,请拒绝尝试:​​这太难了.让用户将操作分开.

If we have some deletes and we have accumulated some SHAs, reject the attempt: it's too hard. Make the user separate out the operations.

否则,如果我们没有累积的SHA,则必须仅具有删除(或可能什么也没有,应该不会发生,但无害);允许(退出0).

Otherwise, if we have no accumulated SHAs, we must just have deletes (or maybe nothing at all—should not happen, but harmless); allow this (exit 0).

否则,我们必须具有一些新的SHA-1值.

Otherwise we must have some new SHA-1 values.

以提议的新SHA为起点,查找所有可到达的git对象,不包括任何名称下当前可到达的所有对象.这些都是新对象.

Using the proposed new SHAs as starting points, find all git objects that would be reachable, excluding all objects that are currently reachable under any name. These are all the new objects.

对于每个提交,请对其进行检查以查看是否被禁止.如果是这样,则拒绝整个操作(即使某些部分可以成功执行);和以前一样,很难弄清楚,因此请用户将好"操作与坏"操作分开.

For each one that is a commit, examine it to see if it's forbidden. If so, reject the entire operation (even if some parts could succeed); as before, it's too hard to figure out, so make the user separate out the "good" operations from the "bad" ones.

如果到此为止,一切都很好;允许整个更新.

If we get this far, everything is OK; permit the entire update.

以代码形式:

#! /bin/sh
# (untested)
NULL_SHA1="0000000000000000000000000000000000000000" # 40 0's
new_list=
any_deleted=false
while read oldsha newsha refname; do
    case $oldsha,$newsha in
    *,$NULL_SHA1) # it's a delete
        any_deleted=true;;
    $NULL_SHA1,*) # it's a create
        new_list="$new_list $newsha";;
    *,*) # it's an update
        new_list="$new_list $newsha";;
    esac
done
$any_deleted && [ -n "$new_list" ] && {
    echo 'error: you are deleting some refs and creating/updating others'
    echo 'please split your push into separate operations'
    exit 1
}
[ -z "$new_list" ] && exit 0

# look at all new objects, and verify them
# let's write the verifier function, including a check_banned function...
check_banned() {
    if [ "$1" = root ]; then
        echo "################################################################"
        echo "Commits from $1 are not allowed"
        echo ... rest of message ...
        exit 1
     fi
}
check_commit() {
    check_banned "$(git log -1 --pretty=format:%an $1)"
    check_banned "$(git log -1 --pretty=format:%cn $1)"
}


git rev-list $new_list --not --all |
while read sha1; do
    objtype=$(git cat-file -t $sha1)
    case $objtype in
    commit) check_commit $sha1;;
    esac
done

这篇关于git pre-receive钩子可以评估传入的提交吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆