Read Anthropic’s case study about Graphite Reviewer

How to resolve merge conflicts in Git

Greg Foster
Greg Foster
Graphite software engineer


Note

This guide explains this concept in vanilla Git. For Graphite documentation, see our CLI docs.


A merge conflict is an event that occurs when Git is unable to automatically resolve differences in code between two commits. Despite following best practices, over the course of your career you will inevitably run into merge conflicts. Learning how to effectively resolve them is a crucial skill for any developer working with Git.

Merge conflicts happen when two branches have been changed in conflicting ways. This usually occurs in collaborative environments where multiple developers are working on the same codebase. These conflicts arise when performing operations like git merge, git rebase, and git cherry-pick , as Git doesn’t automatically know which version of the code to accept.

  • Merging: When merging two branches, conflicts might occur if the same lines of code have been altered differently in each branch.

  • Rebasing: Similar to merging, rebasing can cause conflicts if the commits being rebased have conflicting changes with the new base.

  • Cherry-picking: Applying a commit from one branch to another with git cherry-pick can also lead to conflicts if that commit has conflicting changes with the current branch.

When you encounter a conflict, Git will pause the operation requiring a resolution. It will mark the file as conflicted and insert conflict markers into the file to visually show the conflicting changes.

  1. Git will notify you of a conflict during a merge, rebase, or cherry-pick operation.

  2. Use git status to list all files with conflicts.

Conflicted files will contain sections marked like this:

Terminal
<<<<<<< HEAD
// Code as it is on your local machine
=======
// Conflicting code from the upstream remote branch
>>>>>>> feature/upstream-branch-name
  • The ======= line divides your changes (HEAD) from the changes in the upstream branch (feature/upstream-branch-name).
  1. Manual Resolution: Open the conflicted file(s) in your code editor. Decide which version of changes to keep, or pick and choose from both versions, manually merging them together. After editing, remove the conflict markers.

  2. Using an IDE: many IDEs support version control management including, merge conflict resolution. Each editor in the Jetbrains suite for example provides a specific merge conflict resolution view that highlights each conflict in your file across versions. You can then browse through these files line by line, accepting or rejecting changes by version.

screenshot of jetbrains IDE merge conflict resolution

After resolving the conflicts, you need to add the files to stage them for commit and then continue the operation that was interrupted by the conflict.

For a merge:

Terminal
git add .
git commit -m "Resolve merge conflicts between "

For a rebase:

Terminal
git add .
git rebase --continue
  • Prevention is best: Keep your branches short-lived and merge them frequently to minimize conflicts. Always follow best pull request practices.

  • Understand the conflict: Take the time to understand why the conflict occurred. This understanding can prevent similar conflicts in the future.

  • Communicate: If the conflict involves changes made by others, discuss it with them. If possible it’s best to avoid making changes to the exact same lines of code that someone else is currently working on.

  • Use a consistent coding style: Many conflicts arise from formatting differences. Using tools like linters and formatters can prevent these types of conflicts.

  • Regularly fetch and merge: Stay updated with changes in your repository to avoid large, complex conflicts.

One common place merge conflicts arise is differences in dependency versions. Suppose you have a conflict in a config.json file related to different configurations set in two different branches.

Terminal
<<<<<<< HEAD
{
"name": "app",
"version": "1.0.0",
"dependencies": {
"libraryA": "^2.0.0"
}
=======
{
"name": "app",
"version": "1.0.1",
"dependencies": {
"libraryA": "^2.1.0",
"libraryB": "^3.0.0"
}
>>>>>>> 03-19-chore_update_dependencies

The next step is to decide which changes to keep. If we want to keep the upgrade to version 1.0.1 and includeLibrary B we’ll accept that change. In this case the resolved config.json would look like this:

Terminal
{
"name": "app",
"version": "1.0.1",
"dependencies": {
"libraryA": "^2.1.0",
"libraryB": "^3.0.0"
}
}

The the version version was updated and the dependencies were merged from both branches. Make sure to ensure the JSON format is valid after resolving conflicts, as incorrect syntax can lead to errors in your application.

By following these guidelines and practices, you can navigate and resolve merge conflicts in Git more effectively, maintaining a clean and efficient workflow within your projects.

Graphite
Git stacked on GitHub

Stacked pull requests are easier to read, easier to write, and easier to manage.
Teams that stack ship better software, faster.

Or install our CLI.
Product Screenshot 1
Product Screenshot 2