Resolving Merge Conflicts
What Will We Learn?
Practically learn How to resolve merge conflicts understanding it from the basics.
Resolving Conflicts
First of all, don’t feel overwhelmed listening resolving merge conflicts. The reason people feel daunting to resolve merge conflicts is conflict markers like this:
<<<<<<< HEAD
HI
=======
HELLO
>>>>>>> feat/authUnderstanding Conflict Markers
When you issue git merge command to integrate changes from target branch to your branch, Git tries to combine those changes but when it found the same line modified differently by both branches, Git can’t continue combining and leave these confilct markers for us to deal with it. So, understanding this structure becomes mandatory.
Let’s look at each line of the file above that has been mark for resolving conflicts.
<<<<<<< HEAD: This denotes your branch. You knowHEADwill always point to your current branch. So, this is saying:The following is the change from this branch
HI: And this is exactly what the change from your branch. In your branch, conflicted file has the contentHIat that particular line.=======: This is a seperator denoting that changes from your branch ends above this and anything below is the change from incoming branch.HELLO: And this is exact what the change from the incoming branch looks like. The incoming branch has the contentHELLOat that particular line.>>>>>>> feat/auth: This suggest the end of change from incoming branchfeat/auth.
Generally you keep either top or down of the seperator =======. For example, if you decided to keep the change from incoming branch, you will edit the file and the final content will be HELLO. You remove all the markers and keep only the change you want like:
HELLOResolving Merge Conflicts: Practical Way
The Setup
To practically learn how to resolve merge conflicts, we need to set up first so that Git complains to resolve conflicts. For that:
Initialize Git Repo
First of all create new directory and init with Git.
mkdir learn-resolving-merge-conflict
cd learn-resolving-merge-conflict
git initWrite On File
Create file.c and write the following contents:
#include<stdio.h>
int main(){
printf("Hello!\n");
}Initial Commit
Add and commit changes:
git add file.c
git commit -m 'Initial commit'Commit on New demo Branch
Create a demo branch and switch:
git branch demo
git switch demoModify the return type in file.c from int to void:
#include<stdio.h>
void main(){
printf("Hello!\n");
}And commit the change:
git add file.c
git commit -m 'Update return type of main in file.c'Commit Again on master Branch
Switch back to master branch:
git switch masterAnd modify file.c. The return type now is float (2.0) and printf is printing Hi!\n:
#include<stdio.h>
float main(){
printf("Hi!\n");
return 2.0;
}Commit the changes:
git add file.c
git commit -m 'Update return type and return value of main in file.c, also greet Hi'Merge Conflict
Befre merging, let’s see the commit graph first:
git log --all --graph --oneline --decorateAnd it looks like:
* e4300fa (HEAD -> master) Update return type and return value of main in file.c, also greet Hi
| * a83c15d (demo) Update return type of main in file.c
|/
* 0cf0cff Initial commitThe top two commits shares the same initial parent 0cf0cff. This can be visualized:
%%{init: { 'gitGraph': { 'mainBranchName': 'master' } } }%%
gitGraph
commit id: "0cf0cff"
branch demo
switch demo
commit id: "a83c15d"
switch master
commit id: "e4300fa"
Enough, let’s try to merge the demo branch to our master branch. Make sure you are on master branch.
git merge demoGit tries to integrate changes from demo to master but fails. And complains:
Auto-merging file.c
CONFLICT (content): Merge conflict in file.c
Automatic merge failed; fix conflicts and then commit the result.To understand why it fails, let’s see the content of file.c from both branches side by side.
1 #include<stdio.h>
2 float main(){
3 printf("Hi!\n");
4 return 2.0
5 }1 #include<stdio.h>
2 int main(){
3 printf("Hello!\n");
4 }Only line no. 1 is the same, all other lines are differnt in each branch. So, git doesn’t know and which change to apply and it doesn’ guess which one to keep. It adds conflict markers.
Resolve Merge Conflict
To resolve, merge conflict, let’s understand the marker place at file.c:
cat file.c#include<stdio.h>
<<<<<<< HEAD
float main(){
printf("Hi!\n");
return 2.0
=======
void main(){
printf("Hello!\n");
>>>>>>> demo
}- The first line
#include<stdio.h>is unchanged. So, Git doesn’t keep it inside markers. - The last line
}is also excluded by Git because it is extra line infile.cofmasterwhich is not infeat. Git doesn’t treat it in conflict boundary. - Everything between
<<<<<<< HEADand>>>>>>> demois conflict marker. And generally you choose any one part seperated by=======.
Generally we resolve conflict in any of the following ways:
Keeping Your Part
If you or your team thinks, your branch code makes more sence, keep your (upper) part:
float main(){
printf("Hi!\n");
return 2.0;Remove all the markers and final code looks like:
#include<stdio.h>
float main(){
printf("Hi!\n");
return 2.0;
}Keeping Incoming Part
If you or your team thinks, incoming branch code makes more sense, keep that (lower) part:
void main(){
printf("Hello!\n");Remove all the markers and final code looks like:
#include<stdio.h>
void main(){
printf("Hello!\n");
}Combining Both Part
Sometimes, it may make more sense to combine both parts. Here, if you combine both parts and remove markers, it looks like:
#include<stdio.h>
float main(){
printf("Hi!\n");
return 2.0;
void main(){
printf("Hello!\n");
}If you know C, you know it doesn’t make sense already. So, you can combine parts of both. For example, keep the void return type but print Hi instead of Hello:
#include<stdio.h>
void main(){
printf("Hi!\n");
}Important
If you think resolving conflilct is not making sense at all or resolving is complex, you always have the option to --abort the merge process (git merge --abort). This will give you the same state the first time you had started the merge process.
Tip
It is usually helpful to use Code Editors like VS Code to resolve conflict as they provide nice and easy UI for resolving conflicts.
Commit Resolved Changes
The final step in resolving the conflict is committing resolved changes. Add the resolved file.c and commit it.
git add file.c
git commit -m 'Merged demo to master'The graph after merging:
git log --all --graph --oneline --decorateLooks like:
* 3834dc7 (HEAD -> master) Merged demo to master
|\
| * a83c15d (demo) Update return type of main in file.c
* | e4300fa Update return type and return value of main in file.c, also greet Hi
|/
* 0cf0cff Initial commitVisually:
%%{init: { 'gitGraph': { 'mainBranchName': 'master' } } }%%
gitGraph
commit id: "0cf0cff"
branch demo
switch demo
commit id: "a83c15d"
switch master
commit id: "e4300fa"
merge demo id: "3834dc7"