pre-receive hook on server-side that refuse any push to master which has any non-linear history

前端 未结 3 1646
一向
一向 2020-12-06 08:31

I am looking for a shell script (sh, not bash possibly) that would refuse any push to master which has any non-linear history.

I t

相关标签:
3条回答
  • 2020-12-06 08:42

    After much struggle on the sourceforge server here is what I got:

    #!/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
    
    0 讨论(0)
  • 2020-12-06 08:45

    You can do this much, much more simply than your hook.

    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
    

    You also might want to make this an update hook, not a pre-receive hook. The pre-receive hook runs once for the entire push, taking input on stdin, while the update hook runs once per ref to be updated, taking input as arguments. That gives you better granularity.

    #!/bin/sh
    ref=$1
    rev_old=$2
    rev_new=$3
    if [ "$ref" = refs/heads/master ]; then
        ...
    fi
    

    There's also usually no need to check for fast forwards. Git already by default disallows non-fast-forward pushes, and if you want to deny them even on forced pushes, just set receive.denyNonFastForwards to true in the config. I guess you want to deny them even for forced pushes to master, but allow them elsewhere? That's a kind of access control that's actually implemented very well by things like gitolite, if you're doing your own hosting, but I guess if that's your requirement and you don't have gitolite, this is the best you can do.

    Finally, it also strikes me as odd that you'll allow merge commits to be pushed to other refs, just not master. That could lead to some surprises, if someone pushes to an unstable branch, it gets tested, then you want it on master - but can't push it there!

    0 讨论(0)
  • 2020-12-06 08:47

    This script worked for me! It protects selected branches against forced push and deletes.

    #!/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
    
    
    0 讨论(0)
提交回复
热议问题