Easily Squash, Reword, Amend, and Sort Commits with Git Rebase
Tags: git, github, beginners, tutorial
Published: 2020-05-19 18:25:35

Recently (not *that* recently), I wrote an article explaining the oft-misunderstood git command: `git rebase`.

There I tried to ensure that anyone could glean the purpose and basic application of `git rebase`.

In this article, I'm going to explain a more intimidating application of `git rebase`. Choosing to rebase in this way allows me to convey the purpose and reasoning behind commits with clarity.

I'm talking using `git rebase` in the optional interactive mode. It is my preferred method of organizing code changes; good organization makes git a more powerful tool.

If you're unfamiliar with rebase, I'd give the [previous article](https://dev.to/jacobherrington/git-rebase-explained-simply-k0a) a look before reading this one. This article will provide a glimpse into more powerful applications of the command.

Running `git rebase -i` invokes a rebase in interactive mode.

Why is it called the interactive mode? Glad you asked. Interactive mode allows the programmer to influence how rebase executes. In the previous article, we used rebase to bring upstream changes into our branch, but we didn't manipulate any commits in that process.

In many ways, the process we previously followed is ideal; we didn't have to do any work, git handled everything for us!

Unfortunately, the real world is rarely ideal.

When it comes to git, we frequently make incomplete commits, forget to include important changes, or make bad commit messages.

In our scenario, we can imagine that we've got a branch called `some-feature` which looks something like the following:

```sh
47a0a7008b Add a rea322ally cool feature
6dbc838bae WIP forgot something from that feature
f2ee0378f9 WIP fixing a bug
f0978af68d Fixed the very annoying bug
61b6f8ea3e Fix a typo from my doc changes
8340051649 Update documentation
f8a81956e2 Fix documentation formatting
```

There are a few things that we might not like about this git history. For example, we have a couple of WIP (work-in-progress) commits that could be removed, a typo in one of our commits, and a commit that fixes a typo that we introduced in a previous change on this branch.

We are planning to merge this branch into another branch called `master`.

To fix the obnoxious git mistakes in this branch, we can use `git rebase -i master`.

When you run `git rebase -i` with another branch you'll be dropped into a text editor with the following prompt:

```
pick 47a0a7008b Add a rea322ally cool feature
pick 6dbc838bae WIP forgot something from that feature
pick f2ee0378f9 WIP fixing a bug
pick f0978af68d Fixed the very annoying bug
pick 61b6f8ea3e Fix a typo from my doc changes
pick 8340051649 Update documentation
pick f8a81956e2 Fix documentation formatting

# Rebase a6305fd0a..4a5782397 onto a6305fd0a (1 command)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```

The prompt is extremely helpful, but sometimes a wall of text like this one can be intimidating.

In this view, git is showing you a list of the commits that exist in your branch, but not in `master`. They will be replayed on (or added on top of) the history in `master`. Each commit has a "command" alongside it.

For example, `pick 47a0a7008b Add a rea322ally cool feature` shows the command `pick` on the commit `47a0a7008b` followed by its commit message.

Each command does something unique, you can read through the commands listed in the prompt if you like, but my most used commands are: `squash`, `reword`, and `fixup`.

To progress in the rebase you should replace the `pick` command with your preferred commands, then save and close the file.

I'd fix our example history like this:

```
reword 47a0a7008b Add a rea322ally cool feature
fixup 6dbc838bae WIP forgot something from that feature
pick f2ee0378f9 WIP fixing a bug
squash f0978af68d Fixed the very annoying bug
pick 8340051649 Update documentation
fixup 61b6f8ea3e Fix a typo from my doc changes
squash f8a81956e2 Fix documentation formatting
```

A few things happened.

First, I grouped the documentation commits together. This makes it easier to squash and follow the git history.

Secondly, I chose to `squash` or `fixup` several of the commits that were work in progress commits or fixes for issues I might have created while working on this branch (minor typos, formatting, work-in-progress commits).

Finally, I chose to reword the first commit message because it contains a typo.

Upon saving and closing this file, git will go to work. Each time a commit is squashed or reworded, I will have an opportunity to write a new commit message.

For example, when I hit the first commit I can reword it to "Add a really cool feature." When I hit one of the squash commits, I'll have the option to merge both commit messages or rewrite it entirely. Those commits with the `fixup` command are treated exactly like the ones with `squash` except that the commit message is completely dropped.

Assuming we don't have any merge conflicts, once the rebase has completed, the git history might read something like this:

```sh
47a0a7008b Add a really cool feature
f2ee0378f9 Fix a bug
8340051649 Update documentation
```

Those commits are much easier to follow and should make for a cleaner PR.

### There's more...

I'm writing [a lot of articles](https://jh.codes) these days, I run a [podcast](https://www.devpath.fm), and I've started sending out a [newsletter digest](https://pages.convertkit.com/674caf55d4/f30f7753a7) about all of the awesome stories I'm hearing.

You can also follow me on [Twitter](https://twitter.com/jakeherrington), where I make silly memes and talk about being a developer.