How to effectively use git rebase --onto?
Tags: git, webdev, softwaredevelopment
Published: 2020-06-20 15:39:37

This post was originally published on [https://belev.dev/how-to-effectively-use-git-rebase-onto](https://belev.dev/how-to-effectively-use-git-rebase-onto)

We are going to get a step further and explore the `git rebase --onto` command. What is it, how to use it to fix Git branches after rebase, how to change a branch's base, and a bit more? We will learn those things by exploring real-world scenarios in which `git rebase --onto` is really useful.

Let's get started!

---

### Quick `git rebase` recap

Visiting the [docs](https://git-scm.com/docs/git-rebase), we will see the following explanation:

> Reapply commits on top of another base tip

The most important bit here is what _base_ means/refers to? This is the previous/parent commit. Let's see an example so that it can become clearer:

```
a--b--c--d--e  master
       \
        f--g--h  feature
```

`h`'s previous commit is `g`. This means that `h`'s base is `g`.

1. `g`'s base is `f`
2. `f`'s base is `c`
3. `d`'s base is `c` as well. This shows us that one commit can be the base of multiple commits.

The current `feature`'s base is `c`. By checking out the `feature` branch and executing `git rebase master`, we are going to change the `feature` branch's base to `e`.

```
a--b--c--d--e  master
             \
              f'--g'--h'  feature
```

Mentioned in a [previous blog post](https://belev.dev/git-merge-vs-rebase-to-keep-feature-branch-up-to-date) this is creating new commits and the Git history is changed. With the `'` after `f`, `g`, `h` we are showing that those are different commits. They contain the same set of changes though.

When we have only two branches which we want to keep in sync one on top of the other, `git rebase` is working perfectly. However, this is not enough when we need to make our changes based on the old base. Here `git rebase --onto` comes in help.

### What is `git rebase --onto`?

We can say that this is a more precise way of changing a parent branch. It accepts three arguments - `<new-base>`, `<old-base>`, `<end-inclusive>` where the third one is optional. This gives us complete control over what and where we want to rebase.

Let's take the previous example and achieve the same result using `--onto`.

```
a--b--c--d--e  master
       \
        f--g--h  feature
```

We want `feature` to be rebased off of `master`. So we know what our new base is, it is `master`. The old base is the base of our first commit in the branch which means it is going to be `c`. Now we know both the new and old base, and we can run the command: `git rebase --onto master c`.

This will lead us to:

```
a--b--c--d--e  master
             \
              f'--g'--h'  feature
```

The result is the same as using `git rebase master`. This shows us that they are doing the same thing. `git rebase` master is just a shorthand syntax for `git rebase --onto master <old-base>`.

### How to rebase with skipping not needed commits?

We have already seen the easiest case of using `--onto`. Let's take the previous example again but say that we don't want commit `f` to be present after the rebase because it is not relevant anymore.

```
a--b--c--d--e  master
       \
        f--g--h  feature
```

Running `git rebase --onto master f` gives us:

```
a--b--c--d--e  master
             \
              g--h  feature
```

> Be careful with the value provided for `<old-base>`. It is a common mistake to think that it is inclusive.

It is important to remember/understand that we are working with bases, not a certain commit. `g`'s base is `f` so when we say `<old-base>` is `f` this is going to be changed by the provided `<new-base>` and we are going to loose commit `f` this way.

### How to fix branch from a branch after rebase

Working with multiple people or in a large codebase, it is almost certain that at some point you will need to create a branch from a branch. For example, you need to start a new feature but it depends on another one so you need to start your branch from your previous branch. Or you have automation QAs which are creating a branch from your feature branch.

Let's see a visual representation of the problem:

```
a--b--c--d--e  master
       \
        f--g--h  feature-1
               \
                i--j--k--l  feature-2
```

Being in this situation, you would like to move the entire tree to be based on `master`. Meaning `feature-1` is going to be rebased on top of `master` and `feature-2` on top of `feature-1`.

Let's checkout `feature-1` and run `git rebase master`. This will lead to:

```
a--b--c--d--e  master
      |      \
      |       f'--g'--h'  feature-1
       \
        f--g--h--i--j--k--l  feature-2
```

If you have opened PR from `feature-2` to `feature-1` branch, you are going to see the commits from `feature-1` in your PR. There is no need to worry because as we can see from the visual representation above and the image below this is expected.

![Rebase feature-1 result](https://dev-to-uploads.s3.amazonaws.com/i/xxoe6h7ggwbm0eppp79s.png)

We can now checkout `feature-2` and run `git rebase --onto h' h`. This gives us:

```
a--b--c--d--e  master
             \
              f'--g'--h'  feature-1
                       \
                        i'--j'--k'--l'  feature-2
```

Keep in mind that sometimes simple `git rebase feature-1` is going to work if f/g/h is the same as f'/g'/h'. Git is smart enough and when we use rebase, it is going to/can remove the commits that are with the same set of changes. However, it is hard and annoying to check if everything is the same in another branch, and using `--onto` is the better option here because otherwise, it can lead to a branch with commits that don't belong in it.

You should give `--onto` a try. However, if you are not feeling ready or you don't want to use it and end up in the described situation above, there is a solution with an interactive rebase. Rebase `feature-2` from `feature-1`. Check if there are commits that don't belong in the branch and drop those commits by doing an interactive rebase. If you are not familiar with how to do that, have a look at the previous blog post [Some of the most used Git interactive rebase options](https://belev.dev/some-of-the-most-used-git-interactive-rebase-options).

This was a problem that we had for a few months. People were surprised by the changes that they have been seeing in open PRs. After all, you are not doing anything and all of a sudden you see some commits in your PR which don't belong there. It seems strange but hopefully, with this article, this will be cleared out and it won't be strange anymore.

### How to change Git branch's base?

From time to time we need to change the base of a branch. Two examples would be:

- we haven't started our branch from the right branch
- we started working on something from `master` but a colleague made some changes in his/her branch that are going to make our life easier. Then maybe we would like to change `master` as a base and use the colleague's branch changes.

Let's have a visual representation for a better understanding:

```
a--b--c--d--e  master
             \
              f--g  feature-1
                  \
                   h--i  feature-2
```

We started our branch `feature-2` from `feature-1` but we did that by mistake. The features turned out to be not connected at all and we would like our `feature-2` branch to start from `master`.

It is not a surprise that we are going to use `--onto`. So we have `git rebase --onto <new-base> <old-base>`. Our old base is `feature-1`, we started our branch from there. Our new base should be `master` because we want our branch to start from it. We are going to execute `git rebase --onto master feature-1`.

This gives us:

```
a--b--c--d--e  master
            | \
            |  f--g  feature-1
             \
              h--i  feature-2
```

We are using [Git flow](https://nvie.com/posts/a-successful-git-branching-model/) at work and changing the base of a branch comes up from time to. In our case, it is mostly when the hotfix branch is not created from the proper branch. For example, the hotfix branch is opened, but we started our branch with a fix from the release branch. Then we would need to change the base from release to hotfix.

### How to use the third argument of `git rebase --onto`?

`git rebase --onto` takes up a third argument which is used as an end stop inclusive.

Let's have the following example:

```
a--b--c--d--e  master
       \
        f--g--h--i--j--k--l  feature
```

We want to rebase `feature` from `master` but take only certain commits - from `h` to `j`. We have seen how to skip redundant commits by using `--onto`. If we only want to do that, we would need to execute `git rebase --onto master g`.

Now we want a specific commit for the last one. The third argument of `--onto` is exactly what we want. So we would need only to say which is the last commit.

> Note: it is taken inclusively.

Run `git rebase --onto master g j` and this gives us:

```
a--b--c--d--e  master
       \
        h--i--j  feature
```

### Conclusion

We dived deeper into `git rebase --onto`. It is one of the more advanced commands with which we can easily solve certain Git problems with our branches. Even if we don't know about it, we can solve our problems with a combination of commands - normal and interactive rebase, cherry-pick, .etc. As developers, we should try to automate our work as most as possible, so using less number of commands is a sufficient condition to learn about `--onto`.

We covered a lot of things so let's quickly summarize:

- A quick recap about `git rebase`, what it _base_ and what does rebase do
- What does `git rebase --onto` give us by using it
- How to use `--onto` to rebase a branch and skip not needed commits
- How to fix our branches after rebase when we have a deeper branch structure - branch from a branch from a branch
- How to change Git branch's base and why would it be useful
- Use the complete form of `git rebase --onto` with 3 arguments which allows us to take only certain commits from start to end provided

Thank you for reading this to the end. I hope you enjoyed it and learned something new. If so, please follow me on [Twitter](https://twitter.com/BelevMartin) where I will share other tips, new articles, and things I learn. If you would like to learn more, have a chat about software development, or give me some feedback, don't be shy and drop me a DM.