Git reset --hard和推送到远程仓库

271

我有一个仓库,其中存在一些不好的提交(以此示例为例,为D、E和F)。

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

我使用git reset --hard特别修改了本地代码库。在重置之前我创建了一个分支,现在我的仓库看起来像:

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 push时,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'  

如何使远程仓库获取当前本地仓库的状态?


2
这是几个“如何推送修改历史问题”的“几乎”重复,例如在此处查看答案https://dev59.com/gXVC5IYBdhLWcg3wjx_u#255080。 - CB Bailey
2
这是真的,我发布之前已经在 StackOverflow 上搜索了答案。但是我的搜索只得到了一些使用 git push --force 解决问题的答案。感谢你链接到你的帖子 :) - robertpostill
2
你很快就能(在git1.8.5,2013年第四季度)更加谨慎地执行git push -force操作了。 - VonC
5个回答

349
如果强制提交不起作用(使用git push --force origingit push --force origin master应该足够),这可能意味着远程服务器拒绝非快进推送,可能是通过receive.denyNonFastForwards配置变量(请参见git config手册中的描述)或通过更新/预接收挂钩。

在旧版本的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

5
@JakubNarębski,谢谢。get revert HEAD~N 很有帮助。这里的 N 是提交次数。例如,如果我需要前一个提交,我会使用 git revert HEAD~1 - Maksim Dmitriev
2
请注意,这样做会破坏其他人的本地主分支。 - The Student

41

对于GitHub用户,以下操作适用:

  1. 在任何希望进行更改的分支保护规则中,确保启用了允许强制推送
  2. git reset --hard <full_hash_of_commit_to_reset_to>
  3. git push --force

这将在您的本地计算机和GitHub服务器上“纠正”分支历史,但是自从有问题的提交以来已经将此分支与服务器同步化的任何人都会在其本地计算机上拥有该历史记录。如果他们有直接向该分支推送的权限,则在同步时这些提交将再次显示出来。

其他所有人只需要执行上述git reset命令即可在其本地计算机上“纠正”该分支。当然,他们需要注意在目标哈希之后对该分支所做的任何本地提交。根据需要进行挑选/备份并重新应用它们,但如果您处于受保护的分支中,则可以直接向其提交的人数很可能是有限的。


对于Gitlab也是一样的:如果你遇到了像“error: failed to push some refs to http://www.gitlab.com/me/myproject”这样的错误信息,问题可能是你需要“允许强制推送”:设置-->仓库-->受保护的分支(在撰写本文时)。不需要实际取消保护该分支,但要允许强制推送。我实际上对所涉及的分支进行了拉取,然后进行了硬重置,最后进行了强制推送。 - mike rodent

28
为了补充Jakub的回答,如果您可以通过ssh访问远程git服务器,您可以进入git远程目录并设置:
user@remote$ git config receive.denyNonFastforwards false

然后回到你的本地仓库,再尝试使用 --force 提交:

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

最后将服务器的设置还原为原始受保护状态:
user@remote$ git config receive.denyNonFastforwards true

请参阅http://pete.akeo.ie/2011/02/denying-non-fast-forward-and.html,了解有关适用于sourceforge的详细信息。 - hlovdal
本SO帖子提供了如何使用vi禁用denyNonFastForwards的详细说明:stackoverflow.com/a/43721579/2073804 - ron190

3

与其修复您的“主”分支,重命名分支以将其替换为“期望的主分支”要容易得多。请参见https://dev59.com/s3E85IYBdhLWcg3wQxPG#2862606。这样做,您甚至不会留下多个还原日志的痕迹。


2
整个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目录被替换为几个提交之前的状态。


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