I'm surprised that changes done after cherry-picking in git become obsolete when merging. Here is a full example.
The following is business as usual.
Mac:git user1$ mkdir myrepo; cd myrepo; git init
Initialized empty Git repository in /Users/user1/tmp/git/myrepo/.git/
Mac:myrepo user1$ echo "rotums kanoner och krut" > rotum.txt
Mac:myrepo user1$ git add rotum.txt
Mac:myrepo user1$ git commit -m "Added file"
[master (root-commit) 1044abb] Added file
1 file changed, 1 insertion(+)
create mode 100644 rotum.txt
Mac:myrepo user1$ git checkout -b mybranch
Switched to a new branch 'mybranch'
Mac:myrepo user1$ echo "mutors kanoner och krut" >> rotum.txt
Mac:myrepo user1$ git commit -am "Added mutor"
[mybranch 19afeba] Added mutor
1 file changed, 1 insertion(+)
Mac:myrepo user1$ git checkout master
Switched to branch 'master'
Mac:myrepo user1$ git cherry-pick 19af
[master cce2ca5] Added mutor
Date: Wed May 19 16:12:04 2021 +0200
1 file changed, 1 insertion(+)
Mac:myrepo user1$ cat rotum.txt
rotums kanoner och krut
mutors kanoner och krut
Now is when the unexpected behaviour occurs.
Mac:myrepo user1$ echo "rotums kanoner och krut" > rotum.txt
Mac:myrepo user1$ cat rotum.txt
rotums kanoner och krut
Mac:myrepo user1$ git commit -am "Removed mutor"
[master f63dc50] Removed mutor
1 file changed, 1 deletion(-)
Mac:myrepo user1$ git merge mybranch
Merge made by the 'recursive' strategy.
rotum.txt | 1 +
1 file changed, 1 insertion(+)
Mac:myrepo user1$ cat rotum.txt
rotums kanoner och krut
mutors kanoner och krut
Is this expected behaviour or a bug?
With a diagram :
Initial commit: create file
| Add a line in file (cherry-pick b)
| | Remove line, return file to its state in a.
v v v
a----c----d <- master
\
b <- mybranch
^
Add a line in file
When you merge mybranch
into master
:
a.
in the diagram),master
(commit d
) and sees that, when compared to a.
, the file is left unchangedmybranch
(commit b
) and sees that, when compared to a.
, a line should be addedso the merge succeeds without conflicts, and "brings in" the changes from mybranch
.
You reach this situation because :
git merge
does not inspect the intermediate commits in the history of master
and mybranch
,git cherry-pick
creates new, unrelated commits, and nothing is kept in the commit graph to indicate "these changes were already included",To give further perspective :
git rebase
:git checkout mybranch
git rebase master
unlike git merge
, git rebase
does compare the list of commits, and if a rebased commit introduces the exact same changes as another commit in the target branch, that commit is dropped.
In your example : b
wouldn't be reapplied, because it introduces the exact same changes as c
which is already on master
.
b
instead of cherry-picking : merge 'mybranch'
v
a---c----d <- master
\ /
b <- mybranch
then git merge mybranch
would have said already merged
, and wouldn't have re-applied commit b