Saturday, July 1, 2023

git rebase

   

Currently, I am curious to find out what Git will do for us behind the scene if we run the 'git rebase' commands. Below are the logs coming from my experiment. 


Reference: Git Book 


Introduction


We should discuss the difference between the 'fast-forward merging' and the 'three-way merging' before introducing the 'git rebase' command.


*Fast-forward merging

If the merging branch (hotfix)  has all commits from the current branch (master), Git will not create a new merging commit. Instead, Git will move the 'master' branch pointer to the latest commit of the 'hotfix' branch because it is a linear relationship.


*Three way merging

When the current branch (master) contains commits that the merging branch (hotfix) doesn't have, Git will create a new merging commit that points two the last commits from those branches without modifying the existing branches. It is a tree (with a cycle) relationship.


Therefore, if we don't want to make Git to create a new merging commit, we need to try to make the git tree structure to be linear.  The 'git rebase' command can help! 



*Rebasing

The git rebase command is basically an automated cherry-pick. (Refer to git book)

Git will rewrite the commits from the current branch (hotfix) and put them on top of the target branch (master). It keeps the git tree structure to be linear.


Take the following as an example, both branches 'master' and 'hotfix' have commits which other branches don't have.


Use the 'git rebase' command to make the git tree structure to be linear.

The C4 commit was rewritten to C4' by Git.



Use the 'git merge' command to merge the hotfix branch to the master branch.

Since it is the linear git commit history, the master branch pointer will move to the last commit of the hotfix branch.



Preparation


Create a brand new folder and run the 'git init' command.


Step 1:


Run the following commands to create c1, c2, and c3 commits.


    $ echo 'c1' > test.txt
$ git add .
$ git commit -m 'c1'
[master (root-commit) 09c1658] c1 1 file changed, 1 insertion(+) create mode 100644 test.txt

    $ echo 'c2' > test.txt
$ git add .
$ git commit -m 'c2'
[master f3333e2] c2 1 file changed, 1 insertion(+), 1 deletion(-)

    $ echo 'c3' > test.txt
$ git add .
$ git commit -m 'c3'
[master c9368a3] c3 1 file changed, 1 insertion(+), 1 deletion(-)


Check the git status.


    $ git status
On branch master nothing to commit, working tree clean


Check the .git tree structure.


    $ tree .git/
.git/ ├── COMMIT_EDITMSG ├── HEAD ├── branches ├── config ├── description ├── hooks │   ├── applypatch-msg.sample │   ├── commit-msg.sample │   ├── fsmonitor-watchman.sample │   ├── post-update.sample │   ├── pre-applypatch.sample │   ├── pre-commit.sample │   ├── pre-merge-commit.sample │   ├── pre-push.sample │   ├── pre-rebase.sample │   ├── pre-receive.sample │   ├── prepare-commit-msg.sample │   ├── push-to-checkout.sample │   └── update.sample ├── index ├── info │   └── exclude ├── logs │   ├── HEAD │   └── refs │   └── heads
│   └── master ├── objects     │   ├── 07 
    │   │   └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
    │   ├── 09 
    │   │   └── c165883d0a25f729d9a7c69fa02efd6c87c618 (git commit object for c1)
    │   ├── 16 
    │   │   └── f9ec009e5568c435f473ba3a1df732d49ce8c3  (git blob object for c2)
    │   ├── ae 
    │   │   └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
    │   ├── b5 
    │   │   └── fab02de1c1f88003afeb7e98cf84a141bff039 (git tree object for c3)
    │   ├── c0 
    │   │   └── b072a3d368667939677b9315360631a100ecde (git tree object for c2)
    │   ├── c9 
    │   │   └── 368a318142494f31acd9c83a9763ed9f395a3b (git commit object for c3)
    │   ├── f1 
    │   │   └── 8c7b882bf09e79d96b692c31450cf59344fbf4 (git tree object for c1)
    │   ├── f3 
    │   │   └── 333e2df7930fa29ef8fb7ee177388f4a02b039 (git commit object for c2)
│   ├── info │   └── pack └── refs ├── heads
│   └── master └── tags 21 directories, 31 files


Check more info.


    $ cat .git/HEAD
ref: refs/heads/master

$ cat .git/refs/heads/master
c9368a318142494f31acd9c83a9763ed9f395a3b

$ git ls-files -s
100644 0771aea884dd394a7b12783d049f05b5599f41a4 0 test.txt

$ git log --oneline --decorate --graph --all
* c9368a3 (HEAD -> master) c3 * f3333e2 c2 * 09c1658 c1


Step 2:


Create and checkout a new branch 'hotfix'.


    $ git checkout -b hotfix
Switched to a new branch 'hotfix'


Check the git status.


    $ git status
On branch hotfix nothing to commit, working tree clean


Check the .git tree structure.


    $ tree .git/
.git/ ├── COMMIT_EDITMSG ├── HEAD ├── branches ├── config ├── description ├── hooks │   ├── applypatch-msg.sample │   ├── commit-msg.sample │   ├── fsmonitor-watchman.sample │   ├── post-update.sample │   ├── pre-applypatch.sample │   ├── pre-commit.sample │   ├── pre-merge-commit.sample │   ├── pre-push.sample │   ├── pre-rebase.sample │   ├── pre-receive.sample │   ├── prepare-commit-msg.sample │   ├── push-to-checkout.sample │   └── update.sample ├── index ├── info │   └── exclude ├── logs │   ├── HEAD │   └── refs │   └── heads
│   ├── hotfix
│   └── master ├── objects     │   ├── 07 
    │   │   └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
    │   ├── 09 
    │   │   └── c165883d0a25f729d9a7c69fa02efd6c87c618 (git commit object for c1)
    │   ├── 16 
    │   │   └── f9ec009e5568c435f473ba3a1df732d49ce8c3  (git blob object for c2)
    │   ├── ae 
    │   │   └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
    │   ├── b5 
    │   │   └── fab02de1c1f88003afeb7e98cf84a141bff039 (git tree object for c3)
    │   ├── c0 
    │   │   └── b072a3d368667939677b9315360631a100ecde (git tree object for c2)
    │   ├── c9 
    │   │   └── 368a318142494f31acd9c83a9763ed9f395a3b (git commit object for c3)
    │   ├── f1 
    │   │   └── 8c7b882bf09e79d96b692c31450cf59344fbf4 (git tree object for c1)
    │   ├── f3 
    │   │   └── 333e2df7930fa29ef8fb7ee177388f4a02b039 (git commit object for c2)
│   ├── info │   └── pack └── refs ├── heads
│   ├── hotfix
│   └── master └── tags 21 directories, 33 files


Check more info.


    $ cat .git/HEAD
ref: refs/heads/hotfix

$ cat .git/refs/heads/master
c9368a318142494f31acd9c83a9763ed9f395a3b

$ cat .git/refs/heads/hotfix
c9368a318142494f31acd9c83a9763ed9f395a3b

$ git ls-files -s
100644 0771aea884dd394a7b12783d049f05b5599f41a4 0 test.txt

$ git log --oneline --decorate --graph --all
* c9368a3 (HEAD -> hotfix, master) c3
* f3333e2 c2 * 09c1658 c1

Both the hotfix branch and the master branch point to the c3 commit.


Step 3:


Create a new commit 'c4' on the hotfix branch.


    $ echo 'c4' > test.txt
    $ git add .
    $ git commit -m 'c4'
[hotfix 85ad7e6] c4
 1 file changed, 1 insertion(+), 1 deletion(-)


Check the git status.


    $ git status
On branch hotfix nothing to commit, working tree clean


Check the .git tree structure.


    $ tree .git/
.git/ ├── COMMIT_EDITMSG ├── HEAD ├── branches ├── config ├── description ├── hooks │   ├── applypatch-msg.sample │   ├── commit-msg.sample │   ├── fsmonitor-watchman.sample │   ├── post-update.sample │   ├── pre-applypatch.sample │   ├── pre-commit.sample │   ├── pre-merge-commit.sample │   ├── pre-push.sample │   ├── pre-rebase.sample │   ├── pre-receive.sample │   ├── prepare-commit-msg.sample │   ├── push-to-checkout.sample │   └── update.sample ├── index ├── info │   └── exclude ├── logs │   ├── HEAD │   └── refs │   └── heads
│   ├── hotfix
│   └── master ├── objects     │   ├── 07 
    │   │   └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
    │   ├── 09 
    │   │   └── c165883d0a25f729d9a7c69fa02efd6c87c618 (git commit object for c1)
    │   ├── 16 
    │   │   └── f9ec009e5568c435f473ba3a1df732d49ce8c3  (git blob object for c2)
    │   ├── 3d
    │   │   └── 3095743da8025788f376069d9c07b9d867d5c1 (git tree object for c4)
    │   ├── 85
    │   │   └── ad7e6b045bfc28bf4546a86f1e9b0b669fc034 (git commit object for c4)
    │   ├── a1
    │   │   └── 03f673dd1b7c5727aa0ae0100419adc50e1b76 (git blob object for c4)
    │   ├── ae 
    │   │   └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
    │   ├── b5 
    │   │   └── fab02de1c1f88003afeb7e98cf84a141bff039 (git tree object for c3)
    │   ├── c0 
    │   │   └── b072a3d368667939677b9315360631a100ecde (git tree object for c2)
    │   ├── c9 
    │   │   └── 368a318142494f31acd9c83a9763ed9f395a3b (git commit object for c3)
    │   ├── f1 
    │   │   └── 8c7b882bf09e79d96b692c31450cf59344fbf4 (git tree object for c1)
    │   ├── f3 
    │   │   └── 333e2df7930fa29ef8fb7ee177388f4a02b039 (git commit object for c2)
│   ├── info │   └── pack └── refs ├── heads
│   ├── hotfix
│   └── master └── tags 24 directories, 36 files


Check more info.


    $ cat .git/HEAD
ref: refs/heads/hotfix

$ cat .git/refs/heads/master
c9368a318142494f31acd9c83a9763ed9f395a3b

$ cat .git/refs/heads/hotfix
85ad7e6b045bfc28bf4546a86f1e9b0b669fc034

$ git ls-files -s
100644 a103f673dd1b7c5727aa0ae0100419adc50e1b76 0 test.txt

$ git log --oneline --decorate --graph --all
* 85ad7e6 (HEAD -> hotfix) c4 * c9368a3 (master) c3 
* f3333e2 c2 * 09c1658 c1

Now the hotfix branch has one more commit ahead the master branch.


Step 4:


Switch to the master branch. 

Create a commit c5 (make sure to avoid conflict).


    $ git checkout master
Switched to branch 'master'

    $ echo 'c5' > c5.txt
$ git add .
$ git commit -m 'c5'
[master 25e3699] c5 1 file changed, 1 insertion(+) create mode 100644 c5.txt


Check the git status.


    $ git status
On branch master nothing to commit, working tree clean


Check the .git tree structure.


    $ tree .git/
.git/ ├── COMMIT_EDITMSG ├── HEAD ├── branches ├── config ├── description ├── hooks │   ├── applypatch-msg.sample │   ├── commit-msg.sample │   ├── fsmonitor-watchman.sample │   ├── post-update.sample │   ├── pre-applypatch.sample │   ├── pre-commit.sample │   ├── pre-merge-commit.sample │   ├── pre-push.sample │   ├── pre-rebase.sample │   ├── pre-receive.sample │   ├── prepare-commit-msg.sample │   ├── push-to-checkout.sample │   └── update.sample ├── index ├── info │   └── exclude ├── logs │   ├── HEAD │   └── refs │   └── heads
│   ├── hotfix
│   └── master ├── objects     │   ├── 07 
    │   │   └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
    │   ├── 09 
    │   │   └── c165883d0a25f729d9a7c69fa02efd6c87c618 (git commit object for c1)
    │   ├── 16 
    │   │   └── f9ec009e5568c435f473ba3a1df732d49ce8c3  (git blob object for c2)
    │   ├── 25
    │   │   └── e3699293eeeb9b6f4e33e5873171b6aba01e80 (git commit object for c5)
    │   ├── 3d
    │   │   └── 3095743da8025788f376069d9c07b9d867d5c1 (git tree object for c4)
    │   ├── 85
    │   │   └── ad7e6b045bfc28bf4546a86f1e9b0b669fc034 (git commit object for c4)
    │   ├── 9a
    │   │   └── ed71345f7f6af10cf15445dcc767b5f3433ae7 (git tree object for c5)
    │   ├── a1
    │   │   └── 03f673dd1b7c5727aa0ae0100419adc50e1b76 (git blob object for c4)
    │   ├── ae 
    │   │   └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
    │   ├── b5 
    │   │   └── fab02de1c1f88003afeb7e98cf84a141bff039 (git tree object for c3)
    │   ├── c0 
    │   │   └── b072a3d368667939677b9315360631a100ecde (git tree object for c2)
    │   ├── c3
    │   │   └── 6357109ce20af8f90df3c0dd0784e89408d707 (git blob object for c5)
    │   ├── c9 
    │   │   └── 368a318142494f31acd9c83a9763ed9f395a3b (git commit object for c3)
    │   ├── f1 
    │   │   └── 8c7b882bf09e79d96b692c31450cf59344fbf4 (git tree object for c1)
    │   ├── f3 
    │   │   └── 333e2df7930fa29ef8fb7ee177388f4a02b039 (git commit object for c2)
│   ├── info │   └── pack └── refs ├── heads
│   ├── hotfix
│   └── master └── tags 27 directories, 39 files


Check more info.


    $ cat .git/HEAD
ref: refs/heads/master

$ cat .git/refs/heads/master
25e3699293eeeb9b6f4e33e5873171b6aba01e80

$ cat .git/refs/heads/hotfix
85ad7e6b045bfc28bf4546a86f1e9b0b669fc034

$ git ls-files -s
100644 c36357109ce20af8f90df3c0dd0784e89408d707 0 c5.txt 100644 0771aea884dd394a7b12783d049f05b5599f41a4 0 test.txt

$ git log --oneline --decorate --graph --all
* 25e3699 (HEAD -> master) c5 | * 85ad7e6 (hotfix) c4 |/ * c9368a3 c3
* f3333e2 c2 * 09c1658 c1

Now the commit history is diverged.


Step 5:


Switch to the hotfix branch.

Run the 'git rebase' command to make the git commit history to be linear.


$ git checkout hotfix
Switched to branch 'hotfix'

    $ git rebase master
Successfully rebased and updated refs/heads/hotfix.


Check the git status.


    $ git status
On branch hotfix nothing to commit, working tree clean


Check the .git tree structure.


    $ tree .git/
.git/ ├── AUTO_MERGE (a new file)
├── COMMIT_EDITMSG ├── HEAD 
├── ORIG_HEAD (a new file)
├── branches ├── config ├── description ├── hooks │   ├── applypatch-msg.sample │   ├── commit-msg.sample │   ├── fsmonitor-watchman.sample │   ├── post-update.sample │   ├── pre-applypatch.sample │   ├── pre-commit.sample │   ├── pre-merge-commit.sample │   ├── pre-push.sample │   ├── pre-rebase.sample │   ├── pre-receive.sample │   ├── prepare-commit-msg.sample │   ├── push-to-checkout.sample │   └── update.sample ├── index ├── info │   └── exclude ├── logs │   ├── HEAD │   └── refs │   └── heads
│   ├── hotfix
│   └── master ├── objects     │   ├── 07 
    │   │   └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
    │   ├── 09 
    │   │   └── c165883d0a25f729d9a7c69fa02efd6c87c618 (git commit object for c1)
    │   ├── 16 
    │   │   └── f9ec009e5568c435f473ba3a1df732d49ce8c3  (git blob object for c2)
    │   ├── 25
    │   │   └── e3699293eeeb9b6f4e33e5873171b6aba01e80 (git commit object for c5)
    │   ├── 3d
    │   │   └── 3095743da8025788f376069d9c07b9d867d5c1 (git tree object for c4)
    │   ├── 60
    │   │   └── 0188dcf6aa195ed51c91212610f0ab42b29f43 (git tree object for c4')             <= NEW
    │   ├── 85
    │   │   └── ad7e6b045bfc28bf4546a86f1e9b0b669fc034 (git commit object for c4)
    │   │   └── ec49582eb31885ee195c2dd5a9df9c7baa9fd9  (git commit object for c4')      <= NEW
    │   ├── 9a
    │   │   └── ed71345f7f6af10cf15445dcc767b5f3433ae7 (git tree object for c5)
    │   ├── a1
    │   │   └── 03f673dd1b7c5727aa0ae0100419adc50e1b76 (git blob object for c4)
    │   ├── ae 
    │   │   └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
    │   ├── b5 
    │   │   └── fab02de1c1f88003afeb7e98cf84a141bff039 (git tree object for c3)
    │   ├── c0 
    │   │   └── b072a3d368667939677b9315360631a100ecde (git tree object for c2)
    │   ├── c3
    │   │   └── 6357109ce20af8f90df3c0dd0784e89408d707 (git blob object for c5)
    │   ├── c9 
    │   │   └── 368a318142494f31acd9c83a9763ed9f395a3b (git commit object for c3)
    │   ├── f1 
    │   │   └── 8c7b882bf09e79d96b692c31450cf59344fbf4 (git tree object for c1)
    │   ├── f3 
    │   │   └── 333e2df7930fa29ef8fb7ee177388f4a02b039 (git commit object for c2)
│   ├── info │   └── pack └── refs ├── heads
│   ├── hotfix
│   └── master └── tags 28 directories, 43 files


Check more info.


    $ cat .git/HEAD
ref: refs/heads/hotfix

    $ cat .git/ORIG_HEAD
85ad7e6b045bfc28bf4546a86f1e9b0b669fc034

$ cat .git/refs/heads/master
25e3699293eeeb9b6f4e33e5873171b6aba01e80

$ cat .git/refs/heads/hotfix
85ec49582eb31885ee195c2dd5a9df9c7baa9fd9

$ cat .git/AUTO_MERGE
600188dcf6aa195ed51c91212610f0ab42b29f43

$ git ls-files -s
100644 c36357109ce20af8f90df3c0dd0784e89408d707 0 c5.txt 100644 a103f673dd1b7c5727aa0ae0100419adc50e1b76 0 test.txt

$ git log --oneline --decorate --graph --all
* 85ec495 (HEAD -> hotfix) c4 * 25e3699 (master) c5
* c9368a3 c3
* f3333e2 c2 * 09c1658 c1

Now the git commit history is linear.


Step 6:


Switch to the master branch.

Run the 'git merge' command to merge the work from the hotfix branch.


$ git checkout master
Switched to branch 'master'

    $ git merge hotfix
Updating 25e3699..85ec495
Fast-forward test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)

It is the fast-forward merging. So there is no new merging commit.


Check the git status.


    $ git status
On branch master nothing to commit, working tree clean


Check the .git tree structure.


    $ tree .git/
.git/ ├── AUTO_MERGE (removed)
├── COMMIT_EDITMSG ├── HEAD 
├── ORIG_HEAD
├── branches ├── config ├── description ├── hooks │   ├── applypatch-msg.sample │   ├── commit-msg.sample │   ├── fsmonitor-watchman.sample │   ├── post-update.sample │   ├── pre-applypatch.sample │   ├── pre-commit.sample │   ├── pre-merge-commit.sample │   ├── pre-push.sample │   ├── pre-rebase.sample │   ├── pre-receive.sample │   ├── prepare-commit-msg.sample │   ├── push-to-checkout.sample │   └── update.sample ├── index ├── info │   └── exclude ├── logs │   ├── HEAD │   └── refs │   └── heads
│   ├── hotfix
│   └── master ├── objects     │   ├── 07 
    │   │   └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
    │   ├── 09 
    │   │   └── c165883d0a25f729d9a7c69fa02efd6c87c618 (git commit object for c1)
    │   ├── 16 
    │   │   └── f9ec009e5568c435f473ba3a1df732d49ce8c3  (git blob object for c2)
    │   ├── 25
    │   │   └── e3699293eeeb9b6f4e33e5873171b6aba01e80 (git commit object for c5)
    │   ├── 3d
    │   │   └── 3095743da8025788f376069d9c07b9d867d5c1 (git tree object for c4)
    │   ├── 60
    │   │   └── 0188dcf6aa195ed51c91212610f0ab42b29f43 (git tree object for c4')
    │   ├── 85
    │   │   └── ad7e6b045bfc28bf4546a86f1e9b0b669fc034 (git commit object for c4)
    │   │   └── ec49582eb31885ee195c2dd5a9df9c7baa9fd9  (git commit object for c4')
    │   ├── 9a
    │   │   └── ed71345f7f6af10cf15445dcc767b5f3433ae7 (git tree object for c5)
    │   ├── a1
    │   │   └── 03f673dd1b7c5727aa0ae0100419adc50e1b76 (git blob object for c4)
    │   ├── ae 
    │   │   └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
    │   ├── b5 
    │   │   └── fab02de1c1f88003afeb7e98cf84a141bff039 (git tree object for c3)
    │   ├── c0 
    │   │   └── b072a3d368667939677b9315360631a100ecde (git tree object for c2)
    │   ├── c3
    │   │   └── 6357109ce20af8f90df3c0dd0784e89408d707 (git blob object for c5)
    │   ├── c9 
    │   │   └── 368a318142494f31acd9c83a9763ed9f395a3b (git commit object for c3)
    │   ├── f1 
    │   │   └── 8c7b882bf09e79d96b692c31450cf59344fbf4 (git tree object for c1)
    │   ├── f3 
    │   │   └── 333e2df7930fa29ef8fb7ee177388f4a02b039 (git commit object for c2)
│   ├── info │   └── pack └── refs ├── heads
│   ├── hotfix
│   └── master └── tags 28 directories, 42 files


Check more info.


    $ cat .git/HEAD
ref: refs/heads/master

    $ cat .git/ORIG_HEAD
25e3699293eeeb9b6f4e33e5873171b6aba01e80

$ cat .git/refs/heads/master
85ec49582eb31885ee195c2dd5a9df9c7baa9fd9

$ cat .git/refs/heads/hotfix
85ec49582eb31885ee195c2dd5a9df9c7baa9fd9

$ git ls-files -s
100644 c36357109ce20af8f90df3c0dd0784e89408d707 0 c5.txt 100644 a103f673dd1b7c5727aa0ae0100419adc50e1b76 0 test.txt

$ git log --oneline --decorate --graph --all
* 85ec495 (HEAD -> master, hotfix) c4 * 25e3699 c5
* c9368a3 c3 * f3333e2 c2 * 09c1658 c1


After rebasing, the git commit history will be linear.
Therefore, git will only update the 'master' pointer without creating any extra git object after running the 'git merge' command.


No comments:

Post a Comment