Git: Guide to Solving Merge Conflicts

Merge Conflicts arise when multiple agents modify the same part of a file and push their changes to a remote branch. When you attempt to merge, pull from or push to these branches - there's a conflict, and Git isn't sure which set of changes to accept and which to reject, since there's no objective measure of which change is right.

Merge Conflicts only arise when it's impossible to discern upfront which changes to keep, and in this case, you have to step in and make a decision.

There are three ways you can deal with a Merge Conflict - you can continue with the merge, by updating your local file to match what already exists in a remote repository, you can abort a merge, which is typically done if there's a major conflict that isn't easily remedied or you can keep the local changes from the working directory and force them upon the remote repository.

In this guide, we'll take a look at the three ways you can resolve a Merge Conflict with Git.

How do Merge Conflicts Happen?

Let's quickly create a repository and a merge conflict so we can observe which changes caused it and how the files look like when we resolve it. We'll emulate a remote-work environment by creating two folders and two Git repositories within them:

$ cd Jane
$ git init
$ git remote add origin https://github.com/DavidLandup0/solving-merge-conflicts.git
$ cd..
$ cd John
$ git init
$ git remote add origin https://github.com/DavidLandup0/solving-merge-conflicts.git

Jane and John are working on a project together, and share the same file - README.md. John wrote the file, accidentally leaving in a typo, and pushed it to the remote origin. Jane caught this, fixed the typo and pushed the file to the remote origin again.

Once John wanted to add a new line to the file, and merge his branch to the main branch - his README.md (which has the typo) was in conflict with the main's README.md, which had the typo fixed.

On John's feature branch, he added a README.md file:

$ git branch feature_john
$ git checkout feature_john
Switched to branch 'feature_john'
$ echo 'Welcome to our READMW.md!' >> README.md
$ git add README.md
$ git commit -m "Added README.md"
[feature_john c44d65f] Added README.md
 1 file changed, 1 insertion(+)
 create mode 100644 README.md

$ git push origin feature_john
git push origin feature_john
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
...
To https://github.com/DavidLandup0/solving-merge-conflicts.git
 * [new branch]      feature_john -> feature_john
 
$ git checkout main
Switched to branch 'main'

$ git merge feature_john
Updating 48f09c2..c44d65f
Fast-forward
 README.md | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
 
$ git push origin main
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/DavidLandup0/solving-merge-conflicts.git
   48f09c2..c44d65f  main -> main

John added a new file to his branch and pushed it to his remote branch and then merged into main - no issues, there weren't any files there before that.

Now, Jane wants to get up to date with the main branch by pulling the changes made there, notices the typo - fixes it, and pushes back to main to prevent others from pulling the erroneous piece:

$ cd Jane
$ git pull origin main
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
...
Updating 48f09c2..c44d65f
Fast-forward
 README.md | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
 
$ git branch feature_jane
$ git checkout feature_jane

$ echo 'Welcome to our README.md!' > README.md

$ git add README.md

$ git commit -m "Fixed typo in README.md file"
[feature_jane 60f64fc] Fixed typo in README.md file
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git push origin feature_jane
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 292 bytes | 292.00 KiB/s, done.
...
To https://github.com/DavidLandup0/solving-merge-conflicts.git
 * [new branch]      feature_jane -> feature_jane

$ git checkout main
Switched to branch 'main'

$ git merge feature_jane
Updating c44d65f..60f64fc
Fast-forward
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git push origin main
Total 0 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/DavidLandup0/solving-merge-conflicts.git
   c44d65f..60f64fc  main -> main

Now main is clean - no typo in the README.md. Though, John's file is now out of sync.

  • If he tries pulling the origin's main - a Merge Conflict will occur.
  • If he tries to push a change with a conflicting change on the remote branch - a Merge Conflict will occur.
  • If he tries to run $ git merge on two branches that have conflicting changes - a Merge Conflict will occur.

Say John added a new line to the file, pulled from the main branch and then tried merging his new addition into main before pushing his :

$ echo 'New line!' >> README.md
$ git add README.md

$ git commit -m "Added new line to README.md"
[feature_john ba27684] Added new line to README.md
 1 file changed, 1 insertion(+)

$ git checkout main
Switched to branch 'main'

$ git pull origin main
From https://github.com/DavidLandup0/solving-merge-conflicts
 * branch            main       -> FETCH_HEAD
Updating c44d65f..60f64fc
Fast-forward
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

$ git merge feature_john
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

There it is - Merge conflict in README.md. There are three things John can do to solve this conflict, as he is now in the MERGING phase. When Git encounters a conflict, it doesn't abandon the merge - it allows you to attempt fixing the issue on the spot or abandon it if you'd like to.

Find Merge Conflict Source

The first step you need to take is find out why there's a Merge Conflict in the first place. When in the MERGING phase, as we are, Git will annotate the file that's causing the conflict. If we open the README.md file on John's local machine, we'll see:

<<<<<<< HEAD
Welcome to our README.md!
=======
'Welcome to our READMW.md!' 
New line!
>>>>>>> feature_john

The <<<<<<< denotes the cause of the conflict and the current reference follows it (HEAD). This is the change from main. Then, we've got the ======= line (just a separator) before John's set of changes.

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

Finally, the >>>>>>> denotes that that's the end of the conflict, with the branch name we're trying to merge into the top side of =======.

Note: If we were merging by pulling main changes into feature_john - the order of changes would be the opposite, since the current reference would be on feature_john and the changes on main would be beneath the ======= line. Though, keep in mind that this isn't good practice as the feature branch is meant to contain separate changes from the main branch.

Solve Merge Conflict with git merge --abort

A valid way to solve the conflict is to abort from it - and stop the MERGING phase. This is typically done when the solution isn't to fix a single line - and when large changes need to be made. This usually necessitates a plan with a team member as well.

If we abort the merge, the added conflict lines will be removed and we'll have John's README.md file once again.

While we're still in the merging phase, let's abort the merge altogether:

$ git merge --abort

This simply aborts the merge and your file is returned to its state before you've encountered the conflict:

'Welcome to our READMW.md!' 
New line!

If you're using Git's Command-Line Editor (or other shells that support the feature), you'll also be able to see which phase you're in:

(main)
$ git merge feature_john
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
(main|MERGING)
$ git merge --abort
(main)
$

Now, with your file out of harm's way - phone your colleague and discuss your next steps. Alternatively, if you accept their changes, you can continue with the merge.

Solve Merge Conflict with git merge --continue

You can continue the merge with a conflict, but Git won't overwrite the files automatically. If you try merging, encounter a conflict, and try to $ git merge --continue, you'll face another error:

$ git merge feature_john
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

$ git merge --continue
error: Committing is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm <file>'
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.
U       README.md

If it were possible to continue, Git would've continued already. You're still in the MERGING phase, so you can change your README.md to conform to main's version of it, mark the resolution by adding or removing the file again, and then run the $ git merge --continue command.

Let's fix the file first. In our case, since the "Welcome..." line was causing an issue, but the "New line!" wasn't - we can leave the new line in - John's new feature - and fix the typo that's in conflict:

Welcome to our README.md!
New line!

Now, we add the file once again and run the $ git merge --continue command:

Fix the file...
$ git add README.md

$ git merge --continue
[main fea8fbb] Merge branch 'feature_john'

You've accepted the changes from main and adapted your local file to reflect it, and adding it back.

Note: In newer versions of Git, when you run the $ git merge --continue command, it'll commit that merge automatically, so you don't have to, though, you do have to add the changed file again. When you run the command, a text editor will open up with the default commit message of Merge branch 'branch_name'. You can just exit it, saving the message, to commit the change and merge the branches.

Solve Merge Conflict By Forcing Local Changes to Remote

Instead of aborting, or yielding to changes - if you're certain that the changes made in your working directory are certainly the ones to keep, you can keep the local changes instead of adapting to the remote ones.

When a Merge Conflict occurs, you can $ git checkout the file from feature_john, and then add it to the main branch.

Note: Remember that $ git checkout updates the files in the working tree to match the version in the index.

When updating - you can keep the changes made on a different branch and apply it to this branch. On the main branch, into which we wish to merge feature_john, let's update the README.md file to contain the changes from the feature_john branch.

In the context of main, these changes are referred to as theirs, while the changes on main are referred to as ours. If you wish to keep changes from main, switch the --theirs flag with --ours:

$ git checkout --theirs README.md
Updated 1 path from the index
$ git add README.md

$ git commit -m "Accepting changes from feature_john"
[main 5541f29] Accepting changes from feature_john

Now, you can merge the rest of the changes that are not in conflict cleanly, since we've only created a roundabout merge for the one file that caused a conflict this way.

Conclusion

In this guide, we've taken a look at how to resolve Merge Conflicts in Git. We've explored the three ways you can bump into this common error, and how they arise.

Then, we've explored three solutions to the issue, with the dummy two local repositories and a remote repository we've created in the examples.

Last Updated: January 15th, 2022
Was this article helpful?

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

David LandupAuthor

Entrepreneur, Software and Machine Learning Engineer, with a deep fascination towards the application of Computation and Deep Learning in Life Sciences (Bioinformatics, Drug Discovery, Genomics), Neuroscience (Computational Neuroscience), robotics and BCIs.

Great passion for accessible education and promotion of reason, science, humanism, and progress.

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms