我有一个存储库,它有一些糟糕的提交(在这个例子中是D, E和F)。

A-B-C-D-E-F master和origin/master

我专门用git重置修改了本地存储库——很难。我在重置之前采取了一个分支,所以现在我有一个回购,看起来像:

A-B-C master  
     \ D-E-F old_master

A-B-C-D-E-F origin/master

现在我需要那些糟糕的提交的一些部分,所以我选择了我需要的比特,并进行了一些新的提交,所以现在我在本地有以下内容:

A-B-C-G-H master
     \ D-E-F old_master

现在我想把这种状态推到远程回购。然而,当我试图做一个git推git礼貌地给我刷掉:

$ git push origin +master:master --force  
Total 0 (delta 0), reused 0 (delta 0)  
error: denying non-fast forward refs/heads/master (you should pull first)  
To git@git.example.com:myrepo.git  
! [remote rejected] master -> master (non-fast forward)  
error: failed to push some refs to 'git@git.example.com:myrepo.git'  

我如何让远程回购采取本地回购的当前状态?


当前回答

如果强制推送没有帮助(git push——force origin或git push——force origin master应该足够了),这可能意味着远程服务器拒绝非快进推送,通过receive. denynonfastforwarded配置变量(参见git配置manpage的描述),或通过更新/pre-receive钩子。

对于旧的Git,你可以通过删除Git push origin:master(注意:在分支名之前),然后重新创建Git push origin master给定分支来解决这个限制。

如果你不能改变这个,那么唯一的解决方案是,而不是重写历史,创建一个提交,在D-E-F中恢复更改:

A-B-C-D-E-F-[(D-E-F)^-1]   master

A-B-C-D-E-F                origin/master

其他回答

如果强制推送没有帮助(git push——force origin或git push——force origin master应该足够了),这可能意味着远程服务器拒绝非快进推送,通过receive. denynonfastforwarded配置变量(参见git配置manpage的描述),或通过更新/pre-receive钩子。

对于旧的Git,你可以通过删除Git push origin:master(注意:在分支名之前),然后重新创建Git push origin master给定分支来解决这个限制。

如果你不能改变这个,那么唯一的解决方案是,而不是重写历史,创建一个提交,在D-E-F中恢复更改:

A-B-C-D-E-F-[(D-E-F)^-1]   master

A-B-C-D-E-F                origin/master

为了补充Jakub的回答,如果你可以通过ssh访问远程git服务器,你可以进入git远程目录并设置:

user@remote$ git config receive.denyNonFastforwards false

然后回到你的本地回购,再次尝试使用——force执行commit:

user@local$ git push origin +master:master --force

最后将服务器的设置恢复到原来的保护状态:

user@remote$ git config receive.denyNonFastforwards true

与其修复“主”分支,不如通过重命名分支来与“所需的主”进行交换。见https://stackoverflow.com/a/2862606/2321594。这样,您甚至不会留下多个恢复日志的任何痕迹。

对我来说,整个重置git的工作看起来非常复杂。

所以我做了一些沿着线让我的src文件夹在我有几个提交前的状态

# reset the local state
git reset <somecommit> --hard 
# copy the relevant part e.g. src (exclude is only needed if you specify .)
tar cvfz /tmp/current.tgz --exclude .git  src
# get the current state of git
git pull
# remove what you don't like anymore
rm -rf src
# restore from the tar file
tar xvfz /tmp/current.tgz
# commit everything back to git
git commit -a
# now you can properly push
git push

通过这种方式,src中的事务状态保存在tar文件中,git被迫接受这种状态,而不需要太多的修改,基本上src目录被替换为它在几次提交前的状态。

对于GitHub的用户来说,这对我来说很管用:

在希望进行更改的任何分支保护规则中,请确保启用了“允许强制推送” Git重置——hard <full_hash_of_commit_to_reset_to> Git push—force

这将“纠正”你的本地机器和GitHub服务器上的分支历史,但是自从错误提交以来,任何已经将这个分支与服务器同步的人都将在他们的本地机器上拥有历史。如果他们有权限直接推送到分支,那么这些提交将在同步时显示出来。

所有其他人需要做的是从上面的git reset命令来“纠正”本地机器上的分支。当然,他们需要警惕目标哈希之后对该分支的任何本地提交。在必要的时候选择/备份并重新应用它们,但是如果你在一个受保护的分支中,那么可以直接提交给它的人数可能是有限的。