Friday, June 30, 2023

git merge: three-way merging

  

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


Reference: Git Book


Introduction


If it is not the linear relationship of the commit history, we cannot use the fast-forward approach.

We need to use the three-way merging.


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


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


The 'git merge' command will help to create a new merging commit C6, and the parents of C6 are C4 and C5.


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:


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


    $ git merge hotfix

Below is a prompt message:
----------------------------------------
Merge branch 'hotfix' # Please enter a commit message to explain why this merge is necessary, # especially if it merges an updated upstream into a topic branch. # # Lines starting with '#' will be ignored, and an empty message aborts # the commit.
    ----------------------------------------

Merge made by the 'ort' strategy. test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)

Then the auto-generated merging commit was created.


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 ├── 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 merge commit)
    │   ├── 71
    │   │   └── a62be3c4950898a454f6057f353678b542b920 (git commit object for merge commit)
    │   ├── 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 29 directories, 42 files


Check the content of the git commit object '71a62be'


    $ git cat-file -p 71a62be
tree 600188dcf6aa195ed51c91212610f0ab42b29f43
parent 25e3699293eeeb9b6f4e33e5873171b6aba01e80
parent 85ad7e6b045bfc28bf4546a86f1e9b0b669fc034
author Frank <frank@demo.com> datetime
committer Frank <frank@demo.com> datetime

Merge branch 'hotfix'

This merge commit has two parents: commit c4 and c5.


Check more info.


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

    $ cat .git/ORIG_HEAD
25e3699293eeeb9b6f4e33e5873171b6aba01e80

$ cat .git/refs/heads/master
71a62be3c4950898a454f6057f353678b542b920

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

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

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


No comments:

Post a Comment