Getting geeky with Git #11. Keeping our Git history clean with fixup commits

Git

This entry is part 11 of 11 in the Getting geeky with Git

In this series, we’ve put a big emphasis on keeping our Git history clean. A big part of it was using features such as rebase. In this article, we go further and learn about fixup commits. With them, we can easily modify changes we’ve introduced in a single commit in our history.

Let’s create a brand new repository to visualize better a situation in which the fixup commits can come in handy.

commit 675383fcbe5af72b38846e9d4b12d84d78bacee8 (HEAD -> master)
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:10:26 2021 +0200

Added hello world

commit 9a45ed3c7d689ce6db85597b0a5eaf37f3064d07
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:09:56 2021 +0200

Initial commit

Above, we’ve created a commit that added to the file. Unfortunately, we’ve made a typo. Let’s fix it!

Fixing an error in a commit

One way of fixing the above issue would be to use interactive rebasing.

If you would like to know more about interactive rebasing, check out Getting geeky with Git #6. Interactive Rebase

When we’ve started the process of rebasing, we now need to modify our file.

Once we’ve fixed our error, we need to amend the commit.

Successfully rebased and updated refs/heads/master.

Introducing fixup commits

Unfortunately, all of the above can be quite a chore. Because of that, we might be tempted to skip it and create a brand new commit that fixes our mistake. However, with fixup commits, we can do that while still maintaining a clear history!

First, let’s implement the changes we need.

To create a fixup commit, we need to obtain the hash of the commit we want to modify.

commit 675383fcbe5af72b38846e9d4b12d84d78bacee8 (HEAD -> master, origin/master)
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:10:26 2021 +0200

Added hello world

commit 9a45ed3c7d689ce6db85597b0a5eaf37f3064d07
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:09:56 2021 +0200

Initial commit

We now need to create a commit with the flag.

Git only needs a part of the commit’s hash to identify it. If you want to know more, check out Getting geeky with Git #2. Building blocks of a commit

Doing the above creates a brand new commit with the prefix in the message and adds it to our history.

commit 1652fdd237589ca31ed7416da1c45b4158c22a8f (HEAD -> master)
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:55:21 2021 +0200

fixup! Added hello world

commit 675383fcbe5af72b38846e9d4b12d84d78bacee8 (origin/master)
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:10:26 2021 +0200

Added hello world

commit 9a45ed3c7d689ce6db85597b0a5eaf37f3064d07
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:09:56 2021 +0200

Initial commit

Squashing the commits

The last step in cleanup up our Git history is squashing the fix with the commit it improves. To do that, we need to perform an interactive rebase with the flag.

We can use to make this a default behavior.

Doing the above squashes the original commit with the fix giving us a clean history.

The above is a simple example. The golden rule of rebasing is to avoid doing it on branches used by other developers, because it involves overwriting history and would cause issues for our teammates. Doing it on a master branch would not be a good idea in a real project.

Things to watch out for when creating fixup commits

Instead of using the command, we can create a fixup commit manually.

Doing the above will also cause Git to recognize this as a fixup commit when rebasing with the flag. This shows that Git uses the commit messages to figure out what commit the fixup belongs to. This can cause some issues. Let’s create a second commit with the same message:

commit 3d10af1b5affeb5b0ad4e1d24d302ae53ee93f59 (HEAD -> master, origin/master)
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 17:33:20 2021 +0200

Added hello world

commit 675383fcbe5af72b38846e9d4b12d84d78bacee8
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:10:26 2021 +0200

Added hello world

commit 9a45ed3c7d689ce6db85597b0a5eaf37f3064d07
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:09:56 2021 +0200

Initial commit

Above, we can see that we have two commits with the same commit message. Now, let’s create a fixup for the latest one:

Doing the above results in creating a new fixup commit:

commit fe507faafd83989cf0d85e305d6001725f4db926 (HEAD -> master)
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 17:39:23 2021 +0200

fixup! Added hello world

commit 3d10af1b5affeb5b0ad4e1d24d302ae53ee93f59 (origin/master)
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 17:33:20 2021 +0200

Added hello world

commit 675383fcbe5af72b38846e9d4b12d84d78bacee8
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:10:26 2021 +0200

Added hello world

commit 9a45ed3c7d689ce6db85597b0a5eaf37f3064d07
Author: Marcin Wanago <wanago.marcin@gmail.com>
Date: Sat Jul 31 15:09:56 2021 +0200

Initial commit

Because the fixup matches more than one commit, we need to proceed with caution when rebasing.

Since above, we’ve used , Git takes only the latest two commits into account when rebasing. Therefore, it worked out without issues.

The order of the commits when rebasing

We might encounter a problem if we rebase more commits and more than one matches the fixup commit.

Fixup works similarly to squashing. It melds the fixup commit into the previous commit. Above, Git attempts to use the fixup commit for the wrong commit and gives us a conflict. To deal with it, we need to move the line with the fixup commit down.

Summary

In this article, we’ve gone through the feature of fixup commits. They can be handy when we want to alter a commit straightforwardly. The easier this process is, the less often we will take a shortcut and create an additional commit messing our history. Since the fixup and autosquash features rely on commit messages, we’ve also dealt with some issues we might encounter.

Series Navigation<< Getting geeky with Git #10. The overview of Git hooks with Husky
Subscribe
Notify of
guest
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
TamusJRoyce
2 years ago

Why do all this when you can squash merge (branch-first, never rebase)?