Search code examples
gitgit-mergegit-branchgit-commitgit-log

Git reset HEAD~1 goes to the wrong commit


I read about commits, parents, and ancestors. To get more clarification, I tried to experiment with it and created a test git repo. My goal was to merge 3 branches into the master branch to understand how parents and ancestors differ. The following screenshot is the git log prettified.

enter image description here

As we can see in the log, the merge commit 44f1883 seems to have three parents i.e. 4385eae, 7288d94 and ee647fe. I don't understand why doesn't it have 98c9d28 as it's parent as well. I think 98c9d28 should have been it's the immediate parent so that if from master, I run git reset HEAD~1, it should land me at commit 98c9d28 which is the state before I did merge but unfortunately, it lands me at 4385eae which logically is incorrect(although correct from the log perspective).

Am i missing something?


Solution

  • What you're seeing is git merge trying its best to be helpful when given an unusual command.

    From master, you must have said

    git merge branch1 branch2 branch3
    

    This is allowable and performs an "octopus merge", but it is not the usual way to merge branches - normally you merge one branch in at a time. When you do use octopus merge, there are limitations (conflicts generally won't be handled well); and even when it works the behavior, as you see, can be a little strange.

    If you look at your output from the merge command, you'll see that it used a fast-forward to branch1, and then performed the merge for the rest of the branches from there. Presumably this was to reduce the complexity of the octopus merge.

    (If you're not familiar with the fast-forward behavior of merge... Basically git sees that master contains no changes that aren't already in branch1 so instead of merging it just moves master to branch1.)

    In any situation you may or may not want git to use fast-forwards, and in this case it can be particularly confusing because the fast-forward is immediately combined with additional merge operations, making it less obvious what just happened - and, as you say, causing the final graph to perhaps not look like you'd expect.

    So you can add the --no-ff option to your merge command, and then it won't do that. (Interestingly, in my tests the output still suggests that a fast-forward was used; but the final graph shows all 4 parents. Most likely it still uses the shortcut algorithm to try to produce the results, but then records the commit as though it hadn't.)

    But realistically, I'd be wary of octopus merging anyway.