Thursday, February 7, 2019

How to solve git merge conflict with merge rebase

A git repository usually have a stable master branch for building the release artifacts, a development branch for collecting development checkins. The developers branch out bugfix and feature branches from develop branch.

At 1 pm, developer A branched out from develop a bugfix1 branch and developer B branched out feature1 branch from develop. At 2 pm, developer A merged bugfix1 branch back to develop. At 3 pm, developer B also try to merge feature1 branch back to develop. The merge failed, the reason is they both modified the same lines for the same file and git can not tell whose version to keep. This situation is called merge conflict.

merge conflict at node4
merge conflict at node4

To merge feature1 to develop, we use the following commands:
git checkout develop;
then
git merge feature1;

If the merge didn't success, the first thing we need to do is to issue
git merge --abort
on the develop branch to abort the merge. When you use bitbucket gui to create a pull request, the bitbucket do the "git merge --abort" for you on the develop branch automatically.

Next, we need to fix the feature1 branch so that the conflict won't be there when develop merge feature1 next time.

To do that, we can use the following commands, as we went through in the previous post:
git checkout feature1;
git merge develop;

This approach will create 3 logs in the develop branch after the merge: bugfix1 commit, feature1 commit, conflict resolving commit.

In this post, we will go through another approach, which is using:

git checkout feature1;
git rebase develop;

Git rebase command is one of the most powerful commands in Git. It has the ability to rewrite your repository's commit history, by rearranging, modifying, and even deleting commits.

rebase from node2 to node3
rebase from node2 to node3

Here the rebase means to give a new parent to the feature1 branch. This is what you do when you use git rebase to keep up with the upstream repository. The most important reason for using git rebase here is to change the starting point of your local branch feature1. It previously has the starting point 2, after rebase, it's starting point becomes 3. Since the starting point changes, all the content from node3 is applied to your local copy and the conflict will be introduced. Then you manually modify the files to solve the conflict, then you got a new version on top of the node3 version to commit.

This approach will create 2 logs after develop merge the feature1 branch: bugfix1 commit, feature1 commit.  Notice there is no conflict solve commit.

Now you have the local changes ready for feature1 branch. You have 2 options to merge the changes back to develop branch.

If you use pure command line for merge, you can merge develop local, then push to remote repo.
You can just
git checkout develop;
git pull;
git merge feature1;
git push origin develop;

If you are using bitbucket and pull request for merge. You can push the feature1 changes to remote repo, then create pull request. That way, the git bucket pull request merge will execute the following command for you on the remote repository with bitbucket's credential instead of yours.
git checkout develop;
git merge feature1;


Since the rebase changed the starting point of feature1, the remote branch will get confused, so if you are using bitbucket, and using pull request to merge feature1 to develop, then you need to use the following command to push your local changes about feature1 to remote repo.

git push -f origin feature1;

The -f flag means you acknowledged the fact that the starting point of you local branch differ from the starting point of the remote branch for feature1.

The process is demonstrated in the following demo:



===============
GIT>git clone https://github.com/tekgadg/Pong_sept_20_2D.git demorepo
Cloning into 'demorepo'...
remote: Enumerating objects: 6, done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 6
Unpacking objects: 100% (6/6), done.
Checking connectivity... done.
GIT>cd demorepo/
GIT>echo a > a.txt
GIT>echo b > b.txt
GIT>echo c > c.txt
GIT>git branch develop
GIT>git checkout develop
Switched to branch 'develop'
GIT>git config --global --edit
GIT>git status
On branch develop
Untracked files:
  (use "git add <file>..." to include in what will be committed)

a.txt
b.txt
c.txt

nothing added to commit but untracked files present (use "git add" to track)
GIT>git add --all
GIT>git commit -m "develop base commit"
[develop e45abe7] develop base commit
 3 files changed, 3 insertions(+)
 create mode 100644 a.txt
 create mode 100644 b.txt
 create mode 100644 c.txt
GIT>git branch feature1
GIT>git checkout -b bugfix1 develop
Switched to a new branch 'bugfix1'
GIT>echo xyzcode > a.txt
GIT>echo xyzcode >> b.txt
GIT>echo xyzcode >> c.txt
GIT>git commit -am "bugfix1 commit"
[bugfix1 8488ffa] bugfix1 commit
 3 files changed, 3 insertions(+), 1 deletion(-)
GIT>git checkout develop
Switched to branch 'develop'
GIT>git merge bugfix1
Updating e45abe7..8488ffa
Fast-forward
 a.txt | 2 +-
 b.txt | 1 +
 c.txt | 1 +
 3 files changed, 3 insertions(+), 1 deletion(-)
GIT>git checkout feature1
Switched to branch 'feature1'
GIT>echo xyznetwork >> a.txt
GIT>echo xyznetwork > b.txt
GIT>git rm c.txt
rm 'c.txt'
GIT>git status
On branch feature1
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

deleted:    c.txt

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

modified:   a.txt
modified:   b.txt

GIT>git commit -am "feature1 commit"
[feature1 ce6291a] feature1 commit
 3 files changed, 2 insertions(+), 2 deletions(-)
 delete mode 100644 c.txt
GIT>git checkout develop
Switched to branch 'develop'
GIT>git merge feature1
CONFLICT (modify/delete): c.txt deleted in feature1 and modified in HEAD. Version HEAD of c.txt left in tree.
Auto-merging b.txt
CONFLICT (content): Merge conflict in b.txt
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
Automatic merge failed; fix conflicts and then commit the result.
GIT>git diff
diff --cc a.txt
index ae5111a,56bdbb4..0000000
--- a/a.txt
+++ b/a.txt
@@@ -1,1 -1,2 +1,6 @@@
++<<<<<<< HEAD
 +xyzcode
++=======
+ a
+ xyznetwork
++>>>>>>> feature1
diff --cc b.txt
index 5e2bb23,c09e41c..0000000
--- a/b.txt
+++ b/b.txt
@@@ -1,2 -1,1 +1,6 @@@
++<<<<<<< HEAD
 +b
 +xyzcode
++=======
+ xyznetwork
++>>>>>>> feature1
* Unmerged path c.txt
GIT>git merge --abort
GIT>git checkout feature1
Switched to branch 'feature1'
GIT>git rebase develop
First, rewinding head to replay your work on top of it...
Applying: feature1 commit
Using index info to reconstruct a base tree...
M a.txt
M b.txt
M c.txt
Falling back to patching base and 3-way merge...
CONFLICT (modify/delete): c.txt deleted in feature1 commit and modified in HEAD. Version HEAD of c.txt left in tree.
Auto-merging b.txt
CONFLICT (content): Merge conflict in b.txt
Auto-merging a.txt
CONFLICT (content): Merge conflict in a.txt
Failed to merge in the changes.
Patch failed at 0001 feature1 commit
The copy of the patch that failed is found in:
   /Users/homenetwork/git/demorepo/.git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

a
GIT>git diff
xyznetwork
diff --cc a.txt
index ae5111a,56bdbb4..0000000
--- a/a.txt
+++ b/a.txt
@@@ -1,1 -1,2 +1,6 @@@
++<<<<<<< HEAD
 +xyzcode
++=======
+ a
+ xyznetwork
++>>>>>>> feature1 commit
diff --cc b.txt
index 5e2bb23,c09e41c..0000000
--- a/b.txt
+++ b/b.txt
@@@ -1,2 -1,1 +1,6 @@@
++<<<<<<< HEAD
 +b
 +xyzcode
++=======
+ xyznetwork
++>>>>>>> feature1 commit
* Unmerged path c.txt
GIT>vi a.txt
GIT>vi b.txt
GIT>git diff
diff --cc a.txt
index ae5111a,56bdbb4..0000000
--- a/a.txt
+++ b/a.txt
diff --cc b.txt
index 5e2bb23,c09e41c..0000000
--- a/b.txt
+++ b/b.txt
* Unmerged path c.txt
GIT>git status
rebase in progress; onto 8488ffa
You are currently rebasing branch 'feature1' on '8488ffa'.
  (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 reset HEAD <file>..." to unstage)
  (use "git add/rm <file>..." as appropriate to mark resolution)

both modified:   a.txt
both modified:   b.txt
deleted by them: c.txt

no changes added to commit (use "git add" and/or "git commit -a")
GIT>git log
commit 8488ffa7e153fe97fb6566f94f544a91e11b926b
Author: you <you@example.com>
Date:   Thu Feb 7 06:57:35 2019 -0500

    bugfix1 commit

commit e45abe719c91e6932329eaca3885f7f75715ac96
Author: you <you@example.com>
Date:   Thu Feb 7 06:54:31 2019 -0500

    develop base commit

commit 40aac7d161dc232299f81ed7112b88986b5fbe39
Author: SeanShin <seanshin@RetOne.local>
Date:   Sun Jun 14 16:19:04 2015 -0700

    upload
GIT>git add --all
GIT>git status
rebase in progress; onto 8488ffa
You are currently rebasing branch 'feature1' on '8488ffa'.
  (all conflicts fixed: run "git rebase --continue")

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

modified:   a.txt
modified:   b.txt

GIT>ls
AndroidManifest.xml a.txt c.txt sketch.properties
Pong_sept_20_2D.pde b.txt code
GIT>git add c.txt
GIT>git status
rebase in progress; onto 8488ffa
You are currently rebasing branch 'feature1' on '8488ffa'.
  (all conflicts fixed: run "git rebase --continue")

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

modified:   a.txt
modified:   b.txt

GIT>git rebase --continue
Applying: feature1 commit
GIT>git status
On branch feature1
nothing to commit, working directory clean
GIT>ls
AndroidManifest.xml a.txt c.txt sketch.properties
Pong_sept_20_2D.pde b.txt code
GIT>git log
commit 995868efe3b50a562b4015ec9aeb89fda585de1d
Author: you <you@example.com>
Date:   Thu Feb 7 06:59:33 2019 -0500

    feature1 commit

commit 8488ffa7e153fe97fb6566f94f544a91e11b926b
Author: you <you@example.com>
Date:   Thu Feb 7 06:57:35 2019 -0500

    bugfix1 commit

commit e45abe719c91e6932329eaca3885f7f75715ac96
Author: you <you@example.com>
Date:   Thu Feb 7 06:54:31 2019 -0500

    develop base commit

commit 40aac7d161dc232299f81ed7112b88986b5fbe39
Author: SeanShin <seanshin@RetOne.local>
Date:   Sun Jun 14 16:19:04 2015 -0700

    upload
GIT>git checkout develop
Switched to branch 'develop'
GIT>git merge feature1
Updating 8488ffa..995868e
Fast-forward
 a.txt | 3 ++-
 b.txt | 3 +--
 2 files changed, 3 insertions(+), 3 deletions(-)
GIT>ls
AndroidManifest.xml a.txt c.txt sketch.properties
Pong_sept_20_2D.pde b.txt code
GIT>git log
commit 995868efe3b50a562b4015ec9aeb89fda585de1d
Author: you <you@example.com>
Date:   Thu Feb 7 06:59:33 2019 -0500

    feature1 commit

commit 8488ffa7e153fe97fb6566f94f544a91e11b926b
Author: you <you@example.com>
Date:   Thu Feb 7 06:57:35 2019 -0500

    bugfix1 commit

commit e45abe719c91e6932329eaca3885f7f75715ac96
Author: you <you@example.com>
Date:   Thu Feb 7 06:54:31 2019 -0500

    develop base commit

commit 40aac7d161dc232299f81ed7112b88986b5fbe39
Author: SeanShin <seanshin@RetOne.local>
Date:   Sun Jun 14 16:19:04 2015 -0700

    upload
GIT>git status
On branch develop
nothing to commit, working directory clean
GIT>

No comments:

Post a Comment

Why I stopped publishing blog posts as information provider

Now the AI can generate content. Does that mean the web publishing industry reaches the end? ChatGPT said: ChatGPT Not at all. While AI can ...