Git 服务器端的预接收钩子,拒绝任何具有任何非线性历史记录的主机推送

Git 服务器端的预接收钩子,拒绝任何具有任何非线性历史记录的主机推送,git,sh,Git,Sh,我正在寻找一个shell脚本(sh,不可能是bash),它将拒绝任何具有任何非线性历史的主控 我尝试了以下脚本: 它是在ruby中实现的,但它甚至不在服务器上运行。然后我尝试了bash的等效版本: 这一个运行,但不做它应该做的(它不拒绝任何东西) 感谢您的建议。经过在sourceforge服务器上的不懈努力,我得到了以下信息: #!/bin/sh # Author: Mathieu Malaterre # This code is free software. It can be used

我正在寻找一个shell脚本(
sh
,不可能是
bash
),它将拒绝任何具有任何非线性历史的主控

我尝试了以下脚本:

它是在ruby中实现的,但它甚至不在服务器上运行。然后我尝试了bash的等效版本:

这一个运行,但不做它应该做的(它不拒绝任何东西)


感谢您的建议。

经过在sourceforge服务器上的不懈努力,我得到了以下信息:

#!/bin/sh
# Author: Mathieu Malaterre
# This code is free software. It can be used and distributed under the
# same terms as git itself.
# http://stackoverflow.com/questions/5482887/how-to-avoid-merge-branch-master-of-ssh-gdcm-git-sourceforge-net-gitroot-gdc
read rev_old rev_new refname

ref_to_check="refs/heads/master"

if [ "$refname" = "$ref_to_check" ]
then
  merge_bases=`git merge-base ${rev_old} ${rev_new}`
  if [ "$merge_bases" != "$rev_old" ]
  then
    echo "Non fastward is disallowed"
    exit 1
  fi
  # non-fast-forward case:
  git rev-list --parents $merge_bases..$rev_new \
    | while read x; do
      set -- $x
      if [ "$#" != "2" ]
      then
        echo "Multiple parents: $x";
        exit 1
      fi;
    done
  [ $? -ne 0 ] && exit 1
fi
exit 0

你可以做得比你的钩子简单得多

merge_commit=$(git rev-list -n 1 --merges $rev_old..$rev_new)
if [ -n "$merge_commit" ]; then
     echo "Merge commit detected: $merge_commit";
     exit 1
fi
您可能还希望将其设置为更新挂钩,而不是预接收挂钩。pre-receive钩子在整个push中运行一次,在stdin上获取输入,而update钩子在每个要更新的ref上运行一次,将输入作为参数。这会提供更好的粒度

#!/bin/sh
ref=$1
rev_old=$2
rev_new=$3
if [ "$ref" = refs/heads/master ]; then
    ...
fi
通常也不需要检查快进。Git在默认情况下已经不允许非快进推送,如果您想即使在强制推送时也拒绝它们,只需在配置中将
receive.denynonfastforts
设置为true。我猜你想拒绝他们,即使是强迫他们去掌握,但允许他们去其他地方?这是一种访问控制,实际上通过gitolite之类的东西可以很好地实现,如果你自己托管,但我想如果这是你的要求,而你没有gitolite,这是你能做的最好的了


最后,我还感到奇怪的是,您允许将合并提交推送到其他引用,而不是主引用。这可能会导致一些意外,如果有人推到一个不稳定的分支,它会被测试,然后你想要它在master上-但不能推到那里

这个脚本适合我!它保护选定的分支不受强制推送和删除的影响

#!/bin/sh

_REFNAME="$(echo "${1}" | sed -e 's,[^/]\+/,,g')"
_OLDREF="${2}"
_NEWREF="${3}"

_PROTECTED_LIST=( 'master' 'develop' )

_PROTECTED=false
for ref in "${_PROTECTED_LIST[@]}"; do
   if [[ "${ref}" == "${_REFNAME}" ]]; then
      _PROTECTED=true
   fi;
done;

echo ""
echo "#################### YOUR PROJECT NAME ####################"
echo "Branch: ${_REFNAME}"
echo "Old ref: ${_OLDREF}"
echo "New ref: ${_NEWREF}"
echo ""

_SUCCESS=true
if ${_PROTECTED}; then
   _GITOUT="$(git merge-base ${_OLDREF} ${_NEWREF} 2>/dev/null)"
   if [[ -n "${_GITOUT}" && "${_GITOUT}" != "${_OLDREF}" ]]; then
      echo "ERROR! You can't force the push on this branch!"
      _SUCCESS=false
   fi;
   if [[ -n "$(echo "${_NEWREF}" | sed -n -e '/^0\+$/p')" ]]; then
      echo "ERROR! You can't delete this branch!"
      _SUCCESS=false
   fi;
fi;

if ${_SUCCESS}; then
   echo "SUCCESS! :-)"
   echo "See you later?"
   echo ""
   exit 0
fi;

echo ""
exit 1


+1个很好的答案-我希望你不介意,但我更新了链接问题中的答案,使用了
git rev list--merges
,并将属性返回到你的答案。master之外的非线性历史对我来说很好。当合并人员时,git将合并--挤压以将其放入master中。“==”是一种bash ism,因此您的脚本将只在服务器上工作,
/bin/sh
实际上是bash。您应该将“==”更改为单个“=”。。。