问题
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:
- when first commit from
rep2
would be applied and the linesline1-modified-rep1 vs line1-modified-rep2
will get conflicted - when first commit from
rep2
would be applied and the linesline2-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