Table of Contents
Introduction
(Edited: 17-10-2024)
Have you ever found yourself in a situation where you’re working on a Git repository locally, but when pushing to remote, you find that the remote repository is a few commits ahead? If you forget to pull from the remote repository and make changes locally, you might end up creating a conflict. A Git merge conflict occurs only if both repositories adjust the same file. In this post, we’ll show you how to resolve Git merge conflicts. There are multiple ways to do it, but we’ll focus on one method in particular.
Commit
A commit is a snapshot of your project at a specific point in time. Everything that git tracks will be stored in this commit. Each commit will get an unique identifier.
Before you can commit, you need to stage the changes that you want to commit. You can do this with the git add command.
# stage everything that has altered after the last commit
git add .
With git commit you can actually commit the changes into a snapshot which will be stored locally.
git commit -m "Commit message which is custom"
Branch
A branch creates a parallel set of commits. When creating a new branch with git branch <branch_name> the new branch will get a copy of the latest commit of the current branch that you’re on.
# create a branch named development
git branch development
# make this branch the active branch
git checkout development
# or, alternatively, create and checkout a new branch at the same time
git checkout -b development
Terms and analogies
Git term | Analogy |
---|---|
Git repository | A building, like an office building or a barn where you store your work. This is space on your hard drive. |
Stage | A preview of the work that has changed, before committing. |
Commit | A snapshot of work or a set of changes of work. Imagine you’re writing a book at a desk. Each commit would be a saved copy of a specific state of your entire desk. So a copy of the old desk including the new changes at your desk will be stored in the building. |
Main branch | An array of desks named “Main” where you work on your project. Or more precisely, this would be the array with snapshots (see: commit) of desks named Main. |
Branch | A new array of snapshotted desks with a specific name. |
Remote repository | The project on a remote location where others can access it as well. |
File | A page of the book you’re writing, which does not have the physical limitation of a real piece of paper. |
Directory | A chapter of the book, where specific content is organized. |
Example
You can see a Git repository as the boundaries of your project. The project happens inside the building. Your local Git repository is your local building with desks that have versions of the project. Git tracks changes in your project automatically.
The remote repository is (usually) an online repository where all team members of the same project can access the project. This can be seen as the main building, the headquarters. Work is shipped to and from the headquarters and synced with your local building (in the analogy), your local computer.
Any file on your computer is like a page in a book. If your project has image files or other content as well, think of it as a scrapbook. You can see a directory as a chapter of a book, an organized section.
With these basic terms defined, we can there now consider the merge conflict with the analogy. When the git pull origin main command is run, we are copying and shipping the work from a specific branch of the headquarters and try to merge it with our own version of that branch. A truck at the headquarters is loaded with desks of a specific branch and drives to your building. When there is no conflict, your main branch will now be synced with the version that came from the headquarters.
The analogy of a merge conflict
Imagine that you have a commit (desk) with updated work in your building (repository) which the headquarters (remote repository) does not yet have. If you look at the image below, that latest commit named “B” is not in the remote repository. The remote repository on the other hand, received a shipment from another local repository (of a team member) with several commits. When you try to merge remote commit “d” with your local commit “B“, a merge conflict happens when the same lines of work in files have changed. Git cannot know which lines it should keep. Git can automatically merge files where only different parts of the file have been edited.
The Flow
# This would give no problems if only different lines in files were affected
git pull origin main
BashWith the git pull origin main command we are fetching the last updates of the book from the online headquarters, and we put them in our building. This is where the git merge conflict might occur.
Configurations
Before we continue to resolve the merge conflict, we are going to set a default editor and setup a merge tool. In this example we are going to use KDiff3 (also mentioned in the open-source alternatives), install this (or any other merge tool) on your system if you haven’t already.
# Setup a text editor accordingly
git config --global core.editor notepad
# Open the config file
git config --global -e
BashWe can now add KDiff3 as the merge (and diff) tool more easily. Add the following to your Git config file and alter paths accordingly. The trustExitCode = false line will make Git use timestamps instead of the exit code to determine if a merge or diff operation was successful. The forward slashes are correct here if your git bash terminal uses the Unix conventions for directory separators.
[merge] tool = kdiff3 [mergetool "kdiff3"] path = C:/Program Files/KDiff3/kdiff3.exe trustExitCode = false [diff] guitool = kdiff3 [difftool "kdiff3"] path = C:/Program Files/KDiff3/kdiff3.exe trustExitCode = false
The Flow (continued)
# Resolve the merge conflict with the mergetool you configured
git mergetool
BashIf everything went well, KDiff3 should now open. At the top, three screens will open: Base, Local and Remote. Base is the first common ancestor, which means the latest commit where both versions were still in sync. Local is your local state of a file. Remote is the state of the file from the remote repository.
For each conflict in the file, you decide from which file the content should be kept. After resolving every conflict, you can close KDiff3 (save when asked), and Git will open the next conflict in the editor to resolve.
Conclusion
After you have resolved all merge conflicts you can commit the merge session and push it to remote. If you have stashed changes away, you can make them undone now. If you would like a more detailed, step by step way to resolve a Git merge conflict with the help of KDiff3, you can continue here.
Some merge problems are harder to solve. Another way to resolve your merge conflict is to just clone the remote repository to a new directory on your system and then manually add your local changes to it.