3

I have a git branch which started to exhibit the inability to do even trivial rebases going back past a certain point, claiming that many commits had just vanished - and in debugging that issue, I found a simpler anomaly.

git log gives me a perfectly consistent list of the commits I expect to see. And if I use git reset --hard HEAD^ to move backwards in the commits one at a time, everything works as expected - until suddenly I just back over a dozen commits with no warning at all.

I start from this commit with ID 367df61041c380b3e769177779cb7c8ab21630b4, and a git log that looks like:

367df61 fixing flake8/syntax errors
14210b8 Merge remote-tracking branch 'origin/dev' into dev
a6818d5 Merge pull request #55 from rec/fast-gamma
e8ffdf2 Tweak exit manager code (fix #56).  Please enter the commit message for your changes. Lines starting
d1c464c Tweak masterBrightness.
975de68 Re-enable timedata end-to-end.
bf7e918 Add optional timedata.Renderer to driver_base.py.
b4daff6 Simplify _render slightly.
a306798 Stop slicing colors and add an offset instead.
b379f21 Move generate_header into util.
e50ec8c Simplify frame rendering slightly.
12cfc2d Extract data and functions into return_codes.py
764e4b9 Convert LDP8806.py from DOS line endings to Unix.
d3dc333 Remove old gamma tables.
5ae2934 Simplify gamma.py; prove backward compatibility.
bae1cf4 Temporarily disable timedata and simplify rendering.
c436f19 showing timedata usage
5228261 updating colors.py for py3

If I execute git reset --hard HEAD^ one time, I move to 14210b8 Merge remote-tracking branch 'origin/dev' into dev, as I expect, and the git log is consistent with the above.

But now, if I execute git reset --hard HEAD^ a second time, I move to c436f19 showing timedata usage, near the bottom of the above list! All commits between a6818d5 and bae1cf4 inclusive just vanish.

My suspicious eye is drawn to the commit a6818d5a2 which seems to include all those vanished changes.

How did this happen? Perhaps by merging from the wrong base branch or commit ID... but exactly what did we do wrong?

Left to my own devices, I'd revert to some known good commit, and then cherry-pick commits one at a time, skipping the merge completely. It wouldn't be huge amounts of work but I feel there must be a better way.

  1. What's going on? Is this a reasonable state for a branch to be in, and I'm making a mistake using git, or is something "broken"?

  2. If it's broken, how do I fix it? And how do we change our workflow to avoid breaking a branch again?

  3. If it isn't broken, how do we change our git workflow to handle this case?

My answer to 3. - disallow merge commits.

All of these boil down to the single question - given that I have these merges, how do I rebase over them?

Tom Swirly
  • 2,740
  • 1
  • 28
  • 44
  • @matt: the hat / caret suffix means specifically the *first* parent, unless you add a number. `HEAD^` and `HEAD^1` are both parent #1, and `HEAD^2` is parent #2. If you have an octopus merge (more than 2 parents) this continues on with `HEAD^3` and so forth. The actual order of parent commits is hard to observe with most tools (though some graphical viewers use line colors: the same color, when going past a merge, means "parent #1") but is specified: the first parent is the branch you were "on" (as in, `git status` says "on branch master") when you did the merge. – torek Sep 08 '16 at 07:00

2 Answers2

0

What's going on? Is this a reasonable state for a branch to be in, and I'm making a mistake using git, or is something "broken"?

Remember that git log shows a linear list of commits by default. This can be a bit confusing in the case of merges because a merge commit has more than one parent. You can use the git log --oneline --graph to see the commit graph. It will include extra visualization to help you see all of the parents of each commit.

If it isn't broken, how do we change our git workflow to handle this case?

I'm new enough to git that I'm not sure about the behavior of HEAD^ when a commit has more than one parent. This is actually the root of your problem as you reset your way back through the commit history. Also, I don't know about the behavior of git rebase when the commit range contains merges. You will need to do some research about both of these issues.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
-1

First, make sure in what status your working copy is with git status. You can get very surprising results when you forgot about a merge, rebase, bisect or other conflict situations.

Then, don't use git reset --hard when the current HEAD is still important. Use git checkout <hash> to move where you want and tag the commit there or open a branch there with git checkout -b <branchname> <hash>. All these safer commands will prevent that you lose HEAD by accident.

I have not seen yet that HEAD^ points to something else than the (first) parent commit. You can find a nice explanation for these kinds of navigation methods here. But when you are not absolutely sure then look up what HEAD^ is with git log HEAD^.

Just in case you lost your HEAD, maybe you can still find the old commits with git reflog if the garbage collector has not done its work, yet.

Community
  • 1
  • 1
Martin Sugioarto
  • 340
  • 3
  • 15
  • What!? I haven't lost my HEAD - I haven't lost any work. Everything is fine; I'm just trying to debug what's wrong with this branch that prevents me from rebasing it. – Tom Swirly Sep 12 '16 at 20:17
  • You haven't explained in what state the working copy is and you haven't answered any question by other people how the merge graph looks like. You wrote your commits have vanished. I just wanted to help. – Martin Sugioarto Sep 12 '16 at 22:36
  • Just because something unexpected happens when I rebase, doesn't mean I actually lose work! I was trying to debug a branch which was impossible to rebase through. No one did actually ask to see the merge graph - but yes, the problem is that merge commits have two parents. Still no idea how to rebase over them and preserve them, though... – Tom Swirly Sep 14 '16 at 02:30
  • There is not much information to help you. I cannot tell why the merge is "bad" as you called it. Are there commits that don't belong there? Or do you just want to have a linear history? – Martin Sugioarto Sep 14 '16 at 05:25
  • Or maybe you cannot restore the state before the merge (= roll back the branch back to the previous point containing just your commits)? – Martin Sugioarto Sep 14 '16 at 05:34