I'm wondering why git doesn't allow cherry-picking a commit that introduces changes to a file that doesn't exist. My understanding is that a commit represents a full snapshot of working directory. So cherry-picking a commit containing file that currently doesn't exist should simply create that file - but I can see that it's not the case.
Below is a couple of commands to reproduce the situation I'm talking about.
$ git init
Initialized empty Git repository in /home/mzakrze/workspace/tmp/.git/
$ echo "Hello world!" > first_file; git add first_file; git commit -m "Init commit"
[master (root-commit) 7f9478a] Init commit
1 file changed, 1 insertion(+)
create mode 100644 first_file
$ echo "Fox jumps over the layz dog" > test; git add test; git commit -m "Commit with a typo"
[master 776387b] Commit with a typo
1 file changed, 1 insertion(+)
create mode 100644 test
$ echo "Fox jumps over the lazy dog" > test; git add test; git commit -m "Fix typo"
[master 9ea19df] Fix typo
1 file changed, 1 insertion(+), 1 deletion(-)
$ git log
commit 9ea19dfe2597b28eb576e2682e745de3da74733f
Author: mzakrze <xxxxxx@gmail.com>
Date: Sun Apr 11 17:01:13 2021 +0200
Fix typo
commit 776387b563edba9df2a12c5d1a2fd5bffb10c643
Author: mzakrze <xxxxxx@gmail.com>
Date: Sun Apr 11 17:00:53 2021 +0200
Commit with a typo
commit 7f9478a83e3938bf57552c1b90ddc7322b1bf315
Author: mzakrze <xxxxxx@gmail.com>
Date: Sun Apr 11 16:59:04 2021 +0200
Init commit
$ git reset --hard 7f9478a83e3938bf57552c1b90ddc7322b1bf315
HEAD is now at 7f9478a Init commit
$ git cherry-pick 9ea19 # cherry-pick "Fix typo"
error: could not apply 9ea19df... Fix typo
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
INB4: I'm not looking for a "solution", I just want to understand why git thinks it's a conflict.
Remember that cherry-pick is technically a three-way merge. You run git cherry-pick C
, for some commit C
—often specified by a hash ID—and Git finds the parent P
of commit C
:
P
is now the merge base;ours
commit is the current (HEAD
) commit; andtheirs
commit is commit C
.The difference between P
and HEAD
is that the file in question has been deleted entirely. The difference between P
and C
is that the content of the file has been modified.
For the standard merge algorithm (git-merge-recursive
), this is a high level or tree level conflict: a modify/delete conflict. That's a merge conflict that cannot be resolved with -X ours
or -X theirs
. The merge therefore stops in the middle, leaving you with a merge conflict that you must resolve.
Resolve the merge conflict and run git cherry-pick --continue
to finish the cherry-pick. The way you resolve this merge conflict is up to you; the git checkout
command in larsks answer is fine, or you can use git checkout --theirs test
and git add test
. Note that when using git checkout --ours
(not applicable in this case) and git checkout --theirs
, Git does not mark the conflict resolved, but when using git checkout CHERRY_PICK_HEAD
(or a raw hash ID), Git does mark the conflict resolved. This is due to a quirk in the implementation of git checkout
.