Skip to content

Fixup

Overview

The "fixup" command is very similar to "squash" but with a differences:

  • The commit message for a "fixup" commmit will just be dropped in favor of the message of the destination commit.
  • As commit messages are just dropped, there's no intermediate step to edit the commit message.

Contrary to squash this command can accept options to indicate which commit message must be preserved:

  • either use commit message from the fixed-up commit without prompt
  • or use fixed-up commit message but interrupt the rebase to let you edit it, as for squash.

Setup playground

From the course repository root, run the following commands:

bash
./scripts/create-playground.sh rebase-int_fixup

Check that you have a proper history in this playground repository.

bash
cd playgrounds/rebase-int_fixup ; git log --oneline
f14859b (HEAD -> main) fix bug
af1809a add debug to enqueue/dequeue
8c6e11a implement size
0633d08 implement dequeue
51c1ece implement enqueue
f07786e add constructor and inner collection
ac4ac84 class skeleton
53dab0d add test for queue size
0eee3d7 add test for dequeue on empty queue
70e2feb add more tests for `dequeue()`
d2df9b7 api use example for `dequeue()`
014de8b api use example for `enqueue()`
73d2c5e create file with draft specifications

WARNING

As playground rebuild a new repository each time you create one, the SHA1 in this course are only example. Only consider those in your local repository as reference.

Activity goal

This exercise is very similar to the one done for squash. Using the same code example, we will reduce the history to something like:

  • fix bug
  • add debug to enqueue/dequeue
  • implementation: all commits in the single "implement size" one.
  • test and specification: all commits in the single "add test for queue size"

Enabler

Whether you use fixup or squash, you need contiguous commits in log history. To be in this state, you need first to reorder commits.

The road is traced, just go ahead.

Blend all tests in single commit

In this first exercise we are going to fuse all commits related to testing in the first commit "create file with draft specifications".

First step is to start the interactive rebase process. Once again we will work from entire log history, starting from the very first commit.

bash
git rebase --interactive --root
  • The rebase command file will open in your editor.
  • Your shell is now waiting the editor to close this file.

The edited file "git-rebase-todo" should be something like the following, with commented lines removed:

git-rebase
pick 73d2c5e # create file with draft specifications
pick 014de8b # api use example for `enqueue()`
pick d2df9b7 # api use example for `dequeue()`
pick 70e2feb # add more tests for `dequeue()`
pick 0eee3d7 # add test for dequeue on empty queue
pick 53dab0d # add test for queue size
pick ac4ac84 # class skeleton
pick f07786e # add constructor and inner collection
pick 51c1ece # implement enqueue
pick 0633d08 # implement dequeue
pick 8c6e11a # implement size
pick af1809a # add debug to enqueue/dequeue
pick f14859b # fix bug

Remember

Remember that commits are shown in chronological order, the oppositive of the git log command.

The fixup command, without additional option, will blend this commit with one above in the list and will just drop the commit message.

You just need to replace the "pick" command by a "fixup" one, or the single-letter version "f". In the example bellow I used the abbreviated "s" for this command:

git-rebase
pick 73d2c5e # create file with draft specifications
f 014de8b # api use example for `enqueue()`
f d2df9b7 # api use example for `dequeue()`
f 70e2feb # add more tests for `dequeue()`
f 0eee3d7 # add test for dequeue on empty queue
f 53dab0d # add test for queue size
pick ac4ac84 # class skeleton
pick f07786e # add constructor and inner collection
pick 51c1ece # implement enqueue
pick 0633d08 # implement dequeue
pick 8c6e11a # implement size
pick af1809a # add debug to enqueue/dequeue
pick f14859b # fix bug

Note

For this this first part we only focus on test commits. Others will be merged in the second part, using extra options.

Save the "git-rebase-todo" file and close it to go back to the shell. Your history should now reflect your changes.

bash
git log --oneline
5343b99 (HEAD -> main) fix bug
d756b8a add debug to enqueue/dequeue
7bc3191 implement size
7560d07 implement dequeue
7aba77e implement enqueue
abd04f1 add constructor and inner collection
dbfbec5 class skeleton
65531bb create file with draft specifications

Fixup and select message

If you read the fixup command help in the rebase commande file, you might have noticed two options:

  • "-C" use this commit message, not the previous one, for the final commit message;
  • "-c" use this commit message, as above, but open editor to let you edit it.

We will first use the -C option.

Start a new rebase:

bash
git rebase --interactive --root

Editor will open with something like:

git-rebase
pick 65531bb # create file with draft specifications
pick dbfbec5 # class skeleton
pick abd04f1 # add constructor and inner collection
pick 7aba77e # implement enqueue
pick 7560d07 # implement dequeue
pick 7bc3191 # implement size
pick d756b8a # add debug to enqueue/dequeue
pick 5343b99 # fix bug

As first step, we will merge two commits:

  • "class skeleton"
  • and "add constructor and inner collection"

So change the command file to use f -C:

git-rebase
pick 65531bb # create file with draft specifications
pick dbfbec5 # class skeleton
f -C abd04f1 # add constructor and inner collection
pick 7aba77e # implement enqueue
pick 7560d07 # implement dequeue
pick 7bc3191 # implement size
pick d756b8a # add debug to enqueue/dequeue
pick 5343b99 # fix bug

Save, close and check the result with git log --oneline:

4443f07 (HEAD -> main)
9f78b97 add debug to enqueue/dequeue
7830551 implement size
ab837e6 implement dequeue
28c5845 implement enqueue
f42a56f add constructor and inner collection
65531bb create file with draft specifications

You can check the content of the new combined commit. Display the diff between previous and merged commit:

bash
git diff 65531bb..f42a56f
diff
diff --git a/queue-example.ts b/queue-example.ts
index eba61f2..214e514 100644
--- a/queue-example.ts
+++ b/queue-example.ts
@@ -8,6 +8,27 @@ A simple queue example
  - `dequeue()` remove the head of the queue and returns the value.

  */
+class Queue<T> {
+  /** Queue is a collection of values */
+  private items: T[];
+
+  /** Constructor just creates an empty queue */
+  constructor() {
+    this.items = [];
+  }
+
+  size(): number {
+    // TODO
+  }
+
+  enqueue(item: T): void {
+    // TODO
+  }
+
+  dequeue(): T | undefined {
+    // TODO
+  }
+}

 // ----- USAGE

Fixup, select and edit message

In this last exercise we are going to use fixup -c command to be able to edit message during the fixup action.

Start again a new rebase:

bash
git rebase --interactive --root

The rebase command file is opened in your editor:

git-rebase
pick 65531bb # create file with draft specifications
pick f42a56f # add constructor and inner collection
pick 28c5845 # implement enqueue
pick ab837e6 # implement dequeue
pick 7830551 # implement size
pick 9f78b97 # add debug to enqueue/dequeue
pick 4443f07 # fix bug

Let's fuse the three implementation method. Edit the file and add two fixup commands, one with the -c option.

git-rebase
pick 65531bb # create file with draft specifications
pick f42a56f # add constructor and inner collection
pick 28c5845 # implement enqueue
fixup -c ab837e6 # implement dequeue
fixup 7830551 # implement size
pick 9f78b97 # add debug to enqueue/dequeue
pick 4443f07 # fix bug

Save and close. A "COMMIT_EDITMSG" file will open to let you edit the message.

You can see all other commit messages, all commented. The only uncommented message is the one where we added the -c option.

git-commit
# This is a combination of 3 commits.
# The 1st commit message will be skipped:

# implement enqueue

# This is the commit message #2:

implement dequeue

# The commit message #3 will be skipped:

# implement size

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Sun Oct 26 22:39:39 2025 +0100
#
# interactive rebase in progress; onto e63c5ad
# Last commands done (5 commands done):
#    fixup -c ab837e6c9084491cf9b2104c89e9998eda61fcbf # implement dequeue
#    fixup 7830551 # implement size
# Next commands to do (2 remaining commands):
#    pick 9f78b97 # add debug to enqueue/dequeue
#    pick 4443f07 # fix bug
# You are currently rebasing branch 'main' on 'e63c5ad'.
#
# Changes to be committed:
#	modified:   queue-example.ts
#
  • Edit the message to be: "feat: implement enqueue, dequeue and size".
  • Save and close.

Rebase will complete with success. Check the result with git log --oneline:

c3ba35b (HEAD -> main) fix bug
8d3c02e add debug to enqueue/dequeue
c1a1471 feat: implement enqueue, dequeue and size
f42a56f add constructor and inner collection
65531bb create file with draft specifications

Important

If you use multiple -c options, only one message will be uncommented. Others will be commented. Obviously you will be able to uncomment them or edit as you want the final message.

What we learned

Using the "fixup" command will blend a commit with the previous one.

  • Command is very similar to squash except for message handling.
  • By default, the message of the commit with "fixup" will be dropped.
  • You can select a specific commit message by adding -C option.
  • It's also possible to pick one commit and edit it by using -c option.

What is the difference with a squash command?

  • squash do not give you the choice of the commit message, it always pick the oldest one.
  • fixup let you pick an arbritrary commit among all the blended commits.

Both actions are very similar and selecting one or the other will depend on your specific context.