Time Traveling Through Your Repo: Git Reset vs. Revert
Tags: git, learning, tutorial
Published: 2024-05-31 08:38:48

Once upon a commit, in the mystical realm of version control... jokes aside 😅. Wassup fellow git adventurers! Today we'll be embarking on a journey through the mysterious realms of version control. We'll dive deep into the git universe to understand the mysterious powers of two commands: reset and revert. Buckle up, because this ride might get a bit bumpy, but fear not, for I shall guide you through with wit and wisdom!

#The Maverick Reset

Ah, the mighty **git reset**, a command so potent, it can make your HEAD spin. But wait! What is this enigmatic "HEAD" in the realm of git?
- It's like the captain's chair of your spaceship. It points to the current position within your repository. Picture it as the guiding compass of your git repository, pointing to the very tip of your current branch. It's the nucleus around which your commits orbit, determining what snapshot of your project you're currently viewing or altering.
- If you are on the master branch, HEAD will point to the latest commit on that branch. So when you hear about "resetting HEAD," it's like repositioning captain git's chair to a different point in space-time, perhaps to a previous commit or even a different branch altogether.

Back to our friend reset. Imagine you're in a time machine, and you want to teleport back to a previous commit. Well, git reset is your [DeLorean](https://delorean.com/), ready to whisk you away to the past. But beware, fellow time travelers, for with great power comes great responsibility! Git reset without caution can lead to catastrophic consequences. One wrong move, and poof! Your commits vanish into the digital abyss faster than you can say "commitment issues." So, before you hit that reset button, remember to backup your work.

Assuming the following is your commit history, let's talk about the responsibilities you'll have, going the maverick way:

```
C1 - C2 - C3 - C4 - C5
```

**Types of Reset**

- _<u>Soft Reset:</u>_ It's akin to a cautious peek into the time-traveling toolbox of coding. You move HEAD to a previous commit, but your changes stay staged, as if hitting the pause button on your code's journey through time. From the commit history above, say you want go back to C2. Running `git reset --soft <hash-for-C2>` or `git reset --soft HEAD~3` will uncommit the changes from C3 to C5, but will keep them staged. To get the hash for your commit, run `git log` then copy the desired hash. In **HEAD~3**, 3 is the number of steps it takes to get to C2. Hopefully your "head" is not spinning though. Pick the convention that suits you. Let's look at an example.

Suppose you have the following in your working environment.

```
.gitignore
app1.py
app2.py
```
You then decided to commit the two apps.

```
git add app1.py app2.py
git commit -m "feat: add apps"
```

Then later realized that you forgot to add the .gitignore file (meaning it is still unstaged), you can do the following.

```
# HEAD will get the very last commit
git reset --soft HEAD
git add .gitignore
git commit -m "feat: add apps and gitignore"
```

- <u>_Mixed Reset:_</u> Feeling a bit indecisive? This reset moves HEAD and resets the index to a previous commit, leaving your changes unstaged. Running `git reset <hash-for-C2>` will keep the changes from C3 to C5 unstaged. With the files in the example above, after committing the two apps, run `git reset HEAD`. Both the three files will then be unstaged, including the two that were initially committed.

**NOTE** that with mixed reset, you do not have to include the `--mixed` flag.

- <u>_Hard Reset:_</u> Ready to go all-in? Brace yourself, because this reset moves HEAD and resets both the index and working directory to a previous commit. It's like a cosmic eraser, wiping away your changes without mercy. Use with caution! To achieve it, run `git reset --hard <hash-commit-C2>`. This will do away with all the changes after C2 from your repository.

Let's take a look at an example by adding some files to the previous working tree. Assuming you committed the three files, then added one file. The new file (README.md) will be in an untracked status. Run `git add .` or `git add README.md`, according to your preference, to add it to your staging area then commit the changes, say, `git commit -m "docs: add readme"` Let's assume the hash for this commit will be 212w544. Your current working tree should now be as follows:

```
.gitignore
app1.py
app2.py
README.md
```

Now by running `git reset --hard <commit-hash-before-212w544>` the commit 212w544 will be discarded together with it's file (README.md). Your working tree should now be as follows:

```
.gitignore
app1.py
app2.py
```

**Ready to rewrite history?**

- After traversing the depths of time with your hard reset, it's now the moment to rewrite history. To discard the contents in your remote repository like github, gitea, or gitlab, run `git push <remote-name> <branch-name> --force`.
- This command will overwrite the history in the remote repository with the changes from your local repository, effectively discarding the unwanted changes.

**CAUTION!!!**

- In case you were working on a project with collaborators, this can potentially cause issues for those who have pulled the changes. Make sure to communicate with your team before force pushing to avoid conflicts.
- Ensure you have a backup of the remote repository in case you need to revert the changes.

#The Renegade Revert

Now, let's talk about git revert - the rebel with a cause, the anti-hero of version control. Unlike reset, which rewrites history like a stealthy ninja, revert takes a different approach. It acknowledges the past but refuses to let it define the future. Basically it's used to undo changes introduced by a specific commit, creating a new commit that reflects the reversal. It maintains a clear and linear history by documenting the reversal as a new commit.

**Here is how it works**

Suppose the following is your commit history:

```
C1 - C2 - C3 - C4 - C5
```
We want to undo the changes introduced by commit C2. Check the hash for your commit by running `git log`, then do the following:

```
git revert <hash-for-C2>
```

If you use the HEAD convention, it will be:


```
git revert HEAD~3
```

If there are conflicts during the revert process, resolve them manually. Git will guide you through the conflict resolution process.

After juggling with conflicts like a clumsy magician (sorry, what I mean is, after resolving all the conflicts), run `git revert --continue` to continue with the revert process. This will finally create a new revert commit, C6, that undoes the changes introduced in C2, leading to the following history:

```
C1 - C2 - C3 - C4 - C5 - C6
```

**Revert a revert**

- Some times you can find yourself in a git time loop, reverting a revert, undoing an undo.
- This often happens when you revert a commit only to realize it was a mistake, disrupting the fabric of your code continuum. But wait! Is that a glimmer of hope on the horizon? Yes, it's the git time machine offering you a chance to set things right.
- All you gotta do is `git log` to find the hash for the revert commit, in our case it will be the hash for C6.
- After getting the hash, run `git revert <hash-for-C6>`, which will create a new revert commit, C7, that undoes the undone changes introduced in C2, leading to the following history:


```
C1 - C2 - C3 - C4 - C5 - C6 - C7
```

The contents of the above history will be like when it started, as below:

```
C1 - C2 - C3 - C4 - C5
```

When you are done with your revert(s), if need be, `git push` the changes to your remote repository.

**Choose Your Destiny**

Your mission, should you choose to accept any of these methods, is to rewrite history without causing a paradox. If any of your projects vanish or get tangled in a time loop, the keyboards responsible for this article will disavow any knowledge of your actions. This article will never self-destruct... well, unless a glitch in the space-time continuum intervenes. Good luck git adventurers. Some of you might get the humor after traveling "back to the future" 😅. Essentially though you can:

- use _git reset_ when you want to rewind the HEAD or branch pointer, and you're okay with discarding changes.
- use _git revert_ when you want to undo changes in a public branch without rewriting history.

So, which path will you choose? Whichever path you take, experiment, explore, but always tread carefully. Until next time, cheerio 😁.