有时我在变基和修复冲突后会遇到这个问题。 我做了git add .
,我会跑git rebase --continue
.
但是,我有时会收到此消息
Applying: xxxxxx
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.
我一直不明白为什么会这样。所以,我最终只是做了git rebase --skip
继续。谁能告诉我为什么 Git 没有意识到我解决了冲突?
与其说Git 没有意识到你已经修复了冲突,不如说 Git 的作者认为这是一个很好的安全设备。
当您正在挑选提交时会发生这种情况——请记住,git rebase
,无论是有效还是实际上,都只是大量重复的挑选——这种更改与您重新定位的提交中已有的更改不完全相同,但非常相似。
例如,假设原始作品是这样的:
...--o--* <-- origin/develop
A--B--C <-- develop
这里A
、B
和C
是你所做的三个提交。 提交*
是develop
刚开始时所处的位置,origin/develop
仍然记得它。
在提交A
中,修复了文件a
中的某些内容,并更新了README.txt
文件。 在B
中,您修复了文件b
中的某些内容,但忘记更新README.txt
。 在提交C
中,您记得更新README.txt
以便执行此操作。
现在其他人已经进行了更改并将其推送到您的上游服务器,因此您可以git fetch
他们的工作:
...--o--*--D--E <-- origin/develop
A--B--C <-- develop
然后继续git rebase
你的develop
在新origin/develop
上。 一般来说,这将复制A
、B
,并C
到新的提交A'
、B'
和C'
,这样,如果一切顺利,你最终会得到:
...--o--*--D--E <-- origin/develop
A'-B'-C' <-- develop
A--B--C [abandoned]
新的A'-B'-C'
提交只是原始git cherry-pick
副本。
Git 足够聪明,如果您的提交A
引入了与D
或E
中完全相同的更改,Git 将在复制时简单地删除A
:它会跳过它并仅B
和C
复制。 但是假设A
在这个意义上"等于"D
,或者——让我们为了论证而说第二种情况是正确的——A
是完全独立的,并且可以很好地复制。 但是,"等于"E
既不是B
也不是C
,而是总和,B+C
,"等于"E
。 也就是说,您和他们进行了相同的修复,但他们记得在一次提交中修复README.txt
......并且他们对文件b
的更改存在一些细微的拼写差异 - 或者可能是文件b
被重新格式化或其他什么,使 Git 无法按原样应用您的更改。
你手动进入并解决 Git 抱怨的b
,而 Git 试图挑选提交B
来制作B'
。 然后git add
解析的文件。 但是现在您的文件b
与上游b
完全匹配。 虽然这是故意的——毕竟没有什么可做b
的——但 Git不知道你已经验证了这一点。 Git 认为也许你只是跑git checkout HEAD -- b
看看他们的版本是什么。 这将提取他们的b
并假装一切都已解决,并使您处于相同的情况。1
因此,由于这会完全放弃您的提交B
,Git 会给您此警告。 如果正确的做法是现在完全放弃提交B
,那么毕竟您应该运行git rebase --skip
,并跳过B
。
Git 现在将尝试应用您的C
来修复README.txt
。 这将不需要做任何事情,因为他们已经在他们的提交D
中这样做了。 你的 Git 将再次看到,在解决冲突(甚至可能是自动的,这次)冲突后,没有什么可提交的。 Git 会想知道它是否弄错了,并让你运行git rebase --skip
以确认这是正确的。
完成后,事实证明只需要复制A
,实际上最终会得到:
...--o--*--D--E <-- origin/develop
A' <-- develop
A--B--C [abandoned]
但是 Git 希望真正确保从复制中省略B
和C
是正确的,因为在运行git rebase --continue
时相对容易忘记git add
。
1如果您意识到 Git 很早就没有一个好的内置git cherry-pick
命令,这可能更有意义。相反,变基将提交变成补丁,然后尝试应用补丁,就好像有人把它邮寄给你一样。 然后,Git 将无法判断您是否完全忘记应用补丁。