Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does git lose changes during a merge?

Let's say that:

  1. We have a master branch where a coworker accidentally added a series of commits (let's call them A B C) that should have belonged to a new feature.
  2. I discover that, and I tell him to move those commits to a new branch, but keeping other unrelated commits that were done later in master. I send him this question I asked and tell him to follow the response: git: how to move a branch's root two commits back
  3. Days later, when the new feature branch is ready, I merge it into master.
  4. After solving all the conflicts in the merge, I commit the changes...
  5. ...and I discover that those first commits (the A B C ones) have disappeared.
  6. I ask my coworker, and he says that "he thinks" that he moved those changes using the method mentioned in the link (basically: checking out the last common commit and then using git cherry-pick to pick only the commits that we wanted later), but he can't remember exactly.
  7. I check the repo's history, and A B C are in the feature branch, at the beginning. They look like they were successfully migrated from master.

Given the above, can anyone explain why git lost those changes? (My personal theory is that git somehow "remembered" that we had undone commits A B C, so when they came from the new feature branch, git decided not to merge them. EDIT: sorry if this explanation sounds too much like "magical thinking", but I'm at a loss. I welcome any attempt to put this explanation in more technical terms, if it's right).

Sorry for not being able to give more details, but I didn't make those changes in the repo personally, so can't give exact details of what was done.

EDIT: okay, as suggested here, I got my coworker to execute git reflog in his machine, so I am pasting here the results. To get back to my previous (linked) question, we had a tree like this:

A - B - C - D - E - F  master
            \ 
             \- G - H  new feature branch

And we wanted to move B and C to the new feature branch.

So, the git reflog he sent me is here. Commit 5acb457 would correspond to "commit A" in the graph above:

4629c88 HEAD@{59}: commit: blah
f93f3d3 HEAD@{60}: commit: blah
57b0ea7 HEAD@{61}: checkout: moving from master to feature_branch
4b39fbf HEAD@{62}: commit: Added bugfix F again
4fa21f2 HEAD@{63}: commit: undid checkouts that were in the wrong branch
1c8b2f9 HEAD@{64}: reset: moving to origin/master
5acb457 HEAD@{65}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
5acb457 HEAD@{66}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
1c8b2f9 HEAD@{67}: checkout: moving from 1c8b2f9bf54ca1d80472c08f3ce7d9028a757985 to master
1c8b2f9 HEAD@{68}: rebase: checkout master
5acb457 HEAD@{69}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
1c8b2f9 HEAD@{70}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
5acb457 HEAD@{71}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
1c8b2f9 HEAD@{72}: merge origin/master: Fast-forward
5acb457 HEAD@{73}: checkout: moving from master to master
5acb457 HEAD@{74}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
5acb457 HEAD@{75}: checkout: moving from undo_branch to 5acb4576eca4b44e0a7574eea19cca067c039dc5
5acb457 HEAD@{76}: checkout: moving from master to undo_branch
1c8b2f9 HEAD@{77}: checkout: moving from undo_branch to master
525dbce HEAD@{78}: cherry-pick: Bugfix F
a1a5028 HEAD@{79}: cherry-pick: Bugfix E
32f8968 HEAD@{80}: cherry-pick: Feature C
8b003cb HEAD@{81}: cherry-pick: Feature B
5acb457 HEAD@{82}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to undo_branch
5acb457 HEAD@{83}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
1c8b2f9 HEAD@{84}: checkout: moving from 1c8b2f9bf54ca1d80472c08f3ce7d9028a757985 to master
1c8b2f9 HEAD@{85}: pull origin HEAD:master: Fast-forward
5acb457 HEAD@{86}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
5acb457 HEAD@{87}: reset: moving to 5acb4576eca4b44e0a7574eea19cca067c039dc5
1c8b2f9 HEAD@{88}: merge origin/master: Fast-forward
5acb457 HEAD@{89}: reset: moving to 5acb4576eca4b44e0a7574eea19cca067c039dc5
1c8b2f9 HEAD@{90}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
5acb457 HEAD@{91}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
1c8b2f9 HEAD@{92}: merge origin/master: Merge made by the 'recursive' strategy.
7b912cd HEAD@{93}: checkout: moving from 7b912cdf33843d28dd4a7b28b37b5edbe11cf3b9 to master
7b912cd HEAD@{94}: cherry-pick: Bugfix F
df7a9cd HEAD@{95}: cherry-pick: Bugfix E
d4d0e41 HEAD@{96}: cherry-pick: Feature C
701c8cc HEAD@{97}: cherry-pick: Feature B
5acb457 HEAD@{98}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
22ecc3a HEAD@{99}: checkout: moving from 5acb4576eca4b44e0a7574eea19cca067c039dc5 to master
5acb457 HEAD@{100}: checkout: moving from master to 5acb4576eca4b44e0a7574eea19cca067c039dc5
22ecc3a HEAD@{101}: commit: bugfix E
3b568bc HEAD@{102}: checkout: moving from feature_branch to master
57b0ea7 HEAD@{103}: commit: blah
152c5b9 HEAD@{104}: checkout: moving from master to feature_branch
3b568bc HEAD@{105}: commit: bugfix D
fe3bbce HEAD@{106}: checkout: moving from feature_branch to master
152c5b9 HEAD@{107}: commit: blah
2318ebc HEAD@{108}: commit: blah
cc5ea32 HEAD@{109}: commit: blah
a5c2303 HEAD@{110}: commit: blah
544a99a HEAD@{111}: commit: blah
299f86a HEAD@{112}: commit: Feature G
fe3bbce HEAD@{113}: checkout: moving from master to feature_branch
fe3bbce HEAD@{114}: commit: Feature C
3852e71 HEAD@{115}: commit: Feature B
5acb457 HEAD@{116}: merge origin/master: Fast-forward

Can anyone make any sense of those 4 cherry-picks in a row? I suspect that he didn't really do the git cherry-pick master~3 thing, specially not the ~3 part (which admittedly threw me off when I first saw it too).

like image 380
PaulJ Avatar asked Apr 30 '17 13:04

PaulJ


People also ask

Will git merge overwrite my changes?

Will git merge overwrite my changes? With default strategy and default text merge driver, if there is no conflict, git never overwrites anything at merge. If there is a conflict then what "git does" fully depends on how person performing merge resolves the conflicts.

What happens when you git merge?

Git merge will combine multiple sequences of commits into one unified history. In the most frequent use cases, git merge is used to combine two branches.

What happens to a branch when you merge?

Merging Branches. Once you've completed work on your branch, it is time to merge it into the main branch. Merging takes your branch changes and implements them into the main branch. Depending on the commit history, Git performs merges two ways: fast-forward and three-way merge.

Does branch gets deleted after merge?

In a good workflow, the feature branch is deleted once its merged back into master. New branches should be created for each new feature(s) that you work on.


1 Answers

The reason why commits A, B and C are lost, that is because this is what the link you shared to your coworker did. Let illustrate by below graphs:

1. Assume the original commit history your coworker did as,

...X---A---B---C---D---E  master

2. Move A, B and C to feature branch. So your coworker created a new feature branch from master (commit E) or any of a commit. And rebase with below steps:

git checkout -b feature
git cherry-pick master~5 master~2

...X---A---B---C---D---E  master
                        \
                         A'---B'---C' feature 

3. Modify master branch by,

git checkout X
git cherry-pick master~2..master
git branch -f master
git checkout master

the commit structure will look like:

...X---D---E  master
     \
       A'---B'---C' feature 

So the direct reason is the command git cherry-pick master~2..master. It will rebase commit D and E directly on commit X, so you can’t find A, B and C on master branch.

Update:

Based on the git flog, it seems these HEAD information is not enough to show what your coworker did. And the feature branch seems to checkout from commit C not D by

3b568bc HEAD@{105}: commit: bugfix D
fe3bbce HEAD@{106}: checkout: moving from feature_branch to master
152c5b9 HEAD@{107}: commit: blah
2318ebc HEAD@{108}: commit: blah
cc5ea32 HEAD@{109}: commit: blah
a5c2303 HEAD@{110}: commit: blah
544a99a HEAD@{111}: commit: blah
299f86a HEAD@{112}: commit: Feature G
fe3bbce HEAD@{113}: checkout: moving from master to feature_branch
fe3bbce HEAD@{114}: commit: Feature C

So the structure should be:

A---B---C---D---E  master
         \
          G---H feature

If you only want to change the commit structure like:

A ---D---E  master
 \
  B---C---G---H feature

You can reset your master branch and feature branch as original, and then cherry-pick commits on master branch, details as:

git checkout master
git reset --hard <original commit id for E>
git checkout feature 
git reset --hard  <original commit id for H>
git checkout master
git checkout <commit id for A>
git cherry-pick master~4..master~2 #To make the commits as A---D---E (drop B and C)
git branch -f master
git checkout master
like image 156
Marina Liu Avatar answered Oct 19 '22 09:10

Marina Liu