How can I prevent non-fastforward pushes to selected branch(es) in git?

I would like to protect my git repository so only non master branches can be overwritten. Is there a way to protect only selected branches?

  • Avoid recursive merge in Git
  • git rebase master then push origin branch results in non-fast-forward error
  • Feature branch with or without fast-forward merge?
  • Undo git fast forward merge
  • What does it mean that a Git push can not be fast foward merged?
  • What does Fast-forward mean when pulling from remote?
  • How can we use version control in a shared work environment?
  • git: replace folder with the same folder on a different branch
  • How to list the last occurance of a specific string in Terminal
  • Add prefix to commit message in SourceTree
  • How to get GitExtensions installed on Visual Studio 2010 Beta 2?
  • How to merge and commit to a branch in TeamCity
  • 6 Solutions collect form web for “How can I prevent non-fastforward pushes to selected branch(es) in git?”

    You can use GitEnterprise to setup per-branch permissions (admin) to block non-fastforward pushes using fine-grained access permission.

    And git config --system receive.denyNonFastForwards true will simply do the job if you need to block history changing for all branches.

    Here’s an update hook (copy to hooks/update) that I wrote for my own use. This script by default denies all non-fast-forward updates but allows them for explicitly configured branches. It should be easy enough to invert it so that non-fast-forward updates are allowed for all but the master branch.

    #!/bin/sh
    #
    # A hook script to block non-fast-forward updates for branches that haven't
    # been explicitly configured to allow it. Based on update.sample.
    # Called by "git receive-pack" with arguments: refname sha1-old sha1-new
    #
    # Config
    # ------
    # hooks.branch.<name>.allownonfastforward
    #   This boolean sets whether non-fast-forward updates will be allowed for
    #   branch <name>. By default they won't be.
    
    # --- Command line
    refname="$1"
    oldrev="$2"
    newrev="$3"
    
    # --- Safety check
    if [ -z "$GIT_DIR" ]; then
            echo "Don't run this script from the command line." >&2
            echo " (if you want, you could supply GIT_DIR then run" >&2
            echo "  $0 <ref> <oldrev> <newrev>)" >&2
            exit 1
    fi
    
    if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
            echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
            exit 1
    fi
    
    # --- Check types
    # if $newrev is 0000...0000, it's a commit to delete a ref.
    zero="0000000000000000000000000000000000000000"
    if [ "$newrev" = "$zero" ]; then
            newrev_type=delete
    else
            newrev_type=$(git cat-file -t $newrev)
    fi
    
    case "$refname","$newrev_type" in
            refs/tags/*,commit)
                    # un-annotated tag
                    ;;
            refs/tags/*,delete)
                    # delete tag
                    ;;
            refs/tags/*,tag)
                    # annotated tag
                    ;;
            refs/heads/*,commit)
                    # branch
                    # git rev-list doesn't print anything on fast-forward updates
                    if test $(git rev-list "$newrev".."$oldrev"); then
                            branch=${refname##refs/heads/}
                            nonfastforwardallowed=$(git config --bool hooks.branch."$branch".allownonfastforward)
    
                            if [ "$nonfastforwardallowed" != "true" ]; then
                                    echo "hooks/update: Non-fast-forward updates are not allowed for branch $branch"
                                    exit 1
                            fi
                    fi
                    ;;
            refs/heads/*,delete)
                    # delete branch
                    ;;
            refs/remotes/*,commit)
                    # tracking branch
                    ;;
            refs/remotes/*,delete)
                    # delete tracking branch
                    ;;
            *)
                    # Anything else (is there anything else?)
                    echo "hooks/update: Unknown type of update to ref $refname of type $newrev_type" >&2
                    exit 1
                    ;;
    esac
    
    # --- Finished
    exit 0
    

    You can prevent non-fast-forward updates by configuring denyNonFastForwards

    git config --system receive.denyNonFastForwards true
    

    But it applies for all branches.
    For more info please refer ProGit

    This SO answer will give you what you’re looking for. Just edit it to apply to the master branch instead:

    #!/bin/sh
    # lock the master branch for pushing
    refname="$1"
    
    if [ "$refname" = "refs/heads/master" ]
    then
        echo "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
        echo "You cannot push to the master branch."
        echo "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
        exit 1
    fi
    exit 0
    

    Update:
    This will prevent all pushes to the master branch, including fast-forward.

    I think it depends on what you use on server side to access your repository. There are some server applications which support per-branch permissions, like Gerrit or Gitlab (however, i’m not sure if Gitlab supports your usecase). Gerrit supports it, since i use a similiar workflow in my company.

    Maybe Gitolite also supports it (that’s what Gitlab uses under the hood), which is easier to setup, but doesn’t have a webinterface like Gerrit or Gitlab.

    Additional comment: GitEnterprise, as suggested, is also a good solution, my suggestions however are suitable, if you have your own server (which is common in many companies).

    If you would be allowed to modify your server, then this will enable fast-forwarding on server.

    ssh ip 'echo $"[receive]
        denyDeletes = false
        denyNonFastForwards = false" >> /path/to/repo/config'
    #then git push -f origin master
    
    Git Baby is a git and github fan, let's start git clone.