【问题标题】:Query on setting up a pre-receive hook in git在 git 中设置 pre-receive 钩子的查询
【发布时间】:2012-04-30 14:08:18
【问题描述】:

我想设置一个 pre-receive hook 来执行这样的逻辑

  • 检查是否有任何合并提交
  • 检查是否推送了新分支
  • 检查是否有多次提交。

如果存在上述任何一项,则与用户交互以检查他是否要推送更改,如果是,则推送更改,否则放弃。

有没有可能在 git 的 pre-receive hook 中实现上述功能?

【问题讨论】:

    标签: git


    【解决方案1】:

    您不能在 pre-receive 钩子中与用户交互,至少一般情况下是这样,因为用户可能在其他机器上执行“ssh”,而标准输入不会发送给用户。 (您可能可以通过检查 uid 和/或用户名等来拼凑一些东西,然后转义到“回调”用户的脚本。我为您留下了那个方向的实验。)您可以进行所有这些检查,不过。

    预接收钩子(在标准输入上)获取一系列行:

    <old-value> SP <new-value> SP <ref-name> LF
    

    (引自githooks(5))。如果 ref-name 具有以下形式:

    refs/heads/<branchname>
    

    然后正在创建、删除或更新给定的分支。如果&lt;old-value&gt; 为 40 0s,则正在创建分支;如果 &lt;new-value&gt; 为 40 0s,则将其删除;否则正在更新。 (除了 refs/heads/* 之外还有其他内容;完整列表请参见 git-send-email 挂钩。)

    如果分支正在更新,&lt;old-value&gt; 是它过去指向的提交 ID,&lt;new-value&gt; 是如果允许更新它将指向的提交 ID(这取决于接收钩子更新钩子)。

    这是一个简单地检测您的第二个和第三个案例(加上删除)的钩子。要确定是否有合并,您必须检查 $between 中的每个 rev 并查看是否有任何合并(即,有多个父级)。要停止提交,请退出非零而不是返回 0。

    #! /bin/sh
    
    check()
    {
        local old=$1 new=$2 longref=$3
        local between rev
    
        if expr $old : '^00*$' >/dev/null; then
            echo creating new branch ${longref#refs/heads/}
            return 0
        fi
        if expr $new : '^00*$' >/dev/null; then
            echo removing branch ${longref#refs/heads/}
            return 0
        fi
        between=$(git rev-list $old..$new)
        case "$between" in
        *$'\n'*)
            echo at least two revs
            for rev in $between; do git log -1 --oneline $rev; done
            return 0
        esac
        echo only one rev
        return 0
    }
    
    while read old new longref; do
        case $longref in
        refs/heads/*) check $old $new $longref;;
        esac
    done
    

    (您可以检查$between 中的所有转速,因为它们已经发送到远程仓库,即使您要拒绝它们)。


    更新:我想出了一种方法,即使在使用 ssh 传输时也能完成这项工作,它不会让你走私任何额外的数据。

    我修改(并重命名)check 函数。在新名称get_confirmation 下,它旨在找出(并在默认情况下不希望允许推送的情况下返回 0)(并在允许推送的情况下返回 1)。

    然后,在主循环中,你可以这样做:

    case $longref in
    refs/heads/*)
        if get_confirmation $old $new $longref; then
            case $PWD in
            *.allow.git)
                echo 'allowed via alternate path'
                ;;
            *.git)
                echo "denied ... push to ${PWD%.git}.allow.git to allow"
                exit 1
                ;;
            *)
                echo "denied, don't know where I am"
                exit 1
                ;;
            esac
        fi
        ;;
    # add more cases here if desired
    esac
    

    这假设您推送到位于/some/path/to/repo.git 中的--bare 克隆。

    要创建一个允许推送的等效存储库,您必须创建一个名称以repo.allow.git 结尾的实际并行目录。这个目录应该包含一个实际的文件,HEAD,从普通的 repo 复制而来;目录中的其他所有内容都可以是指向../repo.git/&lt;same&gt;的符号链接:

    cd /some/path/to
    mkdir repo.allow.git
    cd repo.allow.git
    ln -s ../repo.git/* .
    rm HEAD; cp ../repo.git/HEAD .
    

    HEAD 必须是普通文件的原因是,否则git push 不会将其视为有效的 repo(如果它是指向分支的链接,则仅允许它是符号链接,那就是“老方法”做一个象征性的参考)。

    普通的git push 失败,您可以改为git push remotehost:/some/path/to/repo.allow.git 使其通过。当然,您的用户可能会一直养成这样做的习惯,从而消除了这次 hack 的全部目的,但也许他们不会。

    这一切都假设您的远程仓库是一个类 Unix 主机,当然(即支持符号链接)。

    【讨论】:

    • 托雷克谢谢。我会试试这个。使钩子交互的一种方法是使用类似于stackoverflow.com/questions/3417896/… 中提到的'exec
    • exec 技巧在本地运行的钩子中很好,但预接收通常在具有非交互式 ssh 的远程计算机上运行:git push ... remote: hooks/pre-receive: line 30: /dev/tty: Device not configured
    猜你喜欢
    相关资源
    最近更新 更多
    热门标签