为什么在我的 rebase 操作中只有一次提交而不是两次发生冲突?

Why does a conflict occur for only one commit instead of two in my rebase operation?

我有 rep1 存储库,在 master 分支上有两个提交。这些提交 file.txt 具有以下内容:

line1
line2

我将 rep1 克隆到 rep2 并检出远程分支作为跟踪:

git checkout --track rep1/master

然后在这个存储库中我将 file.txt 的第一行修改为:

line1-modified-rep2
line2

提交。然后修改它的第二行为

line1-modified-rep2
line2-modified-rep2

提交。因此,在 rep2 中,我向 master 分支添加了两个提交,跟踪远程 rep1/master 分支。

现在我要产生一个冲突。在远程 rep1 存储库上,我创建了第三次提交(那里已经有两个),将第一行和第二行的 file.txt 修改为:

line1-modified-rep1
line2-modified-rep1

好的,现在我们都准备好合并冲突了。我将提交从 rep2 推送到 rep1,被拒绝,当提示输入 rebase select 这个选项时。

现在,我读到 rebase 会在同步的第三次提交之上应用来自 rep2 的两次提交(我修改了前缀为 -rep2 的两行) rep1(修改后的行带有前缀 -rep1)所以我期待两个合并冲突:

  1. 当来自 rep2 的第一次提交被应用并且行 line1-modified-rep1 vs line1-modified-rep2 将发生冲突时
  2. 当来自 rep2 的第一次提交将被应用并且行 line2-modified-rep1 vs line2-modified-rep2 将发生冲突时

但是只有一个合并冲突,解决后我可以成功将我的提交推送到 rep1。我在这里错过了什么?

PS。抱歉冗长的阐述,我试图尽可能清楚地阐述我的情况。

编辑:

所以变基之前的设置如下:

A--D (rep1)
 \
  B--C (rep2)

新增解析过程截图:

所以冲突解决后的结果文件包含这些行:

line1-modified-rep2
line2

这是phpstorm记录的git条命令的日志(来源是rep1这里):

23:19:49.586: git -c core.quotepath=false fetch origin --progress --prune
remote: Counting objects: 5, done.[K
remote: Total 3 (delta 0), reused 0 (delta 0)[K
From E:/rep1
   acc72ac..e6317e8  master     -> origin/master
23:20:39.118: cd E:\rep2
23:20:39.118: git -c core.quotepath=false rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: rep2-commit 2
Using index info to reconstruct a base tree...
M   file.txt
Falling back to patching base and 3-way merge...
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Failed to merge in the changes.
Patch failed at 0001 rep2-commit 2
The copy of the patch that failed is found in:
   e:/rep2/.git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
23:24:33.418: cd E:\rep2
23:24:33.418: git -c core.quotepath=false add --ignore-errors -- file.txt
23:24:33.630: cd E:\rep2
23:24:33.630: git -c core.quotepath=false rebase --continue
Applying: rep2-commit 2
Applying: rep2-commit 3

查看您的屏幕截图,您没有在第二次提交时应用冲突。检查您的 PhpStorm 的 Changes->Log,您应该会在 remote/master 之上看到两个提交。根据 git

的输出判断,甚至可能是三个
Applying: rep2-commit 2
Applying: rep2-commit 3

编辑

第二次没有冲突的原因是你已经合并了,文件刚好是rep2-commit 2的状态。如果您选择对 rep1 进行更改,那么您将一直存在冲突。

我的两分钱。 在启动 rebase 之前,你有类似的东西:

A--B--C--D--E rep2 \ F rep1

变基后,你得到

A--B--C \ F--D'--E' rep2

对吗?

git rebase documentation来看,我的理解是commit D和E的内容先保存在临时区。提交 D 首先应用在提交 F 之上,从而产生冲突。在冲突解决结束时,创建提交 D'。然后在提交 D' 之上应用提交 E。提交 E 是提交 D 之后 file.txt 上第 2 行的修改。所以我的观点是:解决冲突以生成 D' 是关键步骤。根据所做的选择,提交 D' 的内容 file.txt 等于提交 D 中 file.txt 的内容。在这种情况下,尝试在顶部生成 E' 时不会有任何冲突提交 D.

TL;DR

在变基操作开始时只发生一次冲突。由于您解决该冲突的方式,第二个补丁可以干净利落地应用;没有其他冲突的原因。

更多详情

为了完整起见,以下是如何从命令行重新创建玩具示例的简化版本;为了方便起见,我将 file.txt 中的行缩短了一点:

mkdir rep1
cd rep1
git init
printf "l1\nl2\n" > file.txt
git add touch.txt
git commit -m "initial commit"

cd ..
git clone rep1 rep2
git remote rename origin rep1 # for clarity
cd rep2
sed -i '.txt' 's/l1/l1rep2/' file.txt
git commit -am "append 'rep2' to first line"
sed -i '.txt' 's/l2/l2rep2/' file.txt
git commit -am "append 'rep2' to second line"

cd ../rep1
sed -i '.txt' 's/$/rep1/' file.txt
git commit -am "append 'rep1' to both lines"

cd ../rep2
git fetch

执行所有这些命令后,您的 rep2 存储库如下所示(file.txt 的内容显示在每个提交下方):

现在,在 rep2,你 运行

git rebase rep1/master

要了解此变基过程中发生的情况,请记住变基只不过是一系列 cherry-pick 操作,然后是一些分支改组;这在 Think like a Git tutorial.

中有很好的解释

首先,Git 尝试在提交 D 之上重播“A->B 补丁”。但是,Git 无法干净地应用此补丁;粗略地说,Git 本身无法确定应该保留的两行的哪个版本:

不知道该怎么办,Git 要求您解决冲突,您通过将 file.txt 的内容替换为

来解决冲突
l1-rep2
l2

(这是有争议的,因为这会创建一个 evil merge, as pointed out by Daniel Böhmer in )。然后暂存并提交更改:

git commit -am "conflict resolution"

您现在 运行 git rebase --continue,然后 Git 尝试在提交 E... 并且 成功 !该补丁应用干净;因为提交 BEfile.txt 的内容是相同的,所以这里没有任何冲突的原因,你最终得到