为什么不同的冲突解决方式会导致不同的冲突?

Why different ways to resolve conflicts cause to different conflicts?

我想找到为什么 git 在我尝试将本地更改与主更改合并时 表现不同的原因,取决于关于使用的命令和序列

为了确保我从一个干净的环境开始,我将所有内容重置为最新的本地提交 69cf18c... 然后我挑选我自己的更改(只有一个文件)已经存储在 Gerrit 服务器中:

git reset --hard 69cf18c...
git fetch ssh://...  && git cherry-pick FETCH_HEAD

从这里开始,我尝试了 3 种方法来解决它,只有第三种方法效果很好:

选项 1:使用 rebase 拉取

git pull --rebase

Git 将向我展示 数百 与我挑选的文件无关的更改。即使我尝试 git rebase --skip 另一场神秘的冲突也会无限期地一次又一次地到来。 为什么会这样?我的本地历史是否被一些旧的本地提交所污染

选项 2:拉取而不使用 rebase

git pull

Git 只会显示一个冲突,一个与我的文件有关的冲突,到这里一切正常。然而,在解决冲突和 运行 git add -u && git commit、git 之后在我的之上创建了一个新的提交,我无法将其与我的 运行 git rebase --interactive HEAD~2 合并(然后我会分别 picksquash):

git log
commit f65738375
Merge: 6c4e66a f235f75
       ...
commit 6c4e66a88
       ...

Git 不会在编辑器中列出提交 "Merge",而是列出数百个其他提交。 为什么我不能将冲突合并到我的提交中?

选项 3:重置、拉取然后 cherry-pick

如果我在拉动后进行 cherry-pick,一切正常:

git reset --hard 69cf18c...
git pull
git fetch ssh://...  && git cherry-pick FETCH_HEAD
# solve the conflict
git add -u && git commit -c FETCH_HEAD

为什么按照这个顺序一切顺利?

这3个选项有解释吗?提前致谢!

看起来 FETCH_HEAD 的历史与 69cf18c

的历史大不相同

查看 FETCH_HEAD69cf18c 的比较历史记录,使用查看器(gitk、gitg、git-kraken , git-extensions ...) 或 运行 :

git log --graph --oneline 69cf18c FETCH_HEAD

你应该看到两个提交的历史是不同的,并且每个分支都有许多其他分支没有的提交(你说 "hundreds")。


当您的本地分支有数百个提交要在远程分支之上重播时,选项 1 是正常行为

选项 2 是 git rebase -i 在处理合并提交时的正常行为:如果合并提交不引入新更改或获取 "discarded"

根据您的描述:选项 3 最接近您的要求。

首先你需要了解"git rebase"和"git merge"之间的区别。

"git pull --rebase" 等同于 "git fetch origin;git rebase"

"git pull" 等同于 "git fetch origin;git merge"

Git rebase 将所有本地提交移动到远程分支的顶部(默认情况下是远程的 master 分支)。如果远程分支上的多次提交与本地提交发生冲突,则必须逐一解决,而不是一次解决,就像爬楼梯一样。最后在 rebase 之后,你会发现你的本地提交在远程提交之上,你所有的本地提交 id 都改变了。

Git merge 只需将您的本地分支和远程分支与一个额外的合并提交连接起来,而不更改本地或远程分支上的提交。它要求一次冲突合并,这样的冲突解决信息保存在新创建的合并提交中。

Git rebase 更适合在提交前解决本地更改。 Git 合并更适合合并服务器上的远程分支,或 git 拉取请求,不会更改那里的提交 ID。 Gerrit 不将拉取请求用作 github 或 bitbucket。 Gerrit 的最佳做法是在本地避免 "git merge" 或 "git pull"。

看来您已经很长时间没有对本地分支进行变基了,因此远程分支上的许多提交与您的冲突。所以直接变基作为选项 1 是令人沮丧的。

您可以这样做来解决冲突。

  1. 根据您当前的本地分支创建另一个分支
git branch -b WIP
  1. 删除本地当前(如master)分支,并再次基于远程master分支重新创建
git branch -D master
git checkout -b master origin/master
  1. Cherry-pick 你在本地分支上一个一个地提交到 master 分支(如果没有太多的话)
git checkout master
git cherry-pick xxx (xxx is local commit on WIP branch)
...
git cherry-pick xxx
  1. Cherry-pick gerrit 提交
git fetch ssh://...  && git cherry-pick FETCH_HEAD

这样你就没有任何"git merge"的操作,节省了避免rebase的多重冲突的时间。