2

我想设置一个预接收钩子,它执行这样的逻辑

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

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

是否有可能在 git 的预接收挂钩中实现上述目标?

4

1 回答 1

3

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

pre-receive 钩子(在标准输入上)获取一系列行:

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

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

refs/heads/<branchname>

然后正在创建、删除或更新给定的分支。<old-value>如果是 40秒,则正在创建分支0;如果<new-value>是 400秒,则将其删除;否则正在更新。(除了 refs/heads/* 之外还有其他东西;完整列表请参见 git-send-email 钩子。)

如果分支正在更新,<old-value>是它过去指向<new-value>的提交 ID,并且是如果允许更新它将指向的提交 ID(这取决于预接收挂钩更新挂钩)。

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

这假设您推送到--bare位于/some/path/to/repo.git.

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

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(如果它是指向分支的链接,则仅允许它是符号链接,这是执行符号 ref 的“旧方式”) .

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

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

于 2012-05-01T04:09:25.760 回答