Reputation: 105517
I'm reading this article about cherry-picking and there is the following picture there:
However, this picture seems misleading to me. From my simple test, it appears that, not just the difference, but the entire file content is merged. Here is the experiment: I have this commit graph:
A--B
\
C
Commit A
had this content in file.txt
:
l1
l2
l3
Commit B
had small change:
l1
l2-new
l3
Commit C
had small change:
l1
l2
l3-new
So now I'm trying to replay commit B
on the commit C
using cherry-pick,
git cherry-pick B
and I get a conflict,
<<<<<<< HEAD
l2
l3-new
=======
l2-3
l3
>>>>>>> C
which should not be there if only changes were applied. Since I didn't touch l2
line in my C
commit, it should have applied smoothly. Am I right?
Upvotes: 1
Views: 332
Reputation: 72256
Git
stores files, but it generates and uses diffs in a lot of places.
A diff
records the state of the affected lines before and after the commit; it also stores one line of text from before and after the modified area. Git
uses this information to avoid data loss and inconsistencies.
Let's say we created branches named A
, B
and C
on the commits having these names in your description.
$ git diff A B -- file.txt
diff --git a/file.txt b/file.txt
index f0f2307..0acba21 100644
--- a/file.txt
+++ b/file.txt
@@ -1,3 +1,3 @@
l1
-l2
+l2-new
l3
I asked Git
to display the changes on file file.txt
between commits A
and B
.
The format is a variation of the uniffied diff format and it is explained on the Wikipedia page about the diff utility
.
In simple words, this diff
says: change line 2 of the file from l2
to l2-new
but only if line 1 is l1
and line 3 is l3
. Even that only line 2 of the file was changed, Git
includes also the lines 1 and line 3 of the file in the diff
. They are the context of the change that happened on line 2.
Remark: In this case the file is small and lines 1-3 represent the entire file. Git
doesn't use the entire file, it only guards any block of lines that changed with 1 line before the change and 1 line after the change. If, for example, the file has 20 lines and we change lines 12 and 13, the diff
contains lines 11-14.
Back to what our diff
, on commit C
the file looks like:
l1
l2
l3-new
but in order to apply the diff
, Git
expects it to look like:
l1
l2
l3
Because its expectations are not met, Git
cannot apply the diff
safely and decides this is a conflict.
Git
need the context lines?Let's say on commit C
we deleted the second line. The file will look like:
l1
l3
Then we cherry-pick commit B
, and apply the changes it introduced (change the line 2 from l2
to l2-new
) without verifying the context. The file will look now like:
l1
l2-new
Wait a moment! Where is l3
?
I didn't delete the line l3
on commit C
and I didn't touch it on commit B
either. Applying a diff without checking the context, can lead to data loss. Git
always checks the context when it applies a diff and I suppose all the programs that work with diffs do the same.
Upvotes: 5
Reputation: 47062
You end up with a conflict because the changes are too close together (it terms of line number). The context (generally the 3 lines surrounding the actual diff hunk) need to match, otherwise Git (and other version control systems) will flag the change as a conflict. Since part of the context changed in your example, it was flagged as a conflict.
Upvotes: 2
Reputation: 4668
Git is storing files, not diffs, so mb since lines are close, git wasn't able to resolve it correctly.
Upvotes: 0