How-to Dig Out of git reset "git hole"
I want to share a git
hole situation I had a hard time digging
myself out of.
I show how I dug myself into the situation by resetting changes more
than I wanted and show to easily fix the problem by using git
reflog
.
If you are in or run into this situation, you won’t have to agonize
how to dig yourself out of the git
hole and can elegantly put the
repository into a desired state.
This article will take you about six minutes to read.
Introduction
Ever get yourself into a “git hole”, where the repository’s state is not what you want by running the wrong command?
Most of the time, just “pulling down the repository again” will fix it (as with almost all problems related to software).
What if there’s actually work you want to keep or worse, other people’s commits you want to save?
What Happened?!
In my situation, a team member fat-fingered a command and put the repository into weird state. A state he didn’t want it to be in.
This article, I share the same situation using a practice repository.
If you want to follow along code in this article:
- Clone repository:
git clone https://github.com/a-leung/tdd_indepth_callbacks.git
- Change to branch:
git checkout fix/update_rails_1
Starting Out
I was looking at recent git history, say before pushing up changes and making sure I have a clean history when pushing up changes:
Well, the running bin/rails app:update
can be part of the Update
Rails to 6.0.3.2
commit because they’re the same process.
Clean up History
In this case, the easy thing to do: reset the last commit and amend it into the commit before that.
Step 1, run: git reset HEAD~1
$ git reset HEAD~11 Unstaged changes after reset: M Gemfile M Gemfile.lock M app/models/order.rb M bin/rails M bin/rake M bin/setup M config/cable.yml M config/environments/development.rb M config/environments/production.rb M config/environments/test.rb M config/initializers/content_security_policy.rb M config/locales/en.yml M config/puma.rb M config/routes.rb M config/spring.rb M db/schema.rb M spec/models/order_spec.rb
What the?! What did I do???
I typed: git reset HEAD~11
instead of git reset HEAD~1
!? I jumped
back 11 commits instead of 1!!!!
AUGH!
What’s the git status
???
Is this situation true??? Let’s confirm using git status
Yeah, there are files that are yet to be commited that are not part of the commit I want to squash. Crap. Did I just lose commit history too?!
What does git log
say??
There’s gotta be a mistake, what’s the current log???
The first entry of git log is from January 18, not July 3, which is what I wanted.
There’s definitely commits missing. In this case, they’re only my commits. If I had commits from team members lost, it would be even worse.
How do I fix this?!
After catching my breath and realizing: well, at least I didn’t lose actual work. That would be even worse. I’ll take the extra “git blame” on those changes. Recreating and extra 10 commits of work, oh boy. Things can definitely be worse. :-)
Enter: git reflog
git has a tool that tracks all the changes of the current HEAD, just
run: git reflog
. When I run it in my case:
This is the last set of actions I took on the repository. git
tracks
all changes made to the HEAD reference of the repository, stating the
SHA value of the change, the HEAD history from 0, and a description of
the action.
How can I use this??
To fix the problem of git reset HEAD~11
, which is in the reflog as:
The solution is the entry just before it:
To have git change HEAD to that entry, just do a git reset
to the
HEAD by running command:
Did that work?!
Let’s check the repository status now, what’s staged?
All clean… how about the git history?? Let’s check that using git
log
:
Everything’s there again, just like it was at the beginning. All work committed with the right commit history.
More information
If you want to find out more, the best place to look is the official documentation on reflog.
How it works
Although git presents a linear time line of work on the tracked items, under the hood, git is tracking every change in a non-linear manner. This is why you can even recover squashed commits.
The reflog
is another entry to git’s guts without exposing all the
details.
Why it’s important to know
If you want to be better at your craft, knowing your tools better improves your craft.
git is an essential tool for software development as it’s one of the most used code versioning systems today.
Another Solution
Given this knowledge of using the reflog
- there is another way of
solving the same problem without using the reflog:
If you notice, the entry:
has the same SHA as:
Which refers to this commit:
The original commit I wanted to “reset” to.
This means running, just running git reset <sha>
would have solved
the same problem:
Will have the same effect.
reflog
won’t be necessary if you had the commit SHA you want to
return to. Either in your screen’s buffer or even on the upstream
repository.
Conclusion
I never knew the reflog
was there and now that I know it, I couldn’t
believe why didn’t I know it sooner!
It would have saved me a lot of stress earlier in my development career and awkward conversations.
With reflog
, I see it’s a clean way into the guts of git as it’s how
git is tracking what to point to inside it’s own database of stuff.