主分支和'origin/master'分支已经分叉,如何“合并”这些分支?

1321

我的masterorigin/master分支不知何故发生了分歧。
实际上,我不希望它们分歧。

我该如何查看这些差异并进行合并


4
你所说的“diverging”是什么意思?你在推送完主分支之后再重新设置基础分支吗? - hasen
44
我收到一条消息,上面写着:"Your branch and 'origin/master' have diverged, and have 1 and 1 different commit(s) each, respectively." 这意味着你的分支和主分支已经分开了,并且它们各自有一个不同的提交。 - Frank
13
这个博客上的解释比下面任何答案都更有帮助:http://sebgoo.blogspot.com/2012/02/git-your-branch-and-originxxx-have.html - user711807
7
如果你想撤销自己本地分支的所有更改(例如进行了推送和拉取,但似乎没有做任何改动),可以使用以下命令:git reset --hard origin/my-branch。请注意,只有在确定没有需要保留的本地更改时才应执行此操作。 - Dean
1
@user711807 存档版本 https://web.archive.org/web/20150213051658/https://serebrov.github.io/html/2012-02-13-git-branches-have-diverged.html - Iceland_jack
显示剩余5条评论
20个回答

1309

您可以通过以下方式查看差异

git log HEAD..origin/main

# old repositories
git log HEAD..origin/master

拉取它之前(获取+合并)(另请参见{{link2:“如何让git始终从特定分支拉取?”}})

注意:自Git 2.28(2020年第三季度)以来,默认分支是可配置的,现在(2021+)设置为main,不再是master
答案的其余部分反映了更近期的惯例。


当你遇到以下类似信息:

"Your branch and 'origin/main' have diverged, # and have 1 and 1 different commit(s) each, respectively."

检查是否需要更新 origin
如果origin已经是最新的,那么说明你本地提交代码时,有其他仓库向origin提交了一些代码。

... o ---- o ---- A ---- B  origin/main (upstream work)
                   \
                    C  main(your work)

你基于提交的A,进行了C的提交,因为那是你在那个时候从upstream中获取到的最新工作。

然而,在你尝试推回到origin之前,有人已经推送了提交B
开发历史已经分为不同的路径。

然后你可以合并或变基。请参见Pro Git:Git分支-Rebasing以获取详细信息。

合并

使用git merge命令:

$ git merge origin/main

# old repositories
$ git merge origin/master

这告诉 Git 将来自 origin/main 的更改集成到您的工作中并创建合并提交。
现在历史记录的图表如下所示:

... o ---- o ---- A ---- B  origin/main (upstream work)
                   \      \
                    C ---- M  main (your work)

新的合并提交M两个父提交,分别代表通向该提交存储的内容的两条开发路径。
请注意,M背后的历史现在是非线性的。

变基

使用git rebase命令:
$ git rebase origin/main

# old repositories
$ git rebase origin/master

这告诉Git将提交C(您的工作)的重放,就好像您是基于提交B而不是A进行的。
当CVS和Subversion用户在提交之前更新时,经常会将他们的本地更改重新命名为upstream工作的顶部。
Git只是在提交和重新命名步骤之间添加了明确的分离。

历史记录的图现在如下所示:

... o ---- o ---- A ---- B  origin/main (upstream work)
                          \
                           C'  main (your work)

C'提交是由git rebase命令创建的新提交。
它与C有两个不同之处:

  1. 它具有不同的历史记录:B而不是A
  2. 其内容考虑了BC中的更改;它与合并示例中的M相同。

请注意,C'背后的历史仍然是线性的。
我们目前选择允许cmake.org/cmake.git中仅有线性历史记录。
这种方法保留了以前使用的基于CVS的工作流,并可能简化过渡。
尝试将C'推送到我们的存储库将起作用(假设您具有权限且在重定位时没有人推送)。

git pull命令提供了一种快捷方式,可以从origin获取并重新设置本地工作:

$ git pull --rebase

这将上述的fetchrebase步骤合并为一个命令。


7
我在搜索同样的问题时发现了这个,你能解释一下为什么“git reset --hard HEAD”没有解决问题吗? - Neth
21
@Neth:这是因为这不是关于暂存修改(即存在于索引中但尚未提交的修改),而是关于本地提交(不同于远程提交)。git reset --hard HEAD只会删除任何本地已经暂存但未提交的修改,对于本地提交和远程提交之间的差异没有任何作用。只有合并或变基(rebase)才能将两组提交(本地和远程)合并在一起。 - VonC
4
哇,感谢您这个很棒的回复。我们之前不小心进行了一次没有使用 "--rebase" 的 "git pull",而 "git rebase origin/master" 就是解决方法! - mrooney
7
怎么样 - 我只想忽略/丢弃我的本地更改,回到我的本地分支,与远程一致?换句话说,我希望在您的示例中,master 指向 B - CygnusX1
38
@CygnusX1,根据下面答案中提到的话,你需要执行git reset --hard origin/master命令:https://dev59.com/lHE95IYBdhLWcg3wHqIV#8476004 - VonC
显示剩余14条评论

1133

我遇到了这个问题,即使阅读了上面的回答,仍然感到困惑。我的解决方案是执行

git reset --hard origin/main

那么这只是将我的(本地)主要副本重置(我认为它已经损坏)到正确的点,如(远程)origin/main所表示的。

警告:您将失去尚未推送到origin/main的所有更改。


42
是的,这种感觉有点像哑铃选项,但如果没有真正的危险,并且你想要快速解决问题 - 这个方法还是有效的(至少对我来说是这样的)。 - PandaWood
12
在执行此操作之前需要在主分支上 ("git checkout master"). - blueyed
1
“origin/master” 这一部分是我需要的——不知怎么地,我的本地代码出了点问题,我真的想回到远程仓库的状态,但是没有显式指定远程名称的重置操作并没有起作用。谢谢! - Brian Moeskau
8
提交记录并没有真正丢失,你仍然可以通过 git reflog 命令找到提交记录,或者在 gitk --all 中查看它们。但是,硬重置和变基当然是不同的事情。 - sebkraemer
@skiphoppy,如果我想要相反的操作怎么办?也就是说,重置我的远程副本? - steven7mwesigwa
显示剩余8条评论

71
git pull --rebase origin/main 

是一个单一的指令,大多数情况下都可以帮到你。

编辑:从 origin/main 拉取提交,并将您的更改应用于新拉取的分支历史记录。


121
请注明命令的作用,否则人们可能会运行它并最终弄乱事情。 - Baz1nga
3
如果没有问题的话,你应该最终得到一个包含所有变更的主分支(master),其中包括原始主分支(origin/master)和你本地提交的所有更改。听起来很不错。 - Philipp Claßen
8
除非遇到真正的差异并导致合并基底失败,否则不会发生这种情况。 - ffledgling
这会产生一个错误:错误:无法合并更改。0024请求和响应模型的补丁失败 - IgorGanapolsky

54

我相信这应该对我有所帮助:

git reset --hard origin/main

但它没有。不知何故,我收到了相同的消息,只要我从远程分支拉取更改,就会发生冲突。由于我确定根本不需要现有的本地分支,我只需要来自远程的main分支的副本,因此我想出了这个解决方案:

  • 切换到一个新分支,例如git checkout -b placeholder-branch。注意:稍后可以删除此分支。
  • git branch -D main,我这样做是因为我确定我的本地分支已损坏,我不需要它。我需要从远程实例获取新的副本。
  • git checkout --track origin/main,然后你就完成了;现在可以使用git branch -D 删除placeholder-branch

39

当我尝试将一个远程分支上的跟踪分支进行变基并且将其变基到主分支上时,我发现自己陷入了这种情况。在这种情况下,如果你尝试变基操作,你很可能会发现你的分支已经分叉了,并且可能会导致一些对于Git新手来说很麻烦的问题!

假设你现在在my_remote_tracking_branch分支上,该分支是从主分支上派生出来的。

$ git status

# 在分支 my_remote_tracking_branch 上

无文件要提交,干净的工作区

现在你想从主分支上进行变基操作,命令如下:

git rebase main

立即停止并使用合并操作以避免麻烦:

git merge main

是的,你的分支最终会有额外的提交。但是,除非你准备好“取消分歧”的分支,否则这将比变基更顺畅。请参见this blog以获取更详细的解释。

另一方面,如果你的分支只是一个本地分支(即尚未推送到任何远程),你应该绝对进行变基(在这种情况下,你的分支不会分歧)。

现在,如果你正在阅读这篇文章,因为你已经由于这样的变基处于“分歧”状态,你可以使用以下命令回到来自原始的最后一个提交(即处于未分歧状态):

git reset --hard origin/my_remote_tracking_branch


6
如果你正在重新定义已经发布(并被其他人使用)的分支,那么遵循一个经验法则就是使用 merge。否则,使用 rebase。如果你重新定义已经发布(并被使用)的分支,你必须协调一场阴谋,以重写每个使用了你的分支的开发者的历史记录。 - Mikko Rantalainen
1
很遗憾,在执行 git rebase master 命令之前我没有读到这条消息... - Vitaly Isaev
如果我在分支“foobar”上执行git rebase master,那么从技术上讲,直到我执行git push -f之前,“foobar”与origin/foobar分支是不同的,对吗? - relipse
http://www.jarrodspillers.com/git/2009/08/19/git-merge-vs-git-rebase-avoiding-rebase-hell.html - akhil_mittal
4
git reset --hard origin/my_remote_tracking_branch 是真正起作用的。 - sgohl
谢谢,伙计...情况变得很棘手,但是答案来拯救了我... :) - mohitesachin217

33
在我的情况下,我是这样引起 "diverged" 消息的:我执行了 `git push`,但紧接着又执行了 `git commit --amend` 来添加一些提交信息。然后我又进行了另一个提交。
所以在我的情况下,这意味着 origin/main 不是最新的。因为我知道没有其他人在修改 origin/main,所以修复问题很简单:`git push -f`(其中 `-f` 表示强制)。

9
支持使用 git push -f 命令覆盖之前提交并推送到远程代码库的更改。同时我确信没有其他人操作过这个代码库。 - zacharydl
5
非常高风险的命令。请撰写一份简短的信息,说明该命令的风险因素。 - J4cK
2
@Trickster:我已经描述了风险:“因为我知道没有其他人在操作origin/master”。在这种情况下,我认为这不是一个冒险的命令。 - Darren Cook
1
如果有人在主分支上提交了代码,然后另一个人运行了命令 git push -f,那么这是一条高风险的命令。 - J4cK
感谢您的回答。我的CI有一个git commit --amend,因此会失败。修改本地提交是有意义的,在CI中,如果您在提交之前进行修改,则技术上正在修改已经推送的远程提交,因此它将其视为分歧。 - Kerry Johnson

19
在我的情况下,我已经将更改推送到了origin/main,然后意识到我不应该这样做 :-( 由于本地更改位于子树中,这使得事情变得复杂。因此,我回到了“坏”本地更改之前的最后一个好提交(使用SourceTree),然后我收到了“分歧消息”。
在本地修复我的混乱后(细节在这里并不重要),我想要“回到过去”,使远程的origin/main分支与本地的main再次同步。在我的情况下,解决方案是:
git push origin main -f

请注意-f(强制)开关。这将删除错误地推送到origin/main的“坏更改”,现在本地和远程分支已经同步。
请记住,这是一项可能具有破坏性的操作,因此只有在您百分之百确定“回退”远程主干时间没有问题时才执行它。

总是有用的,但肯定不能回答问题。 - Thibault D.
2
@ThibaultD 即使它没有,这正是我要找的。 - Neil
1
是的,我在主分支上提交了一些不应该存在的提交。如果您不介意删除它们,可以使用上面提到的"git push --force"命令(重写历史并删除分歧的远程提交)。如果您想保留这些提交但不放在主分支上,则可以将它们移动到另一个分支。 - 00-BBB
@BanAnanas 创建一个新分支的 PR,然后你就可以合并到受保护的分支了。 - Máxima Alekz
1
@zero_cool 没有必要过度政治正确并将 master 分支名称更改为 main。Git 存储库中的“主”分支与奴隶主毫无关系。或者你拒绝下棋,只因为黑白两色的棋子在黑白相间的棋盘上移动吗?如果是这样,那么你会为棋盘提议什么颜色呢? :-) - András Aszódi
显示剩余2条评论

15
我知道这里已经有很多答案了,但我认为git reset --soft HEAD~1值得关注,因为它可以在解决分歧状态时保留最后一个本地(未推送)提交中的更改。我认为这比使用rebase的pull更具通用性,因为可以审查本地提交甚至将其移动到另一个分支。
关键是使用--soft而不是--hard。如果有多个提交,则应该使用HEAD~x的变体。因此,以下是解决我的情况(我有1个本地提交和8个远程提交)的所有步骤: 1) git reset --soft HEAD~1撤销本地提交。对于接下来的步骤,我使用了SourceTree中的界面,但我认为以下命令也可以工作: 2) git stash存储1)中的更改。现在所有更改都是安全的,不再存在分歧。 3) git pull获取远程更改。 4) git stash popgit stash apply应用最后一个存储的更改,然后进行新的提交(如果需要)。与2)一样,当要丢弃本地提交中的更改时,此步骤是可选的。此外,当要提交到另一个分支时,应在切换到所需分支之后执行此步骤。

1
实际上,现在的 pull --rebase 命令会自动进行存储。https://dev59.com/J10a5IYBdhLWcg3wipNA#30209750 - VonC

6

我已经通过移动到最后一次提交到 origin/main 的 commit_sha 来解决了这个问题。

git reset --hard commit_sha
警告:在“commit_sha”提交之后,您将失去所有提交的内容。

4

查看差异:

git difftool --dir-diff main origin/main

这将显示两个分支之间的更改或差异。在Araxis(我最喜欢的)中,它以文件夹差异样式显示。显示每个更改的文件。然后我可以单击文件以查看文件中更改的详细信息。


1
有趣的git-dir使用:+1 - VonC

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