Git Reflog: Rescuing Lost Work in Git's Timeline
Tags: git, github, learning, computerscience
Published: 2024-03-30 14:34:25

Have you ever found yourself in a panic when you accidentally delete a commit or lose track of changes in your Git repository? Don't worry, you're not alone. Git, being the most powerful version control system, has a safety net called "Git Reflog" that can save the day.

In this article, we'll take a beginner-friendly dive into the world of **Git Reflog** and learn how it works, why it's essential, and how it can rescue your lost work in no time.

## What is Git Reflog?

Git has a feature called "reference log," or simply "reflog," that maintains a chronological record of reference updates in a repository. These references include changes to branches, commits, and other pointers, such as the HEAD pointer which indicates the currently checked-out branch or commit. Each entry in the reflog represents a snapshot of the repository's state at a particular point in time, along with information about the action that caused the change.

Each log entry in a reflog file typically contains the following information:

1. **Commit Hash:** The hash of the commit being referenced or affected by the operation.
    
2. **Previous Commit Hash:** The hash of the commit before the operation was performed.
    
3. **Action:** The action that triggered the reference update (e.g., commit, checkout, reset).
    
4. **Date and Time:** The timestamp of when the operation occurred.
    
5. **Author:** The name and email address of the user who operated.
    

By default, reflogs retain a record of each HEAD position for the past 90 days.

> It's important to note that reflog history is confined to the local repository and isn't accessible remotely.

These reflogs are stored in specific directories within the local repository's `.git` directory. These directories can be located at `.git/logs/refs/heads/`, `.git/logs/HEAD`, and also `.git/logs/refs/stash` if the Git stash feature has been utilized within the repository as Git maintains a separate reflog for its stash functionality.

## Why Git Reflog is Needed?

The primary function of Git reflog is to act as a safety net, enabling developers to recover lost commits or branches, reverse accidental changes, and navigate their repository's history effortlessly.

In contrast to commands like git reset or git revert, which can irreversibly alter the repository's history or have broader impacts, Git reflog offers precise control. Developers can pinpoint specific moments in the repository's timeline and restore lost commits or branches without affecting unrelated parts of the history.

Additionally, Git reflog provides transparency by revealing the sequence of operations and changes made to references in the repository. This visibility is crucial for understanding the repository's evolution over time and diagnosing development issues effectively.

## How to Use Git Reflog?

To use git reflog, you simply have to use either `git reflog` or `git reflog show` and you'll get an output like this -

```bash
a861fgd4b HEAD@{0}: commit: Initial Commit
06f00264a HEAD@{1}: commit: Add models
41d87a714 HEAD@{2}: commit: Add migrations
f80a505fb HEAD@{3}: commit: Add serializers
8dbf0c9f2 (origin/staging, staging) HEAD@{4}: pull: Fast-forward
62170e3c3 HEAD@{5}: pull: Fast-forward
```

Each reflog entry, in addition to its ordered index, is timestamped, facilitating filtering based on time.

To view the date and time information along with reflog entries in a more readable format, you can use the following option.

```bash
git reflog --date=local
```

This option displays the reflog entries with their corresponding timestamps in a local time format.

The output in this case would be like this -

```bash
72f3cbb50 HEAD@{Fri Mar 29 10:55:36 2024}: commit: Add missing created_at, updated_at
466c878c6 HEAD@{Thu Mar 28 16:15:21 2024}: commit: Update BaseModelAdmin
3cb9c945d HEAD@{Thu Mar 28 16:05:56 2024}: checkout: moving from ch/start-user-project to ch/minor-admin-changes
```

To see reflog entries with timestamps relative to the current time, rather than showing absolute timestamps with specific dates and times, you can use either of the following commands -

```bash
# To see activity on HEAD, including timestamp
git reflog --relative-date

#or

git reflog --date=relative
```

Relative timestamps make it easier to quickly understand when a particular action occurred relative to the current time.

The output, in this case, would look like this -

```bash
937136cbb HEAD@{3 weeks ago}: commit: Update trigger time
ffe27973b HEAD@{3 weeks ago}: commit: Update task name
c7b76e8f3 HEAD@{3 weeks ago}: commit: Add slack alert
```

You can also view the reflog entries for a specific branch(`staging` in my case) with relative timestamps. Here's how you can use it:

```bash
git reflog show --date=relative <branch_name>
```

The output of the above command would look something like this -

```bash
8dbf0c9f2 (origin/staging, staging) staging@{30 hours ago}: pull: Fast-forward
62170e3c3 staging@{31 hours ago}: pull: Fast-forward
34502ef1d staging@{3 days ago}: pull: Fast-forward
63c9ac44a (ch/ulj-weekly-application-summary-slack-message) staging@{4 days ago}: pull: Fast-forward
2c6bbf64f staging@{4 days ago}: pull: Fast-forward
```

By default, Git reflog provides insights into the history of the HEAD ref, which typically denotes the currently active branch in a repository.

However, Git also maintains reflogs for other references beyond just the HEAD. These references encompass branches, tags, remotes, and even the Git stash, offering a comprehensive record of recent actions across various parts of the repository.

The syntax for accessing a specific Git ref follows the pattern `name@{qualifier}`, allowing developers to pinpoint specific reference updates. For instance, `otherbranch@{0}` refers to the most recent update on the `otherbranch`.

The timestamp can also be used as a qualifier token, enabling precise retrieval of reflog entries based on some time-related criteria.

Commonly used time qualifiers include `@{0}` for the most recent update, `@{6.minutes.ago}` for actions six minutes prior, and `@{`[`3.day`](http://3.day)`.ago}` for events three days in the past. These qualifiers can be combined (e.g., [`1.day`](http://1.day)`.3.hours.ago`) and accept plural forms (e.g., `5.minutes.ago`).

For example, to view reflog entries for the `develop` branch from three days ago, you would execute -

```bash
git reflog develop@{3.days.ago}

#or

git reflog show develop@{3.days.ago}
```

This command retrieves reflog entries specifically related to the `develop` branch that occurred within the specified time window, enabling precise historical analysis and troubleshooting.

## Common subcommands to git reflog

### git reflog expire

The `expire` sub-command in Git helps clean up old or unreachable reflog entries, which can make your repository history tidier.

However, using this command without caution could accidentally delete important data. Usually, it's something Git handles internally rather than users directly dealing with it.

To play it safe, you can run a "dry run" with the `-n` or `--dry-run` option before actually deleting anything. This lets you see which entries would be deleted without actually removing them, acting like a safety net.

You can also set how long entries should stick around before expiring using the `--expire=time` option or by changing a setting called `gc.reflogExpire`.

### git reflog delete

The `delete` command allows you to manually remove particular reflog entries. You can indicate which entry you want to delete by specifying its reference and qualifier.

However, just like the `expire` command, using `delete` can lead to unintended data loss and isn't commonly used by regular users.

## What is the difference between git reflog and git log?

`git reflog` and `git log` are two components provided by Git that allow us to peek into the repository’s commit history, logs, and reflogs. Sometimes, they show similar histories, especially when a developer does a bunch of local commits without syncing with the remote server. This similarity can cause confusion between `git reflog` and `git log`.

However, they are different and have different uses.

The main difference is that the git log shows the public record of the repository's commit history, while git reflog is a private record specific to your workspace, showing your local commits.

When you push, fetch, or pull changes, the git log gets copied to the Git repository. But git reflog doesn't get copied, so you can't see it in the copied repository unless you have access to the computer where the local repository is stored.

The reflog is a file located in `.git\logs\refs\heads` that keeps track of local commits for a specific branch. It doesn't include commits that might have been deleted by Git garbage collection.

On the other hand, the git log shows a historical list of commits on a branch, starting from the most recent one and going back to the very first commit in the branch's history.

To summarize the differences between these two git commands, take a look at the following table -

| **Feature** | **git reflog** | **git log** |
| --- | --- | --- |
| **Tracking** | Tracks all reference updates (e.g., commits, branch operations, stash changes) in the local repository | Tracks the commit history of a specific branch or set of commits |
| **Purpose** | Provides a detailed history of recent actions, acting as a safety net for recovery | Presents a chronological list of commits with details such as messages, authors, dates, and hashes |
| **Scope** | Specific to the local repository | Reflects the commit history of the current branch or specified commits |
| **Usage** | Commonly used for troubleshooting, recovering lost work, and navigating recent changes | Typically used for reviewing project history, understanding changes, and tracking codebase evolution |

## Conclusion

In summary, Git maintains a reflog, which acts as a log of recent changes to your HEAD and branch references within the last few months (usually 90 days). This temporary history is continuously updated by Git whenever there's a modification to your branch tip.

Additionally, the reflog command offers functionality to manage this log. You can use the "expire" sub-command to remove outdated reflog entries, ensuring the log doesn't become cluttered with old data. Similarly, the "delete" sub-command allows you to specify and delete specific entries from the reflog when needed.