How to smartly cleanup your commit history using git reset?
Tags: git, productivity, webdev, tooling
Published: 2020-08-16 16:37:54

### TLDR
Running `git reset --soft <commit-hash>` will move the branch to this old commit. And now when you run `git status`, you will see all the changes you have made since `commit-hash` in your staging area. You can then create a single commit to update your commit history. 

With `--mixed`(also a default) flag, Git will match your staging area with the content snapshot at that commit, which means all those changes since old commit have been unstaged too. Now, you can selectively stage those files and can create multiple new commits however you want.

---
A commit message should justify your intent for the change, and not just add unnecessary noise to the Git history. Therefore, It's a good habit to keep your commit history clean and Git has a few tools to achieve this.

In this article, I will show you how you can smartly clean up your commit history using `git reset` But before I throw in that tip, I would also like to unveil the reasoning behind it so that it can be more fruitful in a long run.

If you wish to skip the basics, you can jump straight to the [cleanup section](#cleanup) too. 

Let's get started. 

### Pointers 👈
**HEAD** and **branch** are two different concepts in Git. The branch is just a lightweight movable pointer to one of the commits you are on, whereas, HEAD by default points to the branch you are currently on.

![HEAD](https://dev-to-uploads.s3.amazonaws.com/i/4jal6h73mpjibnyo3d00.png)

When Git moves the HEAD, it also modifies the index and working directory to reflect the changes of that commit.

Now when you run `git reset`, it moves the branch HEAD is pointing to. For example, if you are on `master` branch, running `git reset 8fgh5` will move the branch to this commit as shown in the picture below: 
![git reset](https://dev-to-uploads.s3.amazonaws.com/i/fgertwtcu6uufwub7kzg.png)

>💡 `git reset` moves the branch HEAD is pointing to; `git checkout <branch-name>` moves the HEAD to that branch.

Git reset has 3 flavours at your disposal: --*soft*, --*mixed*, and --*hard*. Irrespective of which one you chose, running `git reset` will always start by moving the branch HEAD points to, and stops where you ask it to. It runs the following operations in order: 
1. Move the branch HEAD point to (**stop here if --soft**).
2. Modify the index(staging area) to look like HEAD (**stop here if --mixed**).
3. Make the working directory looks like the index (**--hard**).

With the foundation set aside, let's see how can you use the `--soft` and `--mixed`(also a default) options with `git reset` to clean up your commit history.

Time for some awesomeness 🎉.

###<a name="cleanup"/> It's cleanup time ⏰
If you have been lazily writing multiple vague commits, you can use `git reset --soft <old-commit>` to make your branch point to that old commit. And as we learned, Git will start by moving the branch pointer to it and stops right there. **It won't modify the index or working directory**. And everything you have committed so far since this commit (old-commit) will be shown in your staging area. It essentially undid all the `git commit` commands. Running `git status` will now show 'Changes to be committed' message.     

Now, you can go ahead and commit those changes with a new message 😍. 

I normally use `--soft` reset variant when I feel like squashing old commits into a single commit. But if you wish to create multiple commits starting from `old-commit`, you can also run `git reset <old-commit>`. Git runs this command with `--mixed` option by default. Doing so, Git will move your branch to that commit but also **modifies your index area to match HEAD**. In other words, all the changes you have committed since then will be unstaged. Think of it as the opposite of `git add`. Running `git status` will now show `Changes not staged for commit` message. 

Now you can selectively add files to the staging area and create meaningful commits to clean up your commit history.

>💡 **HEAD~{n}** refers to the nth parent of your HEAD. So, **HEAD~1** gives you the first parent commit of HEAD. 

### Closing thoughts 👋
I've always enjoyed this proverb: 
> "Give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime."

I hope that through this article I have not just given you a tip, but also have elaborated the rationale behind it. Now you can feel more confident using `git reset` in your daily workflow. Strive to keep your commit history clean, and your future colleagues will thank you for this 🙏. 

---
That's for today. And if you are interested in learning another tip, here's my article about [git interactive](https://dev.to/aman_singh/git-interactive-enhance-your-work-productivity-10x-4h2l).