Successful use of git reflog

git reflog is one of those commands that I've tried to use many times with no success. Usually something goes horribly wrong with one of git's more advanced commands (ex rebase, cherry-pick, reset), I then search for something to recover my mistake, someone online says it is so easy with a simple reflog, I try it, make my problem worse, and eventually give up. But, a few days ago I had success!

Play-by-play

I opened a pull request and was resolving comments left on it. I fixed a group of related comments, made a commit for the changes, and started to run the tests. While the tests were running I went on to fix other comments. I stacked on two more commits. I then went to the tests and saw that they failed. My changes in the first commit since opening the pull request weren't quite right.

Here is what my commit history looked like

$ git log --abbrev-commit --pretty=oneline | head -n 5
64f97aac7 move checking for mpi to sim_data
5e95a50e3 simplify
a565074a0 mpi abort is needed; share between common-header.py.jinja and sirepo.mpi
688fd1c92 Fix #4038: Always call python parameters.py from slurm

688fd1c92 was the original commit where I opened the pull request. a565074a0 was the first commit resolving pull request comments. 5e95a50e3 and 64f97aac7 were more fixing of pull request comments.

So, I wanted to make some changes but they are relevant to 688fd1c92 since that was the commit I was running the tests for. My normal workflow for this sort of thing is to:

# make some changes
$ git stash
$ git rebase -i master
# select edit for the commit I want to edit (in this case 688fd1c92)
$ git stash pop
$ git commit --all --amend --no-edit
$ git rebase continue

I did just that and here is what I saw:

$ git status
On branch 4038-nersc-prepare-slurm
Your branch and 'origin/4038-nersc-prepare-slurm' have diverged,
and have 4 and 4 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

Shoot! I did git rebase -i master. Rebasing on master means that my original commit for the pull request, 688fd1c92, was modified from the rebase because I set the base to be master. I had broken the golden rule of rebase - never rebase on a public branch.

The correct syntax would've been git rebase -i a565074a0^. Which means rebase on the commit one before the commit I want to edit.

Ugh, now to recover from my mistake. Thankfully I hadn't pushed anything so recovery shouldn't be too bad.

Off to Google how to recover. I searched for "how to undo a git rebase". The first answer, as expected, says to use git reflog.

Here is what I saw:

$ git reflog | head
c152af107 HEAD@{0}: rebase (finish): returning to refs/heads/4038-nersc-prepare-slurm
64f97aac7 HEAD@{1}: rebase (pick): move checking for mpi to sim_data
c7e8b67af HEAD@{2}: rebase (pick): simplify
df5260920 HEAD@{3}: commit (amend): mpi abort is needed; share between common-header.py.jinja and sirepo.mpi
a56507a40 HEAD@{4}: rebase: fast-forward
688fd1c92 HEAD@{5}: rebase (start): checkout master
9fc121349 HEAD@{6}: reset: moving to HEAD
9fc121349 HEAD@{7}: commit: move checking for mpi to sim_data
5e95a50e3 HEAD@{8}: commit: simplify
a56507a40 HEAD@{9}: commit: mpi abort is needed; share between common-header.py.jinja and sirepo.mpi
688fd1c92 HEAD@{10}: commit: Fix #4038: Always call python parameters.py from slurm

Alright, I must be getting better as a programmer because this looks 10x less initmidating than the last time I remember looking at it. I see that for HEAD@{5} it says rebase (start). I probably want to move to the commit just before that. So I tried that:

$ git checkout HEAD@{6}
$ git diff

I did the git diff to make sure that things look ok. They do so I think this is where I want to be. Let's do it for real:

$ git switch -
$ git reset --hard HEAD@{6}
$ git status
On branch 4038-nersc-prepare-slurm
Your branch is behind 'origin/4038-nersc-prepare-slurm' by 2 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

A git diff confirmed that I was back to where I was before the botched rebase! The only bummer is that I lost the work that I wanted to apply to 688fd1c92. I'm sure there is a way I could've saved it but no big deal. It was easy to write again.

Editing a commit the right way

Now that git reflog saved my skin I wanted to edit the commit with my changes. Here is what I did:

# make the changes
$ git stash
$ git rebase -i 688fd1c92^ # ^ means one commit before 688fd1c92
# select edit for 688fd1c92
$ git stash pop
$ git commit --all --amend --no-edit
$ git rebase continue
$ git status
On branch 4038-nersc-prepare-slurm
Your branch is ahead of 'origin/4038-nersc-prepare-slurm' by 3 commits.
  (use "git push" to publish your local commits)

Nice, 3 commits and not in conflict with the upstream. Thanks git reflog!