What a commit is
The code I didn't touch should not appear anywhere since it is not on origin/master nor in my commit.
It is in your commit. You may be thinking incorrectly about what a commit is. It is not a diff. Commits are not changes. A commit is a snapshot of your entire project — all the files, in their current state. So if the parent of feature
had this code, and you didn't touch that code when you created the commit that is now feature
, then yes, that code is most certainly in that commit, because for it not to be there, you would have had to delete it, and you did not.
What you did
Switching to cherry-pick won't change anything; rebase
is cherry-pick
.
So let's run with that, and treat what you did as a cherry-pick. We will say that you cherry-picked the last commit of feature
— that is, the commit pointed to by the branch name feature
, itself — onto origin/main
. (The only actual difference in this case between rebase
and cherry-pick
is what happens to the branch name pointers afterward, and we aren't going to consider that at all in what follows, so it's irrelevant.)
How cherry-pick / rebase works
A cherry-pick is a merge, meaning that its job is to create a brand new commit, although the commit it creates is a normal commit, not a merge commit (that is, it has just one parent). The new commit is created using merge logic, using the parent of the cherry-picked commit as the merge base. (If you don't understand what I just said, read my https://www.biteinteractive.com/understanding-git-merge/.)
Therefore, when you cherry-pick the last commit of feature
onto origin/main
, you are saying to Git:
Think about the diff from the parent of feature
to feature
.
Think about the diff from the parent of feature
to origin/main
.
Enact both those diffs as applied to the parent of feature
, and make a commit expressing that, whose parent is origin/main
.
Very well. What are those diffs?
From the parent of feature
to feature
, some code was added adjacent to oldcode
.
From the parent of feature
to origin/main
, that same oldcode
was deleted.
That, I think, is the part that surprises people. You seem to imagine that origin/main
has no contribution to make here. But it does. The cherry-pick requires, among other things, that we be somehow able get from the parent of feature
to origin/main
. That can be quite an elaborate operation — and for that very reason, merge conflicts are very common when cherry-picking (including rebasing).
The conflict
So let's think about what each diff does with respect to oldcode
in the parent of feature
. feature
added to it. origin/main
deleted it. Thus you are asking Git both to add to the hunk and to delete the hunk. Those are contradictory instructions, so Git asks you what to do.
Resolving the conflict is very, very easy; this is probably the easiest and most common case of a merge conflict. You know what you want; you either want origin/main
to have both oldcode
and the new code, or you want it to have just the new code. But Git doesn't know what you want, so this still counts as a merge conflict, which merely means that you have to do a little manual editing yourself. Do it, don't worry, be happy, and move on.
A corollary
Since the part that probably surprises you the most is the contribution of origin/main
as a deleter of the code, let me enact a little drama for you. We start with this:
* 31da420 (HEAD -> mybranch) myotherfile
* 0ec170a myfile
| * 7dcb9af (main) emptied myfile
| * 7e2b31f myfile
|/
* 61bc628 start
Here's what happened so far since start
.
On main
, we added a file myfile
with contents hello
, and then we edited myfile
to be empty.
On mybranch
, we also added a file myfile
with contents hello
, and then we added another file, myotherfile
.
Now rebase / cherry-pick just the last commit of mybranch
onto main
. What will the state of myfile
be in the newly created commit? It will be emptied. That's because, from the point of view 0ec170a
, the contribution of 7dcb9af
was to empty myfile
. From the point of view of 0ec170a
, the other commit 31da420
did not contradict that in any way.
So you see, even if you had not added the new code, the fate of oldcode
("code I didn't touch") would not have been what you expect. You think it would have been left alone. It would not have been. It would have been deleted.