# Git Rebase IRL

> This is an old blog post I wrote back in 2018. I pick it up from the wayback machine and fixed some typo & re-wording some paragraphs. 

## Intro

Many people might already known that there is a command call `git rebase`, but most of people don't know how to use it right.

In this article, we will see some common cases of `git rebase` that you can use to make your git history  clearer.

> P.S. the man page of `git-rebase` also provided a very clear document. if you want to learn more about `git rebase,` just read it :)

## When will I need it?

There is some common cases:

1. To split one commit to two or more.
2. To rename/delete one or more commits.
3. To merge some commits into one.
4. To append bunch of commits into another branch.

## Split one commit to two or more

Assuming we have following commits:

```text
ad99c7e initial commit
fb89181 implement a feature and fix coding style
7c189c8 remove bugs
```

You might want to split your second commit into two commits, so every commit only do one thing, that also keeps git history atomic.

First, we need to start a rebase, use `git rebase -I ad99c7e`. -i means **i**nteractively rebasing.
The git rebase will use `ad99c7e` as a new base, think like rebase is to create a new branch. Then we works on that branch. Once we have done our works, it destroy the original branch and use the new one to replace it. As a result, the hashes starting from `ad99c7e` will be changed.

```bash
$ git rebase -I ad99c7e
```

Then, `git` will open the editor, showing up following content:

```text
pick fb89181 implement a feature and fix coding style
pick 7c189c8 remove bugs
```

This file is called `git-rebase-todo`. We can now edit it to tell git how we want to do during rebase.

To split a commit, we need to edit this file. Let's change the `pick` command in the first line to `edit`, then save the file and exit your text editor:

```text
edit fb89181 implement a feature and fix coding style
pick 7c189c8 remove bugs
```

Now, git will stop at `fb89181` and waiting for you. Try use git status to see what happened.

```bash
$ git status
interactive rebase in progress; onto fb89191
You are currently editing a commit while rebasing branch 'main' on 'fb89191'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)
```

To edit this commit, first we need to discard this commit by invoking `git reset --soft HEAD`.
You can now use the combination of `git add`, `git reset`, and `git commit` to create more commit.

Once you have done, use `git rebase --continue` to continue.

You might notice that starting from `ad99c7e`, the hashes are different now, just like I explained above:

```text
ad99c7e initial commit
fb89181 implement a feature 
4434f2c fix coding style
45fdb3a remove bugs
```

## Merge some commits into one

Assuming we have following commits:

```text
fb89181 implement a feature 
4434f2c fix coding style
45fdb3a remove bugs
```

What if I want to merge `45fdb3a` into `fb89181` to hide my mistake?

Let's starting rebase by `git rebase -I ad99c7e` like we have done earlier:

```text
pick fb89181 implement a feature 
pick 4434f2c fix coding style
pick 45fdb3a remove bugs
```

This time, we need to use the `squash` command. `squash` means that we want to merge that commit into previous one:

```text
pick fb89181 implement a feature 
squash 45fdb3a remove bugs
pick 4434f2c change the style
```

By doing this, `45fdb3a` will be merged into `fb89191`. You might noticed that I changed the order of the commits. That's okay to do it. You can always changing it's order when rebasing, just be careful of conflicts.
Save it and exit the editor. Then git will prompt the text editor again, to asking you the commit message the merged commit should have:

```text
# This is a combination of 2 commits.
# This is the 1st commit message:

implement a feature 

# This is the commit message #2:

remove bugs
```

Let's hiding the second message. save and exit the text editor. and that's all! we have successfully merged that two commits.

## To rename/delete one or more commits.

Let's take following rebase todo for example:

```text
pick fb89181 implement a feature 
pick 4434f2c fix coding style
pick 45fdb3a remove bugs
```

To rename a commit, just use reword.
To delete a commit, just delete that line ;)

## Append a bunch of commits into another branch

That it a very common case if you are using some git workflow. When using gitflow workflow, for example, to fix a bug, there might be a branch `bugfix/fix-some-bugs` that borned from main. Then, the main keeps adding commits, `bugfix/fix-some-bugs` will have some commits too. Thus, there will be several commits ahead and also several commits behide main.

If we want to move the commits that only on `bugfix/fix-some-bugs` but now on main before we merge it to main, simply do git rebase main on `bugfix/fix-some-bugs`.

That's very useful if you want to keep your history graph more clear. Keeping rebase to main can also avoiding make that branch too far away from main, because it might cause some conflict.

## Append bunch of commits into another branch - Part 2

At first, we made a branch `branchA` after `A`, then we committed `B` on `main`.

Continue, we made commit `C` and `D` on `branchA`. Then, we want to do a new feature based on `branchA`'s work, so wo created new branch `branchB` just after `D`, then committed `E` on that branch.
To keep sync with main, so we did a `git rebase` on `branchA`.
Considering following diagram:

```text
           /---\   /---\
  ---------| A |---| B |---
      main \---/ | \---/  |         /---\   /---\
                 |        \---------| C |---| D |
                 |          branchA \---/   \---/
                 |         
                 |        /----\   /----\   /---\
                 \--------| C' |---| D' |---| E |
                  branchB \----/   \----/   \---/
```

What if we want to keep sync `branchB` with `branchA`?
Since `branchA` has been rebased, the hashed of `C`, `D` will be different on `branchB`'s, we marked it as `C'` and `D'`.
If we did `git rebase branchA` on `branchB`, the `C'` and `D'` will also be append to `branchA`.

For this cases, we need to use git rebase onto:

```bash
$ # on branch `branchB`
$ git rebase --onto branchA <D''s hash> HEAD
```

This way, only `E` will be rebase onto `branchA`.


