A gentle introduction to interactive git rebase
Tags: git, tutorial, webdev, github
Published: 2022-04-07 08:33:57

{% youtube https://youtu.be/9QsFj4_1UAw %}
## Introduction

It may be fair to assume that almost every development team uses `git` for version control and to collaborate on code. Incorporating `git` into your workflow for the first time can be quite daunting. Another related challenge is trying to get your head around `rebasing`. In this tutorial, we will learn a practical beginner’s workflow that utilises `interactive rebasing`.

## Prerequisites

To follow along, you will need to have [git](https://git-scm.com/downloads) installed. You can head to their website and [download the version](https://git-scm.com/downloads) that suits your environment.

If you’re unfamiliar with `vim` commands, you can check out the [little table of commands](#some-vim-commands-used-in-this-guide) at the end of this article to get a small crash course.

## Thoughts about pull requests

Before we dive into the meat of this walkthrough, let’s take a moment to talk about pull requests. I find it helpful to think about a pull request as if I were documenting a potion recipe that I’d like to eventually pass on to my future self.

Having this mental model helps me organise my commits to tell a coherent story. It helps me decide the sequence and composition of my commits.


![A PR is like a potion recipe](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0kf7oy5xoq9d903uwqpp.png)

## Creating a new branch

![Branch with cherry blossoms](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zh291oggif0vard6756x.gif)

Beginning a new task means we’ll need to create a new branch. At this point, we can pick our **base** branch. By default, our base branch would either be the `main` branch or the `develop` branch. Occasionally, we may choose another feature branch as our base. Regardless of the base branch we choose, we’ll want to make sure it’s up-to-date before creating our new branch.

For this example, we have decided to base our work off of `develop`. We can checkout `develop` and once we’re on `develop`, we can pull the latest changes:

```bash
git checkout develop
git pull --rebase --autostash
```

The `--rebase` and `--autostash` flags are a nice way to avoid conflicts when pulling changes. If you have uncommitted local changes, they are stashed before the rebase.


> 💡 I personally use the `git pull --rebase --autostash` command a lot, so I have saved it as an alias called `git up`. If you’d also like to do this, you can run this in your terminal:
`git config --global alias.up 'pull --rebase --autostash'`


Once our chosen `base` branch has the latest changes, we can create our new branch:

```bash
git checkout -b feature/potion-for-posterity
```

## Writing commit messages


![Cat writing commit message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9h2zk5sgke80f0vbji01.gif)

As we happily code away, we should remember to periodically commit our changes to avoid unexpectedly losing our work. It is a good idea to make small, but frequent, commits. It is also a good idea to group relevant changes under a commit message that is descriptive and applicable. Remember, these are messages we are writing for our future selves, so they should act as documentation.

Calling back to our potion analogy, our commit messages should read like simple instructions for a recipe or a to-do list. Some examples of commit messages could be:

- Add Posterity model
- Add Posterity controller
- Add migration for Posterity
- Add tests for Posterity

We can `add` and `commit` our changes by running the following commands and writing our message in the resulting editor:

```bash
git add .
git commit
```

Git allows us to write our commit messages inline by using the `-m` flag. However, writing our commit message in the editor lets us see when our message has gotten too long. In the screenshot below, we can see that the commit message text has changed colour to let us know we’re exceeded the recommended character limit. There is no rule that requires us to adhere to this character limit, it’s just a personal preference.


![Long commit message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2aonxaz10yjnj1akhjke.png)


> 💡 Having this length constraint means we won’t have to expand it to see the full message on Github. It’s not a big deal, though, just a matter of preference.

## Handling changes coming into the base branch


![Dancing foxes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9vyijzfsoat78tfhgmpe.gif)

It is normal to have teammates merging their work into the `base` branch throughout the course of a work day, especially if the `base` branch is `develop`. To avoid potential conflicts later on, we’ll want to make sure we’re keeping up-to-date with the latest changes when we can. It is a good idea to periodically *reset our base branch*.

To do this, we can `commit` or `stash` the changes on our current branch. We can push these changes up to Github just to be extra safe. Once the changes are safe and out of the way, we can checkout our `base` branch and pull the latest changes. As a reminder, our base branch is `develop` for this example, but it can be any other arbitrary branch as well.

```bash
git checkout develop
git up # alias for `git pull --rebase --autostash`
```

Once our `base` branch has all the latest changes, we can switch back to our working branch and rebase against our `base` branch

```bash
git checkout feature/potion-for-posterity
git rebase origin/develop
```

## Interactive rebase

When we use `rebase`, we can think of it as *resetting our base branch*. Rebasing helps us keep our commit history clean. As we talked about in the beginning, a PR is a recipe for a potion we’d like to pass along to our future selves or to other developer friends. So it should read like documentation.

A `rebase` can be done *manually* or *interactively*. I personally find that having a visual guide is helpful, so I usually opt to use interactive rebase. In this section, we’ll walk though some scenarios where we may want to “rewrite history”. We won’t go over every single action, just the ones we’ll find ourselves using most frequently.


![Rebase intro](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hpkmha240lkwsx7driml.gif)

### Squash

There will be cases where we may want to `squash` several commits together. For instance, we may find that we have a handful of small commits that don’t really tell a full story on their own and could work better as part of a single, larger, commit.

In the animated GIF example, below, we have 3 commits we would like to act on. So we can kick off an interactive `rebase` by running:

```bash
git rebase --interactive HEAD~3
```

Executing this will take us into the editor, where we can choose which commits we’d like to squash. We can *mark* a commit for *squashing* by changing the `pick` into a `squash`.

When we save (using `:wq` if your editor is `vim`), we are taken into another editor interface where we can choose the message we’d like to use for our newly combined commit. In the example, we’ve chosen to use the second commit message, so we delete all the other ones. Once we’re happy with the commit message, we can save and exit out of the editor.


![squash](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1if2cn3d8n539wkxxg1o.gif)

### Fixup

There are some cases where we’d like to combine commits and keep the message of the original commit. A `fixup` is similar to a `squash`, but we just use the commit message of the original commit and discard the messages from any commits *marked* for `fixup`.

In the animated GIF example, below, we want to combine the last two commits with the first one. This first commit can be thought of as the *original* commit we want to fuse the other commits into. We also want to keep the first commit’s message and discard the other messages. We’ve decided to consolidate these commits because all these commits are related to the **model**, and tell a richer story when they are combined.

We have 7 commits in total and we want to see our full list of commits. We can kick off an interactive `rebase` by running:

```bash
git rebase --interactive HEAD~7
```

Executing this command will take us to the editor. Here, we rearrange the commit history by moving the last two commits up to the second and third positions, then we can *mark* them for `fixup` by changing the `pick` into a `fixup`.

We remember to save and exit out of the editor (`:wq`). Finally we force push our changes to Github by running:

```bash
git push --force-with-lease origin feature/potion-for-posterity
```


> 💡 I prefer to use the `--force-with-lease` flag when force pushing my changes because it is safer. It does not overwrite any work on the remote branch if more commits were added to the remote branch by another team member.


![Fixup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ut34ejpm6fs2sm7wfl85.gif)

Unlike a `squash`, we did not encounter the additional step to choose which commit message to use. We just used the message of the first (original) commit.

### Reword

Sometimes we can make typos in our commit messages. Other times we can realise, later on, that our commit message does not accurately describe the changes we are adding. In such cases, we can `reword` our commit message.

In the animated example GIF, below, we are rewording the last commit message. Because we only need 1 commit (the last commit), we can start an interactive rebase with the following command:

```bash
git rebase --interactive HEAD~1
```

Executing this command will bring us to the editor where we can *mark* the commit for rewording. It should be noted that all these actions have a shorter version. In this case, we can replace `pick` with `r`. This works the same as if we were to use `reword`, `r` is the short form of `reword`.

When we save (using `:wq` if your editor is `vim`), we are taken into another editor interface where we can change the message of our commit. Then we can save, exit and force push our changes to Github.

```bash
ggpush --force-with-lease
```


![Baby Yoda force](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8knzdf7t2ngvlcagll31.gif)



> 💡 I personally use `ggpush` as an alias for `git push --force-with-lease origin feature-branch`.

If I am not mistaken, you can now get this out-of-the-box with the latest versions of `git`.



![Reword](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jju7e193mpo0sogqtl80.gif)

### Edit

An `edit` will allow us to edit both the commit message and the contents of a commit. In the unfortunate situation where we have accidentally committed sensitive information like a password or a secret, we can use rebase to `edit` the commit.

In the animated GIF example, we realise that we’ve accidentally save secrets and password in our README. We’d like to pull up all our commits just as a sanity check. We can start an interactive rebase using this command:

```bash
git rebase --interactive HEAD~6
```

We are brought into the editor where we can see all our commits. We identify the commit that contains the secret password and mark it with `edit`. Once we save, the rebase process will be paused to give us a chance to go into our code and make the necessary changes.

We can remove the sensitive information, then `add` our changes and `amend` the previous commit.

```bash
git add .
git commit --amend
```

We can then ask the `rebase` process to continue by running:

```bash
git rebase --continue
```

Lastly, we remember to force push our changes to Github. Our secret password has been successfully removed from our commit history

```bash
ggpush --force-with-lease
```



![Edit](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8c8r9pvyitd865crq5kk.gif)

### Drop

Sometimes we realise that some commits just aren’t necessary. Perhaps we cherry-picked commits from another branch that have since become out-dated. Or we realise that we’ve added code that we don’t really need anymore. We can use a rebase to `drop` these commits.

In the animated GIF, below, we want to drop an unnecessary commit. Not only did we make a typo in the commit message, we realised that we no longer need these changes, altogether. We only want to act on the last commit, so we can start an interactive rebase using this command:

```bash
git rebase --interactive HEAD~1
```

We are transported into the editor where we can see the commit in question. We can mark it with `drop`. Once we save and exit the editor, that unnecessary commit no longer exists. We can force push our changes to Github.

```bash
ggpush --force-with-lease
```


![Drop](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/14t77ut06dmcls86299a.gif)

## Additional information

### A small tip about discarding changes

![Simba throw](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ichtz3hqvfqkerinn42s.gif)

Sometimes we’re just experimenting and trying stuff out, so we may not necessarily want to keep all the changes we made. In situations like that, we can discard them all by running:

```bash
git checkout .
git clean -df
```

### Some vim commands used in this guide

When rebasing, we spend a lot of time in the editor. If your default editor is `vim`, here are some of the commands we use frequently throughout this guide:

| Command | Description |
| --- | --- |
| i | To enter INSERT mode. This is the vim mode that allows us to type or insert text. |
| esc or control + c | Exits from INSERT mode and takes us back to NORMAL mode. NORMAL mode is where we can run commands or navigate around the editor with h, j, k, l and other navigational commands. |
| :wq | This command will save our work (w) and exit vim (q). Commands are executed in NORMAL mode and start with a colon (:) |
| :q! | Hard quit without saving anything. The exclamation point (!) is like a force flag. |


## Going further

If you'd like to continue learning about `git` and `rebase`, you can consider doing the following

- read up on the differences between merging and rebasing
- spin up a little repository and play around with the actions we covered, or
- spin up a demo repository and try all the other actions we did not cover

## Conclusion

In this tutorial we learnt how to use `git rebase --interactive` to `squash`, `fixup`, `reword`, `edit` and `drop` commits. We saw how these actions can improve our git workflow by helping keep our commit history clean and allowing us to tell a coherent story in our pull request.

Congratulations, you did great! Keep learning and keep coding. Bye for now, <3

## Resources

- [https://youtu.be/9QsFj4_1UAw](https://youtu.be/9QsFj4_1UAw)
- [https://twitter.com/divrhinodev](https://twitter.com/divrhinodev)
- [https://github.com/divrhino/](https://github.com/divrhino/)
- [https://git-scm.com/downloads](https://git-scm.com/downloads)
- [https://git-scm.com/docs/git-rebase](https://git-scm.com/docs/git-rebase)
- [https://docs.gitlab.com/ee/topics/git/git_rebase.html](https://docs.gitlab.com/ee/topics/git/git_rebase.html)
- [https://www.bryanbraun.com/2019/06/11/why-we-use-git-checkout-to-discard-file-changes/](https://www.bryanbraun.com/2019/06/11/why-we-use-git-checkout-to-discard-file-changes/)