如何在Git中防止对选择的分支进行非快进式推送?

9
我希望保护我的 git 存储库,只允许非主分支进行覆盖。是否有一种方法只保护选定的分支?
7个回答

8

这是我为自己使用编写的更新勾子(复制到 hooks/update)。该脚本默认拒绝所有非快进更新,但允许明确配置的分支进行此类更新。将其反转以允许除主分支外的所有非快进更新应该很容易。

#!/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

3
如果您可以修改脚本,以便允许所有分支进行非快进式更新,但主分支除外,那将非常有用。 - Ren
请看下面我的帖子,其中有一个对此脚本进行轻微修改的方法,允许使用通配符来匹配允许的分支。 - jomofrodo

4
你可以使用GitEnterprise来设置每个分支的权限(管理员),以使用细粒度访问权限阻止非快进式推送。
如果需要阻止所有分支的历史更改,只需使用git config --system receive.denyNonFastForwards true即可。

@Eric,请将local特性分支在远程主分支之上进行变基,并将其合并到主分支中。始终使用快进方式。 - Sergey K.
@Eric 在这种情况下哪里需要重写呢? - Sergey K.
进行提交并跟踪提交ID。Rebase,然后比较提交ID。提交ID不再相同。这是一个新的提交。重写。 - Eric
2
@Eric 这只会发生在你的本地仓库。如果你现在将这个变基后的分支合并回主分支并将其推送到远程仓库,它将是一个安全的快进式推送,具有清晰干净的历史记录。 - Sergey K.
1
好的,我认为我理解了“重写提交是一个坏主意”的应用背景。它并不是“所有提交的重写都是不好的”的笼统覆盖。相反,只有在远程上重写提交才是不好的。我之前就认识到这一点,但是我被告知“所有提交的重写”都应该避免。他们最初误解了为什么重写提交是不好的。这只是一种不同的阅读方式!谢谢!! - Eric
显示剩余4条评论

1

您可以通过配置 denyNonFastForwards 来防止非快进式更新。

git config --system receive.denyNonFastForwards true

但它适用于所有分支。欲了解更多信息,请参考ProGit


0
这是Tanu Kaskinen脚本的修改版,允许使用通配符来匹配分支名称。我们使用以“d/”开头的分支来指定“开发”分支。我想要一种方法来允许这些d/分支进行非快进式更新:
   refs/heads/*,commit)
            # branch
            # git rev-list doesn't print anything on fast-forward updates

            if [[ $(git rev-list "$newrev".."$oldrev") ]]; then
                    branch=${refname##refs/heads/}
                    if [[ "$branch" =~ ^d/ ]] ; then
                      echo "Non-fast-forward update allowed on d/ branch"
                      nonfastforwardallowed="true";
                    else
                      #look for a specific config setting
                      nonfastforwardallowed=$(git config --bool  hooks.branch."$branch".allownonfastforward)
                    fi


                    if [ "$nonfastforwardallowed" != "true" ]; then
                            echo "hooks/update: Non-fast-forward updates are not allowed for branch $branch"
                            exit 1
                    fi
            fi

0

这个Stack Overflow的回答会给你想要的东西。只需将其编辑为适用于主分支即可:

#!/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

更新:
这将阻止对主分支的所有推送,包括快进。


4
这将防止将任何修改推送到所选的分支。但快进式(push)的修改应该是可以的。 - Sergey K.
在这里,有人找出了如何检测更新脚本中的强制性。有一个长的解释,最终片段仍需完成... - Frank N

0

如果您被允许修改服务器,则可以在服务器上启用快进。

ssh ip 'echo $"[receive]
    denyDeletes = false
    denyNonFastForwards = false" >> /path/to/repo/config'
#then git push -f origin master

0

我认为这取决于你在服务器端使用什么来访问你的存储库。有一些服务器应用程序支持每个分支的权限,例如GerritGitlab(但是,我不确定Gitlab是否支持您的用例)。 Gerrit支持它,因为我在公司中使用类似的工作流程。

也许Gitolite也支持它(这是Gitlab在幕后使用的),它更容易设置,但没有像Gerrit或Gitlab那样的Web界面。

附加评论:建议使用GitEnterprise也是一个不错的解决方案,但如果您拥有自己的服务器(这在许多公司中很常见),则我的建议更适合。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接