Git拒绝非快进式合并

90

我觉得这个问题已经被问了很多次,但通常的解决方案是“删除目录并使用全新的checkout重新做一遍工作。” 我进行了提交和推送,但后来意识到在提交消息中引用了错误的票据编号。 因此,我在SO上寻找了一个快速的解决方案,最终在终端中输入了以下内容:

$ git reset --soft HEAD^
$ git commit -m "... correct message ..."

唯一的问题是我收到了以下错误消息:

To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.
我正在使用git-flow模型并在develop分支上工作。我该如何合并回来使得git再次变得可用?

Git 中的“快进”是什么? 的副本。 - user456814
4个回答

176
如果你将一个提交推送到服务器,然后在本地重写该提交(使用git reset, git rebase, git filter-branch或任何其他历史操作),然后将重写的提交推回服务器,那么任何已经拉取了该提交的人都会出现问题。这里有一个例子:假设你已经提交了A,并将其推送到服务器。
-*-*-A <-- 主分支
-*-*-A <-- 远程主分支
现在你决定按照上述方式重写A,重置并重新提交。请注意,这会留下一个悬空的提交A,因为它是不可达的,最终将被垃圾回收。
-*-*-A
    \
     A' <-- 主分支
-*-*-A <-- 远程主分支
如果有其他人(比如Fred)在你这样做时从服务器上拉取了主分支,他们将拥有对A的引用,可能会从A开始工作:
-*-*-A' <-- 主分支
-*-*-A <-- 远程主分支
-*-*-A-B <-- Fred/主分支
现在,如果你能够将A'推送到远程主分支,这将创建一个非快进式的提交,它的历史记录中没有A。因此,如果Fred再次尝试拉取,他将突然不得不合并,并重新引入A提交:
-*-*-A' <-- master

-*-*-A  <-- origin/master

-*-*-A-B-\ 
    \     * <-- fred/master
     A'--/
如果Fred注意到这个问题,他可以进行一次变基(rebase),这将防止提交A再次出现。但他必须注意到这个问题,并记得这样做;如果有不止一个人拉取了提交A,那么他们都必须变基才能避免在树中获得额外的A提交。
因此,在拉取其他人的仓库时,通常不建议更改历史记录。但是,如果您知道没有其他人从该仓库拉取(例如,它是私有的仓库或者只有一个开发人员正在协调易于协调的项目),那么您可以通过运行以下命令强制更新:
git push -f

或者

git push origin +master

这两种方法都会忽略非快进式推送的检查,并将服务器上的内容更新为新的A'修订版,放弃A修订版,因此最终将被垃圾回收。
可能情况是,使用receive.denyNonFastForwards配置选项完全禁用了强制推送。该选项在共享存储库上默认启用。在这种情况下,如果你真的想强制推送,最好的选择是删除分支并重新创建它,使用git push origin :master; git push origin master:master。但是,denyNonFastForwards选项有其原因,如上所述;在共享存储库上,这意味着现在每个使用它的人都需要确保他们重新基于新历史记录。
在共享存储库上,通常最好只推送新提交来解决你遇到的任何问题;你可以使用git revert生成撤消先前提交更改的提交。

8
或者使用不那么晦涩的命令git push --force - Bennett McElwee
3
你希望让多个人同时处理一个庞大而复杂的代码库,而不会相互阻塞(只允许一个人处理它),也不会出现更改相互覆盖的情况。你需要让每个人都能够独立地进行更改,并合并这些更改。合并(无论是手动还是自动)可能会引入意想不到的问题;因此,你希望尽可能保留更多信息,以便在发生错误时能够找出原因。这本质上是一个复杂的问题;它并不脏乱,只是一个难题。 - Brian Campbell
4
可以通过配置选项receive.denyNonFastForwards来完全禁用强制推送,这将防止在共享仓库上进行此类操作。如果你确实需要强制推送,最好的方法是删除分支并重新创建它,使用命令git push origin :remote_A; git push origin local_A:remote_A。但请注意,在共享仓库上执行此类工作流程可能会带来问题,请先仔细阅读我之前所写的内容。你应该只在需要消除或重写提交中存在严重问题时尝试这样做。 - Brian Campbell
@BrianCampbell 谢谢。在我的情况下,强制推送起作用了,并且我认为在我的情况下,我不会对其他在存储库上工作的人造成伤害。我的情况是这样的:我们启用了git存储库的Pull request(PR)系统,因此任何想要更改存储库的人都必须在一个分支中提交更改并推送更改,然后使用该分支提交其PR。所以在我的情况下,我在分支中提交更改,然后进行git pull --rebase,并根据我在分支中进行的第一次提交再次进行一些更改(请注意,PR尚未被接受)。然后在此分支中进行强制推送。 - jospratik
有人可以建议一下我在上面的评论中是否做错了什么吗? - jospratik
显示剩余3条评论

52

强制执行git push:

git push origin +develop

24
这是一个解决方案,但在使用它之前请阅读Brian Campbell的评论,以便了解你正在做什么。 - thelem
5
将本地代码库的主分支(master)推送到远程代码库的origin上。 - Aniket Thakur
请参考 Brian Campbell 的回答 中关于 receive.denyNonFastForwards 的注意事项:取决于他们配置了什么,+--force 可能不够强制。 - torek

14

你可能需要执行git pull命令,它可能会自动为你合并内容,然后你就可以再次提交。如果有冲突,它会提示你解决冲突。

请记住,如果你没有更新gitconfig指定哪个分支进行拉取,你必须指定要从哪个分支进行拉取...

例如:

git pull origin develop:develop

依然为非快进式合并感到烦恼。有什么想法可以强制进行合并吗?! [rejected] develop -> develop (non-fast-forward) - rynmrtn
我认为开关应该是-f,但我可能错了。http://www.kernel.org/pub/software/scm/git/docs/git-pull.html - Tony
这部分代码有一定效果,但是我在github上没有看到更新(即使使用了git push origin develop,它仍然显示之前的提交为最新)。 - rynmrtn

7

我曾经在使用EGit时也遇到了这个问题。只需要尝试 rebase 当前分支即可解决。


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