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

喜欢而已 提交于 2020-01-23 09:31:05

问题


I have rep1 repository with two commits on master branch. These commits have file.txt with the following content:

line1
line2

I clone rep1 into rep2 and checkout remote branch as tracking:

git checkout --track rep1/master

Then in this repository I modify first line of file.txt to be:

line1-modified-rep2
line2

Make commit. Then modify its second line to be

line1-modified-rep2
line2-modified-rep2

Make commit. So, here in the rep2 I have added two commits to master branch that tracks remote rep1/master branch.

Now I'm going to generate a conflict. On the remote rep1 repository I create third commit (there are already two there) modifying file.txt on the first and second lines to be:

line1-modified-rep1
line2-modified-rep1

Ok, now we're all set for merge conflict. I push commits from rep2 to rep1, get rejected and when prompt for rebase select this option.

Now, I was reading that rebase would apply my two commits from rep2 (where I modified two lines with prefix -rep2) on top of the synced down third commit from rep1 (with modified lines with prefix -rep1) and so I'm expecting two merge conflicts:

  1. when first commit from rep2 would be applied and the lines line1-modified-rep1 vs line1-modified-rep2 will get conflicted
  2. when first commit from rep2 would be applied and the lines line2-modified-rep1 vs line2-modified-rep2 will get conflicted

But there is only one merge conflict and upon its resolution I can successfully push my commits to the rep1. What am I missing here?

PS. Sorry for the lengthy elaboration, I tried to lay out my case as clear as possible.

EDIT:

So the setup before rebase is the following:

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

Added screenshots of the resolution process:

So the resulting file after the conflict resolution contains these lines:

line1-modified-rep2
line2

This is a log of git commands recorded by phpstorm (origin is rep1 here):

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

回答1:


TL;DR

Only one conflict occurs at the beginning of the rebase operation. Because of the way you resolve that conflict, the second patch applies cleanly; there is no cause for additional conflicts.

More details

For completeness, here is how to recreate a simplified version of your toy example from the command line; I've shortened the lines in file.txt a bit, for convenience:

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

After all those commands, your rep2 repo looks as follows (the contents of file.txt is shown underneath each commit):

Now, in rep2, you run

git rebase rep1/master

To understand what happens during this rebase, remember that a rebase is little more than a series of cherry-pick operations, followed by some branch shuffling; this is well explained in the Think like a Git tutorial.

First, Git attempts to replay the "A->B patch" on top of commit D. However, Git cannot apply this patch cleanly; roughly speaking, Git, on its own, has no way of figuring out which versions of the two lines it's supposed to keep:

Not knowing what to do, Git asks you to resolve the conflict, which you do by replacing the contents of file.txt by

l1-rep2
l2

(which is controversial, because that creates an evil merge, as pointed out by Daniel Böhmer in his comment). You then stage and commit the changes:

git commit -am "conflict resolution"

You now run git rebase --continue, and Git then attempts to replay the "B->C patch" on top of commit E... and succeeds! That patch applies cleanly; because the contents of file.txt in commits B and E are identical, there is no cause for any conflict here, and you end up with




回答2:


Looking at your screenshots, you didn't get the conflict on your second commit applied. Check Changes->Log of your PhpStorm and you should see two commits on top of remote/master. Or maybe even three, judging from the output of your git

Applying: rep2-commit 2
Applying: rep2-commit 3

Edit

The reason you didn't get conflict the second time is that you have merged it so that the file is exactly in the state of rep2-commit 2. If you chose to take rep1 changes, you would have conflicts all the way further.




回答3:


My two cents. Just before launching the rebase, you had something like:

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

After the rebase, you get

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

Right?

From the git rebase documentation, my understanding is that the content of commits D and E are first saved in a temporary area. Commit D is first applied on top of commit F, generating a conflict. At the end of the conflict resolution, commit D' is created. Then commit E is applied on top of commit D'. Commit E is the modification of line 2 on file.txt following commit D. So my point is : conflict resolution to generate D' is the critical step. Based on the choice made, the content file.txt for commit D' is equal to the content of file.txt in commit D. In that case, there would not be any conflict when trying to generate E' on top of commit D.



来源:https://stackoverflow.com/questions/27930402/why-does-a-conflict-occur-for-only-one-commit-instead-of-two-in-my-rebase-operat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!