Git better with fixups
Tags: git, productivity
Published: 2020-06-18 01:29:55

## Intro

By now I'm sure many of you are familiar with the [Github Flow](https://guides.github.com/introduction/flow/).

Before I introduce some clever git commands to help maintain a clean git history, it's helpful to review some good git principles.

[5 rules to Git Better](https://www.koffeinfrei.org/2016/11/10/5-rules-to-git-better/)

If that link is not available the 5 rules are:
1. Make commits as small as possible
2. Write meaningful commit messages
3. Rebase always
4. Don’t squash everything
5. Commit instead of commenting out

Alright, now that we have that out of the way let's look at a common situation.

Let's assume the following as a starting point for the suggested workflow in this post:


## Preconditions 

You want a clean Git history

You don't want to rewrite your git history consistently


## Situation

```bash
# coding the component

$ git commit -m "add some component"

# missed an edge-case

$ git commit -m "fix missing null check"

# add component to service

$ git commit -m "add component to service"

# updating the component after service tests found a bug

$ git commit -m "fix component due to silly bug"

$ rubocop

# fix linting errors

$ git commit -m "fix linting errors"

# team members do a code review on the PR / MR

$ git commit -m "fixes based on PR comments"
```

Now this history isn't uncommon (in fact I can't think of many PRs of _real substance_ that don't go through something like this).  

This will result in a Git history that looks like this

```bash
fixes based on PR comments
fix linting errors
fix component due to silly bug
add component to service
fix missing null check
add some component
```

But is that really reflective of the work?  We don't want the commit history to be littered with "fix linting" commits.

This is actually representative of the work.

```bash
add component to service
add some component
```

## Solution

Let's go about these "fixes" in a different way.
We'll use the familiar command `git commit` but with an interesting flag called (appropriately) `fixup`.

```bash
# coding the component

$ git commit -m "add some component"

# missed an edge-case

$ git commit --fixup <COMMIT HASH OF THE COMMIT "add some component">

# add component to service

$ git commit -m "add component to service"

# updating the component after service tests found a bug

$ git commit --fixup <COMMIT HASH OF THE COMMIT "add component to service">

$ rubocop

# fix linting errors

$ git commit --fixup <COMMIT HASH OF THE COMMIT "add component to service">

# team members do a code review on the PR / MR

$ git commit --fixup <COMMIT HASH OF THE COMMIT "add component to service">
```

Now if we look at the git log we'll see

```bash
fixup! add component to service
fixup! add component to service
fixup! add component to service
add component to service
fixup! add some component
add some component
```

Instead of regularly committing (and also writing a commit message) we use the `--fixup` flag to create fixup commits.

Now, before merging the merge request, we issue an interactive rebase with the `--autosquash` flag.

#### Note:
> GitLab marks MRs that include fixup commits as WIP, so 
> merging is prevented if you haven't squashed.

```
$ git rebase --interactive --autosquash \
  <COMMIT HASH OF THE COMMIT "add some component">~1
```
We'll enter interactive rebase mode with the relevant commits already marked for fixup:

``` bash
fixup afa6970 fixup! add component to service
fixup 1da5d7c fixup! add component to service
fixup c998b08 fixup! add component to service
pick d9731b5 add component to service
fixup 33d6080 fixup! add some component
pick 73e8a6a add some component
```

The resulting Git history looks like this:

``` bash
add component to service
add some component
```

The final result is a very consistent and readable Git history, where every other developer knows what happens, without knowing about every bug on the journey to this final code.

## Summary

In summary, the following two Git commands are necessary to make use of the Git fixup workflow:

``` diff
$ git commit --fixup <COMMIT THAT NEEDS FIXING>
$ git rebase -i --autosquash <FIRST COMMIT THAT NEEDS FIXING>~1
```