Merge Conflicts for beginners

Merge Conflicts for beginners

·

10 min read

Motivation

Hello, I'm Omar Alaa, a Mobile Software Engineer. I am writing this article because merge conflicts have always been a very scary topic to me and I had to google the approach for resolving these conflicts each time I ran into a conflict.

I never really got a good understanding of it, I was only concerned with fixing the issue and carrying on with my project, however lately I decided to have a good understanding of the topic and to share the approach I use to fix the merge conflicts very easily.


Introduction

Merge conflicts are sort of inevitable when working with git, especially if you are working with teammates on this repository. If you ever encountered one you would notice that it can be resolved using one click like accept mine or accept theirs, but sometimes it leads the code to break or losing some features that was implemented by another teammate. So, we need to ask ourselves what is a merge conflict in the first place to be able to handle it correctly.


Merge Conflict

Warmup

To understand how the merge conflicts happens, lets create a merge conflict ourselves.

We will create an empty folder called "the-simple-project", and we will create an empty javascript file called "index.js"

Screen Shot 2021-09-13 at 2.55.58 PM.png

Now let's initialize our repository by writing the following code in the terminal or powershell in the directory of our project

git init

Now let's add some dummy code to our index.js file, copy and paste the following code:

const text = "Hello World";

const firstColor = "red";
const secondColor = "green";

function useFirstColorToDoSomething() {
    console.log(`${firstColor} is used here`);
}

console.log(text);

Now let's commit our changes by running the following lines in terminal

git add .
git commit -m "Init Commit"

If you made it through here, then now we have a file called index.js that has some dummy code and we have committed this code.

We will treat this file as our main project in real-life-case.

Let's assume that colors are part of our theme in our application. and the function useFirstColor is some component that uses the first color to render some image or text with this color.

Merge Conflict Scenario

Suppose your product manager asks person A to create a feature A, and he asks Person B to work on feature B

Person A story

So, person A creates a new branch from master using

git checkout -b feature/a

Then he edits the index.js file to the following:

  • changed second color to blue
  • added a new function to use secondColor

Make sure to remove comments if you are going to copy the code

const text = "Hello World";

const firstColor = "red";
const secondColor = "blue"; // We edited the second Color to be blue

function useFirstColorToDoSomething() {
    console.log(`${firstColor} is used here`);
}

// We added a new function that uses the second color
function useSecondColorToDoSomething() {
    console.log(`${secondColor} is used here`);
}

console.log(text);

Then person A commits his changes by writing

git add .
git commit -m "changed second color to blue and used it"

Now Person A has a local branch called feature/A that contains the commit that changed the color.

photo1631617218.jpeg

Person B Story

While user A created his branch, Person B created another branch from master called feature/B

Note: in this case we are only one person simulating two persons A, and B so we must go back to the master branch and then checkout to the new branch feature/B

Make sure that you don't checkout from feature/A branch

  • to switch back to master
git checkout master
  • to create new branch feature/B from master
git checkout -b feature/b

As we can see the code should be as it was after the init commit and we don't know anything about color changing person A did

Screen Shot 2021-09-13 at 4.15.15 PM.png

Now Person B decides to create his changes by editing the following in the index.js file

  • changed second color from green to navy
  • created thirdColor and fourthColor
  • changed the first function to use the fourthColor instead of the first
  • added a new function that uses the third color

Make sure to remove comments if you are going to copy the code

const text = "Hello World";

const firstColor = "red";
const secondColor = "navy"; // We changed second color navy
const thirdColor = "purple"; // We added the third color purple
const fourthColor = "yellow"; // We added fourth color yellow

function useFourthColorToDoSomething() { // We changed this function name to use fourthColor instead of firstColor
    console.log(`${fourthColor} is used here`); // We changed this function to use fourthColor instead of firstColor
}

function useThirdColorToDoSomething() { // We added a function to use the thirdColor
    console.log(`${thirdColor} is used here`);
}

console.log(text);

and then similarly person B committed his changes using the following code

git add .
git commit -m "Changed second color. Added More Colors. Changed first function. added thirdColorFunction"

photo1631617240.jpeg

Merging Branches A and B

Now all the changes are done, person A and B implemented their features and you are one who needs to merge these changes into master branch.

First let's go to the master branch

git checkout master

Now let's merge branch feature/A into our master branch to add the features person A created

git merge feature/a

image.png

We'll notice the success of merging feature/a into our master branch and now we have feature A on our master branch.

Now we need to add feature b from branch feature/b

So, let's try to write the same merge command as feature/a for feature/b

git merge feature/b

And Congratulations! we have created our merge conflict!

and you should be seeing this very confusing screen

image.png

Why does it happen?

Simple Definition

A merge conflicts happens when two different branches edit the same line of code differently.

Detailed Explanation

One of the things that happened in our code, that both person A and B edited the line of secondColor, person A changed it to blue, while in the same time person B changed it to navy.

Git is trying to save your life here; he doesn't want to override the blue one by the navy because both were done on the same time. And git is scared to choose any of them and cause your data to be lost.

So basically, what merge conflicts means is git telling you, hey you edited this thing two ways, which one do you want me to apply?

Non-Conflicting changes

Notice that both persons A and B added new functions to the end of the file, but git had no problem with all of them because simply they do not conflict with each other, we are not telling git to change the same thing in two different ways.

hence adding the functions useThirdColor, useSecondColor has no conflict with each other although they may have been added on the same line, but git is smart enough to know they are just new added lines that does not conflict!

And more importantly, we changed the first function name to useFourth instead of useFirst color in branch feature/b, and because we did not change the name of this function on branch feature/a, we didn't get a merge conflict.

We basically edited the name of the function from master branch, but if we edited it in both branch feature/a and branch feature/b we definitely would have got a merge conflict and we would have to resolve it by choosing one of them.

image.png

Now in case of changes that are conflicting it requires some work from us!

Accept mine, theirs, and both

Now we see the options in vs code which prompts us to choose one of the three options Accept current change, accept incoming changes and accept both for a specific piece of code that is bounded by weird arrows.

image.png

hence let's explain what each of them really mean:

  • Accept Current Change: Ignore changes from the branch feature/b am trying to merge into master, hence we won't add secondColor (the navy one), third color and fourth Color and will just have secondColor as blue

image.png

  • Accept Incoming Change: Ignore changes from my current branch master, and take the changes from the incoming branch feature/b. hence we will replace secondColor by navy and add third and fourth colors

image.png

  • Accept Both Changes: This option literally means put both changes one after the other so we will have EVERYTHING from both branches

image.png

Notice that accepting any option of these can very easily cause the code to produce an error, as git is not smart enough to know that accepting which changes can cause no error

Handling Changes

Now you're the man in charge of handling the differences.

You need to define your goals from both features A and B.

  • We want second color to be blue (from feature/a)
  • We want the third and fourth colors to exist (from feature/b)

    Let's discuss the options we have according to these changes:

image.png

  • Accept current change: will remove the third and fourth colors, and hence we are violating that we need the third and fourth colors to exist

image.png

  • Accept Incoming change: will add the third color and fourth color, but we will lose the blue color we want from branch feature/a

image.png

  • Accept Both change: will add the third color and fourth color, and also will add the line of the second color two times, which will produce an error.

image.png

We notice that none of all the three options provided to us is the solution in our case.

Actually, these buttons just act as helper functions because they could work in lots of cases, for example if we wanted the colors to be navy, we could have very easily accepted incoming change and we would be good to go, But They are not always the answer and thats where the confusion arises.

The solution

An important thing that beginners with git forget, that this file can be edited manually so easily.

The merge process is all about creating a commit which merges the changes of two branches, and like any normal commit we can very normally edit in the file however we want without any restrictions.

The only difference is having the extra symbols on line 4, 6, and 10 that just explains to you which changes comes from which branch.

image.png

These scary symbols are just for your eyes to know there are changes, but you can very easily modify them with your hands and remove them.

So, let's remove lines 4, 6 and 10. (Thats the equivalent of accepting both changes)

image.png

Now we can edit freely on our file without this confusing symbols.

In this case, we have all colors and that's good, but we have the double second color line.

So, let's manually remove line 5 from our file.

And the result file should look like this

const text = "Hello World";

const firstColor = "red";
const secondColor = "blue"; 
const thirdColor = "purple";
const fourthColor = "yellow";

function useFourthColorToDoSomething() {
    console.log(`${fourthColor} is used here`);
}

function useThirdColorToDoSomething() {
    console.log(`${thirdColor} is used here`);
}

function useSecondColorToDoSomething() {
    console.log(`${secondColor} is used here`);
}

console.log(text);

We have resolved

It's as simple as that. Once you have your file in merge process, it's your playground, and you can edit it however you want.

You can now very safely stash files and commit them.

You know what could be more interesting?

What if you decided to resolve the conflict by replacing the secondColor with a color called the magicColor and we should use it instead of the secondColor.

const text = "Hello World";

const firstColor = "red";
const magicColor = "MAGIC_COLOR";  // changed second color to magic_color
const thirdColor = "purple";
const fourthColor = "yellow";

function useFourthColorToDoSomething() {
    console.log(`${fourthColor} is used here`);
}

function useThirdColorToDoSomething() {
    console.log(`${thirdColor} is used here`);
}

// Renamed function to magic color
function useMagicColorToDoSomething() {
    console.log(`${magicColor} is used here`); // changed secondColor usage to magicColor
}

console.log(text);

And surprisingly these changes would work perfectly! and we don't even need to use any of the changes from both features A and B.

We could even not use any colors at all! just treat this as any normal commit and adjust the file to your desired changes.

then we can run commands

git add .
git commit -m "Merged feature/b into master easily with no conflicts anymore"

And we have merged the two branches the way we want it!


Conclusion

Merge conflicts if not understood well, can cause much confusion to new git users. I tried to explain the process step by step and explain why we do each step along the way.

Thanks for your reading!

Happy Coding!