在git中设置预接收钩子的查询 [英] Query on setting up a pre-receive hook in git

查看:116
本文介绍了在git中设置预接收钩子的查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想设置一个预接收钩子,它执行的逻辑如下:


  • 检查是否有任何合并提交

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



如果存在上述任何一项,则与用户交互以检查与他,如果他想推动的变化,如果是的话,然后推变化,否则将其放弃。

是否有可能实现上述预接收hook in git?

解决方案

您无法在预接收钩子中与用户进行交互,至少在一般情况下,因为用户可能在某个其他机器上执行ssh,并且标准输入不会发送给用户。 (你可以通过检查用户名和/或用户名等来修补一些东西,然后转到一个脚本来回电用户,我会为你做这个方向的实验。)你可以做所有的检查,但是。



预接收钩子(在stdin上)有一系列的行:

 <旧值与GT; SP<新值> SP< ref-name> LF 

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

  refs / heads /< branchname> 

然后创建,删除或更新给定的分支。如果< old-value> 是40 0 s,如果< new-value> 是40 0 s,它会被删除;并且它正在被更新。 (除refs / heads / *以外的内容;请参阅git-send-email钩子获取完整列表。)



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



这是一个简单检测你的第二和第三种情况(加删除)的钩子。要确定是否有合并,您必须仔细检查 $ 之间的每一次修改,看看是否有合并(即,有多个父代)。要停止提交,请退出非零而不是返回0。

 #! / bin / sh 

check()
{
local old = $ 1 new = $ 2 longref = $ 3
本地转换

if expr $ old:'^ 00 * $'> / dev / null;然后
创建新分支$ {longref#refs / heads /}
return 0
fi
if expr $ new:'^ 00 * $'> / dev / null ;然后
回声消除分支$ {longref#refs / heads /}
return 0
fi
between = $(git rev-list $ old .. $ new)
$
* $'\\\
'*)
在$ between之间至少回复两个转数
;做git log -1 --oneline $ rev;完成
返回0
esac
仅回显一个
返回0
}

读取旧的新的longref;
case $ longref in
refs / heads / *)check $ old $ new $ longref ;;
esac
完成

(您可以检查 $之间,因为它们已经被发送到远程仓库,即使你要拒绝它们)。


更新:我想出了一种方法,即使在使用ssh transport的时候也是如此,它不会让你偷盗任何额外的数据。



我修改(并重命名)检查函数。在新的名称 get_confirmation 下,它的意思是找出(并返回0)默认情况下不允许推送的情况(并且返回1在那里它是被允许的)。

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

<$ p $如果get_confirmation $ old $ new $ longref;
refs / heads / *)
,则返回 case $ longref。那么
case $ PWD在
* .allow.git)
echo'允许通过备用路径'
;;
* .git)
echodenied ... push to $ {PWD%.git} .allow.git允许
exit 1
;;
*)
回声被拒绝,不知道我在哪里
exit 1
;;
esac
fi
;;
#如果需要,可以在这里添加更多的案例
esac

复制到 /some/path/to/repo.git 中的 - bare 克隆。



要创建一个允许推送的等价回购,您必须创建一个名称以 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 不会将其视为有效的回购(只有在链接到分支时才允许它为符号链接,这就是旧的

常规 git push 失败,您可以改为 git push remotehost:/some/path/to/repo.allow.git 让它通过。当然你的用户可能总是习惯于这样做,避免了这种黑客的全部目的,但也许他们不会。



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

I wanted to set up a pre-receive hook that does a logic such that

  • Checks if there are any merge commits
  • Checks if a new branch is pushed
  • Checks if there are multiple commits.

If any of the above is present, then interact with the user to check with him if he would like to push the change and if yes, then push the change and else drop it.

Is there a possibility to achieve the above in pre-receive hook in git?

解决方案

You can't interact with the user in the pre-receive hook, at least not in general, since the user might be on some other machine doing an "ssh", and standard input is not going to the user. (You might be able to cobble something up by inspecting uids and/or user names and such, and escaping out to a script that "calls back" the user. I leave experiments in that direction for you.) You can do all those checks, though.

The pre-receive hook gets (on stdin) a series of lines:

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

(quoted from githooks(5)). If the ref-name has the form:

refs/heads/<branchname>

then the given branch is being created, deleted, or updated. The branch is being created if <old-value> is 40 0s; it's being deleted if <new-value> is 40 0s; and it's being updated otherwise. (There are things other than refs/heads/*; see the git-send-email hook for a complete list.)

If the branch is being updated, <old-value> is the commit ID to which it used to point, and <new-value> is the commit ID to which it will point if the update is allowed (this depends on both the pre-receive hook and the update hook).

Here's a hook that simply detects your second and third cases (plus deletion). To find out if there are merges, you'll have to go through each rev in $between and see whether any is a merge (i.e., has more than one parent). To stop the commit, exit nonzero instead of returning 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

(you can inspect all the revs in $between as they have already been sent to the remote repo, even if you're going to reject them).


Update: I figured out a way to make this work even when using the ssh transport, which does not let you smuggle in any extra data.

I modified (and renamed) the check function. Under the new name, get_confirmation, it's meant to figure out (and return 0 for) cases where you don't want to allow the push by default (and return 1 for cases where it's to be allowed).

Then, in the main loop, you can do this:

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

This assumes that you push to a --bare clone that lives in /some/path/to/repo.git.

To make an equivalent repo that allows pushes, you have to create an actual parallel directory whose name ends in repo.allow.git. This directory should contain one actual file, HEAD, copied from the plain repo; and everything else in the directory can be a symbolic link to ../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 .

The reason that HEAD has to be an ordinary file is that otherwise git push won't treat this as a valid repo (it's only allowed to be a symlink if it's a link to a branch, and that's the "old way" of doing a symbolic ref).

A regular git push fails, and you can instead git push remotehost:/some/path/to/repo.allow.git to make it go through. Of course your users might get in the habit of doing that all the time, obviating the entire purpose of this hack, but maybe they won't.

This all does assume that your remote repo is a Unix-like host, of course (i.e., supports symlinks).

这篇关于在git中设置预接收钩子的查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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