关于在git中设置预接收挂钩的查询

关于在git中设置预接收挂钩的查询,git,Git,我想设置一个预接收挂钩,其逻辑如下 检查是否存在任何合并提交 检查是否推送了新分支 检查是否有多个提交 如果存在上述任何一项,则与用户交互,与用户确认他是否愿意推动更改,如果愿意,则推动更改,否则放弃更改 在git中的预接收钩子中是否有可能实现上述功能?您不能在预接收钩子中与用户交互,至少一般不能,因为用户可能在其他机器上执行“ssh”,并且标准输入不会发送给用户。(你可以通过检查UID和/或用户名之类的东西来拼凑一些东西,然后跳转到一个“回调”用户的脚本中。我把这个方向的实验留给你。)不过

我想设置一个预接收挂钩,其逻辑如下

  • 检查是否存在任何合并提交
  • 检查是否推送了新分支
  • 检查是否有多个提交
如果存在上述任何一项,则与用户交互,与用户确认他是否愿意推动更改,如果愿意,则推动更改,否则放弃更改


在git中的预接收钩子中是否有可能实现上述功能?

您不能在预接收钩子中与用户交互,至少一般不能,因为用户可能在其他机器上执行“ssh”,并且标准输入不会发送给用户。(你可以通过检查UID和/或用户名之类的东西来拼凑一些东西,然后跳转到一个“回调”用户的脚本中。我把这个方向的实验留给你。)不过,你可以做所有这些检查

预接收挂钩(在stdin上)获得一系列行:

<old-value> SP <new-value> SP <ref-name> LF
然后将创建、删除或更新给定的分支。如果
为40
0
s,则正在创建分支;如果
为40
0
s,则将其删除;另外,它正在更新。(除了refs/heads/*,还有其他内容;有关完整列表,请参阅git发送电子邮件挂钩。)

如果分支正在更新,
是它用来指向的提交ID,
是允许更新时它将指向的提交ID(这取决于预接收挂钩和更新挂钩)

这里有一个钩子可以简单地检测第二个和第三个案例(加上删除)。要了解是否存在合并,您必须在
$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
中的所有rev,因为它们已经发送到远程回购,即使您要拒绝它们)。
更新:我找到了一种方法,即使在使用ssh传输时也能做到这一点,因为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,您必须创建一个实际的并行目录,其名称以
repo.allow.git
结尾。该目录应包含一个实际文件,
HEAD
,该文件是从普通repo复制的;目录中的所有内容都可以是指向
。/repo.git/
的符号链接:

HEAD
必须是一个普通文件的原因是,否则
git push
不会将其视为有效的回购(只有当它是到分支的链接时才允许是符号链接,这是执行符号引用的“旧方法”)

常规的
git推送
失败,您可以改为
git推送remotehost:/some/path/to/repo.allow.git
,使其通过。当然,你的用户可能会养成一直这样做的习惯,从而避免了这种黑客行为的全部目的,但也许他们不会


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

Torek谢谢。我试试这个。另一种使钩子交互的方法是使用“execexec中提到的技巧,在本地运行的钩子中也可以,但是预接收通常在远程机器上运行,并使用非交互ssh:
git push
<代码>远程:挂钩/预接收:第30行:/dev/tty:未配置设备
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
cd /some/path/to
mkdir repo.allow.git
cd repo.allow.git
ln -s ../repo.git/* .
rm HEAD; cp ../repo.git/HEAD .