How, why and when to squash your commit history
Tags: git
Published: 2024-06-18 09:49:45

I'm sure this has been covered a million times but here's number a million and one!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3rvfaamxiz5hwugzg8j.png)

I've been coding a decade now, but only working in an active team of devs for about a year or two so I'm pretty green on some git practices. One thing that I have never thought about until very recently is the problem with having too many commits.

**Downsides of too many commits:**
- It is often just too much to get your head around if you're doing a  review. Loads of tiny commits just make commits un-useful.
- If you have to do a rebase, you may be prone to lots and lots of commit merges. If you do as many tiny commits as me this can take a huge amount of time to work through.
- It makes it harder to find when bugs were introduced on your working branch. Fewer, descriptive commits are much more likely to yield answers a lot faster.

My approach has always been *do a thing* -> *do a commit* - what I will call scum-saving, which I of course learned as a practice from playing RPGs. The only change for me now is once I am ready to push I look at what I've done and see how I can realistically group the commits.

**Note:** there are arguments against doing this and there is such thing as going too far, but I shall address these ideas at the end.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hlma59fwr86azo8s33h6.png)

### Undoing commits using `git reset`

First, a quick explanation of `git reset`. This command is a way of rolling back commits in your local working tree. This can be used as an undo commit function when you mis-commit something or you can actually undo the changes in your code. There are three flavours:

`$ git reset --hard <commit>` - which uncommits **and deletes** all changes in your local files back to the point of the passed commit hash

`$ git reset --mixed <commit>` (which is default so you can leave the `--mixed` out if you want...) - which uncommits and unstages all changed but leaves the changes in your files

`$ git reset --soft <commit>` - which just uncommits them but leaves them staged ready to commit again

We will use the `soft` option to squash scum-save commits...

### Grouping (or squashing) commits

What we are actually going to do here is what is generally referred to in git terms as "squashing" your commit history - uncommit a bunch of commits, then re-apply the changes in one commit. So let's use `git reset` to squash some commits.

1. Using `git log` find the hash of the commit you want to reset to. This might be the point at which you branched from your `main` branch or it might be the last major commit.

2. Run `git reset --soft <your commit hash here>` All the changes since the specified hash will now be uncommitted and staged

3. Run `git commit -m "A message that sums up all these changes as one task"`

And congrats, your many commits have been turned into one single commit.

**Note:** if any of the commits between the given hash and your current HEAD have been pushed to your remote e.g. GitHub, you will need to *force push* the changes to overwrite the remote's history. This is done by running `$ git push origin your/branchname --force`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hh9fd8gfcn1gqhqvd64x.png)

The above is using the git CLI, however if you prefer a GUI, I would recommend [GitGraph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph) for VSCode which allows you to see your commit history, right click a commit and reset back to that point. Remember to choose soft.

### When to not do this

The main argument against doing this is it changes the history of your git repository. If you're working on a feature branch on your own this might be ok but do not do a reset and force push on a collaborative branch without speaking with the other people you are working with as they will need to make sure they reset to the same commit on their local machine.

Another pitfall is over-squashing. When it comes to reviewing a PR having just one commit might sometimes be ok, but in a complex feature the ideal is that each major step in completing the feature has a discrete commit. Perhaps the data fetching and processing, the presentational feature and then the tests. Dividing these into commits will make it a little nicer for a reviewer. That said, many repositories will just squash the contents of a PR as its merged, so the benefit of this is minimal.

So go forward and have a play. Remember you can always make backup branches when trying new things which you can merge back into your working branch if something goes wrong. Good luck and may your commit histories be understandable but still descriptive!