Currently, I am curious to find out what Git will do for us behind the scene if we run the 'git fetch' command. Below are the logs coming from my experiment.
Reference: Git Book
Preparation
Create a new git repository called 'git-fetch-exp' from the git provider such as GitHub.
Step 1:
Run the following commands to keep tracking the local branch and the remote branch.
$ git clone git@github.com:frank/git-fetch-exp.git
Cloning into 'git-fetch-exp'...
warning: You appear to have cloned an empty repository.
$ cd git-fetch-exp
$ git add .
$ git commit -m 'c1'
[master (root-commit) 823d641] c1
1 file changed, 1 insertion(+)
create mode 100644 demo.txt
$ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 212 bytes | 212.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:frank/git-fetch-exp.git
* [new branch] master -> master
Check the git status.
On branch master
Your branch is up to date with 'origin/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
│ └── remotes
│ └── origin
│ └── master
├── objects
│ ├── 82
│ │ └── 3d6415e56eb1b4481a24ae16193af80a2ca9ae (git commit object for c1)
│ ├── a6
│ │ └── 97b5a1ad379184d8223b14860be0bd1c2f50b2 (git tree object for c1)
│ ├── ae
│ │ └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
├── remotes
│ └── origin
│ └── master
└── tags
19 directories, 27 files
Check more info.
$ git log --oneline --decorate --graph --all
* 823d641 (HEAD -> master, origin/master) c1
Step 2:
Create a 'c2' commit from the UI of git provider to simulate the collaboration with other people.
Step 3:
Run the following commands to add a new 'c3' commit and try to push it to the remote.
$ git add .
$ git commit -m 'c3'
[master 96dfa26] c3
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
To github.com:frank/git-fetch-exp.git
! [rejected] master -> master (fetch first)
error: failed to push some refs to 'github.com:frank/git-fetch-exp.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Check the git status.
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
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
│ └── remotes
│ └── origin
│ └── master
├── objects
│ ├── 07
│ │ └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
│ ├── 82
│ │ └── 3d6415e56eb1b4481a24ae16193af80a2ca9ae (git commit object for c1)
│ ├── 96
│ │ └── dfa26af1057acfa5fa23ca4a7a92ccebe00f97 (git commit object for c3)
│ ├── a6
│ │ └── 97b5a1ad379184d8223b14860be0bd1c2f50b2 (git tree object for c1)
│ ├── ae
│ │ └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
│ ├── be
│ │ └── 57a37c6cb18a82fa8381ce465de76a5de48103 (git tree object for c3)
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
├── remotes
│ └── origin
│ └── master
└── tags
22 directories, 30 files
Check more info.
$ git log --oneline --decorate --graph --all
* 96dfa26 (HEAD -> master) c3
* 823d641 (origin/master) c1
Step 4:
Run the 'git fetch' commands to fetch commits from the remote.
$ git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 608 bytes | 608.00 KiB/s, done.
From github.com:frank/git-fetch-exp
823d641..5df7c20 master -> origin/master
Check the git status.
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
nothing to commit, working tree clean
Check the .git tree structure.
$ tree .git/
.git/
├── COMMIT_EDITMSG
├── FETCH_HEAD <= NEW
├── 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
│ └── remotes
│ └── origin
│ └── master
├── objects
│ ├── 07
│ │ └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
│ ├── 16
│ │ └── f9ec009e5568c435f473ba3a1df732d49ce8c3 (git blob object for c2)
│ ├── 5d
│ │ └── f7c2013b631ba005404a1153983cc810b6d78c (git commit object for c2)
│ ├── 6a
│ │ └── 13fb3d43e0792ce46f838af08dd8ffdf9eeb48 (git tree object for c2)
│ ├── 82
│ │ └── 3d6415e56eb1b4481a24ae16193af80a2ca9ae (git commit object for c1)
│ ├── 96
│ │ └── dfa26af1057acfa5fa23ca4a7a92ccebe00f97 (git commit object for c3)
│ ├── a6
│ │ └── 97b5a1ad379184d8223b14860be0bd1c2f50b2 (git tree object for c1)
│ ├── ae
│ │ └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
│ ├── be
│ │ └── 57a37c6cb18a82fa8381ce465de76a5de48103 (git tree object for c3)
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
├── remotes
│ └── origin
│ └── master
└── tags
25 directories, 34 files
Check more info.
$ git log --oneline --decorate --graph --all
* 96dfa26 (HEAD -> master) c3
| * 5df7c20 (origin/master) c2
|/
* 823d641 c1
The command 'git fetch' will only fetch commits from remote to our local, but it will not change our local branch. We can make our own decision to either merge it or rebase it.
Step 5: Using the 'rebase' approach
Run the 'git rebase' command first.
$ git rebase origin/master
Auto-merging demo.txt
CONFLICT (content): Merge conflict in demo.txt
error: could not apply 96dfa26... c3
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 96dfa26... c3
Check the git status.
interactive rebase in progress; onto 5df7c20
Last command done (1 command done):
pick 96dfa26 c3
No commands remaining.
You are currently rebasing branch 'master' on '5df7c20'.
(fix conflicts and then run "git rebase --continue")
(use "git rebase --skip" to skip this patch)
(use "git rebase --abort" to check out the original branch)
Unmerged paths:
(use "git restore --staged <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: demo.txt
no changes added to commit (use "git add" and/or "git commit -a")
Check the .git tree structure.
$ tree .git/
.git/
├── AUTO_MERGE <= NEW
├── COMMIT_EDITMSG
├── FETCH_HEAD
├── HEAD
├── MERGE_MSG <= NEW
├── ORIG_HEAD <= NEW
├── REBASE_HEAD <= NEW
├── 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
│ └── remotes
│ └── origin
│ └── master
├── objects
│ ├── 07
│ │ └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
│ ├── 16
│ │ └── f9ec009e5568c435f473ba3a1df732d49ce8c3 (git blob object for c2)
│ ├── 5b
│ │ └── fabaa67f7669a81743ef70095fdcfc9e5e2914 (git blob object for auto-merging)
│ ├── 5d
│ │ └── f7c2013b631ba005404a1153983cc810b6d78c (git commit object for c2)
│ ├── 6a
│ │ └── 13fb3d43e0792ce46f838af08dd8ffdf9eeb48 (git tree object for c2)
│ ├── 82
│ │ └── 3d6415e56eb1b4481a24ae16193af80a2ca9ae (git commit object for c1)
│ ├── 96
│ │ └── dfa26af1057acfa5fa23ca4a7a92ccebe00f97 (git commit object for c3)
│ ├── a4
│ │ └── 5b62e14f4eb1ee3a12863bd32cf6ab938cbb2b (git tree object for auto-merging)
│ ├── a6
│ │ └── 97b5a1ad379184d8223b14860be0bd1c2f50b2 (git tree object for c1)
│ ├── ae
│ │ └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
│ ├── be
│ │ └── 57a37c6cb18a82fa8381ce465de76a5de48103 (git tree object for c3)
│ ├── info
│ └── pack
├── rebase-merge
│ ├── author-script
│ ├── done
│ ├── drop_redundant_commits
│ ├── end
│ ├── git-rebase-todo
│ ├── git-rebase-todo.backup
│ ├── head-name
│ ├── interactive
│ ├── message
│ ├── msgnum
│ ├── no-reschedule-failed-exec
│ ├── onto
│ ├── orig-head
│ ├── patch
│ └── stopped-sha
└── refs
├── heads
│ └── master
├── remotes
│ └── origin
│ └── master
└── tags
28 directories, 55 files
Check more info.
$ cat .git/AUTO_MERGE
a45b62e14f4eb1ee3a12863bd32cf6ab938cbb2b
$ git cat-file -p a45b62
100644 blob 5bfabaa67f7669a81743ef70095fdcfc9e5e2914 demo.txt
$ git cat-file -p 5bfab
<<<<<<< HEAD
c2
=======
c3
>>>>>>> 96dfa26 (c3)
$ cat .git/MERGE_MSG
c3
# Conflicts:
# demo.txt
$ cat .git/ORIG_HEAD
96dfa26af1057acfa5fa23ca4a7a92ccebe00f97
$ cat .git/REBASE_HEAD
96dfa26af1057acfa5fa23ca4a7a92ccebe00f97
$ git log --oneline --decorate --graph --all
* 96dfa26 (HEAD -> master) c3
| * 5df7c20 (origin/master) c2
|/
* 823d641 c1
Step 6:
Resolve the conflict since c2 and c3 modify the same file.
After that, run the command 'git rebase --continue' to keep rebasing.
$ vim demo.txt
$ git add demo.txt
$ git rebase --continue
[detached HEAD b977483] c3'
1 file changed, 1 insertion(+), 1 deletion(-)
Successfully rebased and updated refs/heads/master.
Check the git status.
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
Check the .git tree structure.
$ tree .git/
.git/
├── COMMIT_EDITMSG
├── FETCH_HEAD
├── HEAD
├── ORIG_HEAD
├── REBASE_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
│ └── remotes
│ └── origin
│ └── master
├── objects
│ ├── 07
│ │ └── 71aea884dd394a7b12783d049f05b5599f41a4 (git blob object for c3)
│ ├── 16
│ │ └── f9ec009e5568c435f473ba3a1df732d49ce8c3 (git blob object for c2)
│ ├── 5b
│ │ └── fabaa67f7669a81743ef70095fdcfc9e5e2914 (git blob object for auto-merging)
│ ├── 5d
│ │ └── f7c2013b631ba005404a1153983cc810b6d78c (git commit object for c2)
│ ├── 6a
│ │ └── 13fb3d43e0792ce46f838af08dd8ffdf9eeb48 (git tree object for c2)
│ ├── 82
│ │ └── 3d6415e56eb1b4481a24ae16193af80a2ca9ae (git commit object for c1)
│ ├── 96
│ │ └── dfa26af1057acfa5fa23ca4a7a92ccebe00f97 (git commit object for c3)
│ ├── a4
│ │ └── 5b62e14f4eb1ee3a12863bd32cf6ab938cbb2b (git tree object for auto-merging)
│ ├── a6
│ │ └── 97b5a1ad379184d8223b14860be0bd1c2f50b2 (git tree object for c1)
│ ├── ae
│ │ └── 9304576a6ec3419b231b2b9c8e33a06f97f9fb (git blob object for c1)
│ ├── b9
│ │ └── 774831bbe2500eedc5ba0f5a4dc86a8e3c81e3 (git commit object for c3')
│ ├── be
│ │ └── 57a37c6cb18a82fa8381ce465de76a5de48103 (git tree object for c3)
│ ├── info
│ └── pack
└── refs
├── heads
│ └── master
├── remotes
│ └── origin
│ └── master
└── tags
28 directories, 39 files
Check more info.
$ git log --oneline --decorate --graph --all
* b977483 (HEAD -> master) c3'
* 5df7c20 (origin/master) c2
* 823d641 c1
Step 7:
Run the 'git push' command to push the local changes to the remote.
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 248 bytes | 248.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:frank/git-fetch-exp.git
5df7c20..b977483 master -> master
Check the git status.
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
Check more info.
$ git log --oneline --decorate --graph --all
* b977483 (HEAD -> master, origin/master) c3'
* 5df7c20 c2
* 823d641 c1