Git reflog: Restore Version Control History
Tags: git, versioncontrol, productivity
Published: 2022-09-02 11:33:42

---
title: Git reflog: Restore Version Control History
published: true
description: Did you know we can use Git reference logs to restore history in the commit log(?) Often we developers experience challenges when working …
tags: git, versioncontrol, productivity
---

Did you know we can use Git reference logs to restore Git commit history(?) Often we developers experience challenges when working with Git. We can accidentally force-push commits to the remote main branch, complete a rebase that went bad, delete a branch we wanted to keep or hard reset to the wrong commit.

In this post, we will look at `git reflog` and `git reset`. We will review some common scenarios on restoring local commits, restoring a deleted branch, and reverting force-pushed changes due to a rebase.

## Git reflog
[git reflog](https://medium.com/r/?url=https%3A%2F%2Fgit-scm.com%2Fdocs%2Fgit-reflog) is a tool that can help us recover almost anything - at least everything we have committed. The tool keeps track of commits that have been created but also discarded ones in a log called the reference log. The reference log is kept on our local computer under the `.git` directory in a local repository. It contains a list of commits that `HEAD` has pointed to and keeps track of the entire history of a commit. Besides the reference log for branches, we have a separate reference log for stash.

(Records of the history on a branch will expire after 30 days by default. We can change the expiration days, by running `git reflog —expire` in our terminal, if we want to keep the history for a longer time.)

It's important to know that _reference logs_ do not track in the remote repository, like pushes, fetches and clones; it is purely local and current to the working environment. Below is two screencaps, the first from Github Codespaces and the second from a local filesystem that initially created all the commits of the main branch in the _same_ repository:


**Github Codespaces**:
![Reference log from a workspace in Github Codespaces](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ae4iplwe9uk8a8v575ra.png)

**Local computer**:
![Reference log from local computer where the commits were initially created](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0szc19whz79i1abqcwqf.png)


## Git log vs reflog

[git log](https://git-scm.com/docs/git-log) is a command that shows a list of commits related to a branch. `git reflog` is similar to `git log` but shows a list of snapshots and times when `HEAD` was changed. reference logs  also reference other types such as _stash_, _branches_, _rebase_, _merge_ and _tags_. A reference is known as _refs_, and the syntax for accessing these is `name@{qualifier}`. Running `git reflog` is the most basic use case and essentially the equivalent of writing `git reflog show HEAD`. The output will look like this:

```bash
=> git reflog

47efaa2 HEAD@{0}: commit: refactor: remove post schema
f243f12 HEAD@{1}: commit: refactor: change author fields
4783ba0 HEAD@{2}: commit: chore: add .env to gitignore
a19d142 HEAD@{3}: commit: chore: update readme
f5ade9a HEAD@{4}: commit: chore: update yarn.lock
854c08f HEAD@{5}: commit (initial): feat: bootstrap sanity studio
```


## Git reset

[git reset](https://git-scm.com/docs/git-reset) is a command we can use to move back to a previous commit, discarding or undoing previous changes. We can run `git reset --hard <commit_sha>` to move our branch back in the commit history.

**A hard reset** (`git reset --hard`) will _completely destroy any changes_ and remove them from your local repository.

**A soft reset** (`git reset --soft`) will keep your changes, and stage them back automatically.

**A mixed reset** (`git reset --mixed`) which is the default, will keep you changes but they will also become unstaged. Even tho it is flexible, you will have to stage the changes manually.




## Restore local commits

### Scenario

We have made some commits that haven’t been pushed to the remote repository yet. We do a `git reset —-hard <commit_sha>` to a previous commit, and our local branch is set back to an earlier point and discarded all history after the chosen commit. We then run `git log` and see that the commits are gone. We cannot pull the changes from the remote repository since we never synced the changes.

### How to restore local commits

When running git reflog we can see that our changes still are tracked by the reference log; we even have a separate commit for the hard reset that we did earlier - take a look at commit `f243f12` on `HEAD@{0}`:

```bash
f243f12 HEAD@{0}: reset: moving to f243f12
eec6f27 HEAD@{1}: commit: refactor: author should not have image
47efaa2 HEAD@{2}: commit: refactor: remove post schema
f243f12 HEAD@{3}: commit: refactor: change author fields
```

With this information, we can reset our branch back to the state we discarded by using one of the references in the reflog. We can move our branch to the selected commit by running `git reset --hard <commit_sha>`:

```bash
=> git reset --hard f243f12
=> git reflog

f243f12 HEAD@{0}: reset: moving to eec6f27
f243f12 HEAD@{1}: reset: moving to f243f12
eec6f27 HEAD@{2}: commit: refactor: author should not have image
47efaa2 HEAD@{3}: commit: refactor: remove post schema
f243f12 HEAD@{4}: commit: refactor: change author fields
```

## Restore a deleted branch

We can recover deleted branches using SHA’s in the reference logs.

### Scenario

Not uncommonly, we might checkout a new branch in our local environment. We could have a branch called `chore/update-packages` and commit a change that adds [dayjs](https://day.js.org/) as a package:

```bash
=> git checkout -b chore/add-dayjs-package
=> yarn add dayjs
=> git add .
=> git commit -m 'chore: add dayjs package'

5a75042 HEAD@{0}: commit: chore: add dayjs package
```

At a later point, we checkout the _main_ branch and deletes the `chore/add-dayjs-package` branch — maybe we did not need it anymore for some reason:

```bash
=> git checkout main

47efaa2 HEAD@{0}: moving from chore/add-dayjs-package to main

=> git branch -D chore/add-dayjs-package

Deleted branch chore/add-dayjs-package (was 5a75042)
```

We will get a confirmation message that git deleted the branch. After a while, we discover that we could need the changes after all, so how do we get them back?

### How to restore a deleted branch

We can undo these changes with `git reflog` and `git checkout`. If we run `git reflog` we won't see any references to the deleted branch. We could use the commit SHA provided in the delete message (`5a75042`) to revert the latest commit on that branch. But if we have lost the message, we need to use the reference log. And if you named your commits well, it should be easy to find the missing commit take a look at commit `5a75042` on `HEAD@{1}`:

```bash
=> git reflog

// deletion of a branch is not logged to the reference log
47efaa2 HEAD@{0}: moving from chore/add-dayjs-package to main
5a75042 HEAD@{1}: commit: chore: add dayjs package
47efaa2 HEAD@{2}: moving from main to chore/add-dayjs-package
```

We can now checkout the deleted commit SHA and create a new branch from that commit running `git checkout -b <branch_name>`:

```bash
=> git checkout 5a75042
=> git checkout -b chore/add-dayjs-package
```

## Revert force-pushed changes

### Scenario

We reset our branch using git reset --hard to an earlier commit and run git push - force to override any changes that we otherwise could pull from the remote repository. But at some point, we want those changes back.

```bash
=> git reset --hard <commit_sha>
=> git push --force
```

### How to revert force-pushed changes

We can use git reflog and git reset to restore force-pushed changes:

```bash
=> git reflog

f243f12 HEAD@{0}: reset: a19d142 to f243f12
```

By reading the log we can see that the message on `HEAD@{0}` is `reset: a19d142 to f243f12`, which means that commit with hash `a19d142` was force-pushed by `f243f12`. That also gives us information about which commit we need to restore, the one with hash `a19d142`. We can simply do the same procedure as initially to move back to the overriden commit:

```bash
git reset --hard a19d142
git push --force
```

Finally, we force-push the new changes to the remote repository. Always be careful and check that you are on the correct branch before doing a force push. Especially to prevent that production code is not being affected or team member code is not being overwritten.

Thanks for reading! (This article was [originally posted on Medium](https://medium.com/dfind-consulting/restore-commit-history-with-git-reflog-67f7676e902f) )

_Code foh shizzle_