How can I prevent non-fastforward pushes to select

2020-02-28 10:53发布

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?

6条回答
叛逆
2楼-- · 2020-02-28 11:42

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.

查看更多
不美不萌又怎样
3楼-- · 2020-02-28 11:44

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

查看更多
淡お忘
4楼-- · 2020-02-28 11:46

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.

查看更多
仙女界的扛把子
5楼-- · 2020-02-28 11:50

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
查看更多
Anthone
6楼-- · 2020-02-28 11:52

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).

查看更多
小情绪 Triste *
7楼-- · 2020-02-28 11:58

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
查看更多
登录 后发表回答