How to fix merge conflicts

When starting out with git, fixing merge conflicts can be a daunting task. Especially if you’re still getting used to working with git. As with programming (and most things), you can read all you want about it, but actually doing it is where you learn the most. So, I’ve made a git repo that you can use to practice fixing a merge conflict. That way you don’t need to practice on your actual working repo. You can practice on my dummy one. We’ll go through fixing the merge conflict in the next few sections.

Prerequisites

I’m using a Vue.js app in this example, so some knowledge of javascript would be helpful with understanding what’s going on when looking at the code in the merge conflict.

Getting Set Up

To start, head over to: https://github.com/leorojas22/merge-conflict-example (it’s a basic Vue.js calculator app) and then click the Fork button at the top right of the page. This will essentially copy over the repo to your github account and let you work with it. Then, to get the repo onto your computer, you’re going to need to clone it. Make sure you’re on the forked version of the repo and click the “Clone or download” button.

Note:
You’ll be able to tell your on the forked version by looking at the repo name. It will be called [your_account_name]/merge-conflict-example

In the “Clone or download” dropdown, copy the url. Then open your command line, navigate to a place where you want to store the repo and type:

git clone [paste_your_url_here]

Next, navigate into the repo’s folder by typing:

cd merge-conflict-example

Then we’re going to make sure we have the problem branch locally by typing:

git checkout origin/feature-calculate

Then type:

git checkout -b feature-calculate

This will create the problem branch in your local repo.

Finally, we can go back to the master branch. So type:

git checkout master

Merging

We’re going to be merging the feature-calculate branch into master. To do that type:

git merge --no-ff feature-calculate

And… we have a conflict:

If you open the src/App.vue file you’ll see this around line 55:

<<<<<<< HEAD
               if(value == 'clear')
               {
                   console.log('cleared');
                   this.value = 0;
               }


               console.log("Action")
=======
               this.pendingOperations.push(this.value);

               // Equals pressed, run through each operation
               let calculatedValue = 0;
               let nextOperation = false;
               this.pendingOperations.forEach(value => {

                   if(isNaN(value))
                   {
                       nextOperation = value;
                   }
                   else
                   {

                       if(nextOperation)
                       {
                           switch(nextOperation)
                           {
                               case '+':
                                   calculatedValue += value;
                                   break;
                               case '-':
                                   calculatedValue -= value;
                                   break;
                               case '/':
                                   calculatedValue /= value;
                                   break;
                               case 'x':
                                   calculatedValue *= value;
                                   break;
                           }
                       }
                       else
                       {
                           calculatedValue = value;
                       }
                   }
               });


               this.value = calculatedValue;
               this.pendingOperations = [];
               this.nextAction = false;
           }
           else if(isNaN(value))
           {
               this.pendingOperations.push(this.value);
               this.pendingOperations.push(value);
               this.nextAction = value;
>>>>>>> feature-calculate

Looking at this the first time might be a little scary. But, if you look closely it’s a little easier to see what this is saying. The top section, in between the <<<<<<< HEAD and ======= , is the branch that we’re currently on. And the code between the ======= and >>>>>>> feature-calculate is the branch that we’re merging. It will always be this way, the top section is the current branch, the bottom section is the branch we’re merging.

What we now need to do is decide what needs to stay or be rearranged. So to start, what I usually like to do is remove the <<<<<<< HEAD, ======= and >>>>>>> feature-calculate leaving large gaps so I can better see what’s going on.

When we do that we have these bits of code separated out:

if(value == 'clear')
{
   console.log('cleared');
   this.value = 0;
}

console.log("Action")

And

	this.pendingOperations.push(this.value);

	// Equals pressed, run through each operation
	let calculatedValue = 0;
	let nextOperation = false;
	this.pendingOperations.forEach(value => {

	   if(isNaN(value))
	   {
		   nextOperation = value;
	   }
	   else
	   {

		   if(nextOperation)
		   {
			   switch(nextOperation)
			   {
				   case '+':
					   calculatedValue += value;
					   break;
				   case '-':
					   calculatedValue -= value;
					   break;
				   case '/':
					   calculatedValue /= value;
					   break;
				   case 'x':
					   calculatedValue *= value;
					   break;
			   }
		   }
		   else
		   {
			   calculatedValue = value;
		   }
	   }
	});


	this.value = calculatedValue;
	this.pendingOperations = [];
	this.nextAction = false;
}
else if(isNaN(value))
{
	this.pendingOperations.push(this.value);
	this.pendingOperations.push(value);
	this.nextAction = value;

By doing this, we’ve actually fixed any of the coding errors because there’s no more of the conflict indicators. And if you look towards the bottom of the conflict, this bit of code:

else if(isNaN(value))
{
   this.pendingOperations.push(this.value);
   this.pendingOperations.push(value);
   this.nextAction = value;

actually matches up with the closing curly brace outside of the conflict. So that part of the conflict is resolved without doing much other than removing the conflict indicators! We can remove the extra space so it looks a little more normal.

Next let’s look at the top section:

if(value == 'clear')
{
   console.log('cleared');
   this.value = 0;
}


console.log("Action")

Since you’re looking at this code for the first time, it might be tough to know if it’s fine to leave this here or if we need to move it. So to give some background, the calculate method looks at the value parameter and decides what to do. If you look at the if statement at the beginning of the method, it’s checking for the equal sign. Scroll down some more and you’ll see it checks to see if the value is not a number (else if(isNaN(value))), and then lastly it lands in an else section where it handles any numeric input.

With that, it should hopefully be a little more clear what we need to do with this if(value == ‘clear’) portion of the code. We need to move it out of the if(value == ‘=’) block since the conflicted code will never run since if value is an equal sign, it will never be equal to ‘clear’ while inside that if block.

We can change it to either be an else if, or we can move it to the top and change the equal sign check to be an else if. Either way is up to you, but I’ve decided to move it to the top. Your calculate method should now look something like this:

calculate(value) {
	if(value == 'clear')
	{
		console.log('cleared');
		this.value = 0;
	}
	else if(value == '=')
	{

		this.pendingOperations.push(this.value);

		// Equals pressed, run through each operation
		let calculatedValue = 0;
		let nextOperation = false;
		this.pendingOperations.forEach(value => {

			if(isNaN(value))
			{
				nextOperation = value;
			}
			else
			{

				if(nextOperation)
				{
					switch(nextOperation)
					{
						case '+':
							calculatedValue += value;
							break;
						case '-':
							calculatedValue -= value;
							break;
						case '/':
							calculatedValue /= value;
							break;
						case 'x':
							calculatedValue *= value;
							break;
					}
				}
				else
				{
					calculatedValue = value;
				}
			}
		});


		this.value = calculatedValue;
		this.pendingOperations = [];
		this.nextAction = false;
	}
	else if(isNaN(value))
	{
		this.pendingOperations.push(this.value);
		this.pendingOperations.push(value);
		this.nextAction = value;
	}
	else
	{

		if(this.nextAction)
		{
			// An action was pressed prior to this, clear out current value
			this.value = "";
		}

		let newValue = "";
		newValue += this.value + "" + value;
		this.value = parseFloat(newValue);

		this.nextAction = false;
	}
}

Finishing the merge

When you fix your conflicts, you need to re-add the conflicted file to be committed. So, in your terminal type:

git add src/App.vue

Then commit as you normally would do. You can do it through a gui or through the command line. I’ll show you how through the command line. So type:

git commit -m "added calculate feature"

Then, we can push the commit by typing:

git push origin master

And that’s it! Hopefully that was a little helpful for you so you can figure out how to fix your own merge conflicts. If you want more merge conflict examples or have any questions, feel free to leave a comment below!