git deep dive part 6: Rebase
Tags: git
Published: 2020-05-08 05:42:57

---
title: git deep dive part 6: Rebase
published: true
description: git deep dive
tags: git
---

In the [previous article](https://dev.to/kenakamu/git-deep-dive-part-5-merge-branches-3h6d), I explain how to merge branches. In this article, I explain how git [rebase](https://git-scm.com/docs/git-rebase) works.

# What is rebase

Rebase is restart branching from certain point. For example, If I create dev branch from master right now, it looks like below.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4m8g6al0vv01ol5m1wkt.png)

Then I create commits in dev branch.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/47gi7orumfb2zxsc685y.png)

But someone introduced change in master.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/wg6h6rgvmzmui5qfrv79.png)

Now I want to **rebase** from the latest commit of the master.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lhruiyoq96mg6ngfdq1n.png)

The reason I put ``???????`` to commit id is that we already know the id should be different now as commit file contains different data.

### Check the current log and prepare for test

At the moment, my git log looks like below.

```shell
gitDeepDive> git log --oneline --graph
*   a3d0879 (HEAD -> master) Merge branch 'dev'
|\
| * 867d90c (dev) update hello.txt
| * 367c2d0 Update news
* | c36490a update docs
|/
* 2adbcac (test) Add doc folder and files
* 16f1fa8 commit hello.txt
```
Let's reset master branch to previous commit.
```shell
gitDeepDive> git switch master
Already on 'master'
gitDeepDive> git reset --hard c36490a
HEAD is now at c36490a update docs
gitDeepDive> git log --oneline --graph --all
* c36490a (HEAD -> master) update docs
| * 867d90c (dev) update hello.txt
| * 367c2d0 Update news
|/
* 2adbcac (test) Add doc folder and files
* 16f1fa8 commit hello.txt
```
# Rebase

I rebase the dev branch. So I need to go to dev branch first.

```shell
git switch dev
```
Then I run ``git rebase`` command. As I rebase from the last commit of master, I can do ``git rebase master`` but I explicitly specify the commit id instead to illustrate that we can rebase from certain commit.

I see conflict error. Let's a closer look now.

```shell
gitDeepDive> git rebase c36490a
error: could not apply 367c2d0... Update news
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 367c2d0... Update news
Auto-merging docs/news.txt
CONFLICT (content): Merge conflict in docs/news.txt
```

It says "could not apply 367c2d0... Update news". This means git try to apply the changes of commit 367c2d0 to create new commit. Let's resolve the conflict. I simply take both changes here.

```shell
git mergetool
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cjsgben1fu2hw9w190ba.png)

Once I resolved news.txt conflict, continue the rebase. I can edit the commit message. I simply accept default message and completed but I have another conflict.

```shell
gitDeepDive> git rebase --continue                                                        [detached HEAD 8bd3445] Update news
 1 file changed, 1 insertion(+)
error: could not apply 867d90c... update hello.txt
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 867d90c... update hello.txt
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
```

It says "could not apply 867d90c... update hello.txt". git apply next commit 867d90c on top of current commit. Simply resolve the conflict and continue.

```shell
gitDeepDive> git rebase --continue                                                        [detached HEAD 43df8ff] update hello.txt
 1 file changed, 2 insertions(+)
Successfully rebased and updated refs/heads/dev.
```
Let's check the log now.

```shell
gitDeepDive> git log --oneline --graph --all
* 43df8ff (HEAD -> dev) update hello.txt
* 8bd3445 Update news
* c36490a (master) update docs
* 2adbcac (test) Add doc folder and files
* 16f1fa8 commit hello.txt
```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/wgukjx2m8cxycr86sblt.png)

So this is exactly what I wanted.

# How to redo the rebase

I sometimes need to "redo" the rebase if I mistake on conflict resolution. As git doesn't delete objects items immediately, I still can go back to original state. Simply reset to original commit id will do the undo.

```shell
gitDeepDive> git reset 867d90c
Unstaged changes after reset:
M       docs/news.txt
M       hello.txt
gitDeepDive> git log --oneline --graph --all
* c36490a (master) update docs                                                            | * 867d90c (HEAD -> dev) update hello.txt
| * 367c2d0 Update news
|/
* 2adbcac (test) Add doc folder and files
* 16f1fa8 commit hello.txt
```
As you can see, it simply updates the commit id information in .\.git\refs\heads\dev.

From here, I can do rebase again. If you really want to remove any orphaned objects, do ``git prune`` afterward but I usually don't.

# Why people confuse rebase

This time, it was easy as there are only two commits to apply and each commit has different conflict. However, in real world scenario, I may need to keep resolving the same file again and again, as I keep updating the same file for each commit.

The reason why git have to apply each commit one by one, rather than apply the latest commit is to keep the history.

# Squash commits

If you don't need all the commit history, you can "squash" commits. By squashing, I can create another commit which has all the changes in certain ranges.

I will create new commit which contains both 367c2d0 and 867d90c.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2ss973nu5ayw3q37tf0m.png)

Make sure to reset to commit 867d90c on dev branch.

```shell
gitDeepDive> git switch dev
Switched to branch 'dev'
gitDeepDive> git reset --hard 867d90c
HEAD is now at 867d90c update hello.txt
gitDeepDive> git log --oneline --graph --all
* c36490a (master) update docs
| * 867d90c (HEAD -> dev) update hello.txt
| * 367c2d0 Update news
|/
* 2adbcac (test) Add doc folder and files
* 16f1fa8 commit hello.txt
gitDeepDive> 
```

Now run rebase with ``-i`` parameter and specify 2adbcac as rebase commit id.

```
git rebase -i 2adbcac 
```
git opens an editor to let me decide how I want to treat each commit. I have many options here.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/r4t5zycv2q8hk4ptea0t.png)

I mark "squash" for the commit 867d90c so that I can combine this into 367c2d0.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5tjux3rxnqx4g92fh503.png)

Once I save the file, git open another editor so that I can edit commit message. I just accept the default one. Once rebase completed, check the log.

```shell
gitDeepDive> git log --oneline --graph --all
* 6a695b7 (HEAD -> dev) Update news                                                       | * c36490a (master) update docs                                                          |/
* 2adbcac (test) Add doc folder and files
* 16f1fa8 commit hello.txt
```
As I explained new commit is created. When I dig into commit file, I can see newly created commit and original commit reference same blob. I only check hello.txt here but you can check news.txt and article.txt

```shell
gitDeepDive> git cat-file -p edbfe82
040000 tree 2baf027b74c551817c2a5ef6a3472ccc8e99738c    docs
100644 blob 7a218e826670e77d05c1c244b514a7f449056752    hello.txt
gitDeepDive> git cat-file -p 867d90c
tree edbfe822158fb3be005aca22897a0395fa2c9252
parent 367c2d000be0ffbb640252384c820ce472fe32a4
author Kenichiro Nakamura <kenakamu@microsoft.com> 1588842488 +0900
committer Kenichiro Nakamura <kenakamu@microsoft.com> 1588842488 +0900

update hello.txt
gitDeepDive> git cat-file -p edbfe82
040000 tree 2baf027b74c551817c2a5ef6a3472ccc8e99738c    docs
100644 blob 7a218e826670e77d05c1c244b514a7f449056752    hello.txt
```
Now my commit chain looks like this. 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vp4grrf5iypubwsmsjxq.png)

Of course we can do rebase from c36490a rather than 2adbcac. Try it by yourself to see how it works.

# Summary

Rebase is nothing special from git point of view, but as it may apply multiple commits one by one, it maybe a bit confusing. I explain cherry-pick in the next article.

[Go to next article](https://dev.to/kenakamu/git-deep-dive-part-7-cherry-pick-565h)