Conflicts

Last updated on 2025-03-12 | Edit this page

Overview

Questions

  • What do I do when my changes conflict with someone else’s?

Objectives

  • Explain what conflicts are and when they can occur.
  • Resolve conflicts resulting from a merge.

As soon as people start working in parallel, they’ll likely step on each other’s toes. This will even happen with a single person: if we are working on a piece of software on both our laptop and a server in the lab, we could make different changes to each copy. Version control helps us manage these conflicts by giving us tools to resolve overlapping changes. You will encounter conflicts no matter which branching model you choose. No model is more or less likely to produce conflicts. No model will make conflicts easier (or harder) to resolve. Using forks has no impact on how likely conflicts are to occur.

To learn how to resolve conflicts we are going to create one on purpose. We will do this by getting everyone to edit the same line of the same file at once. Open an Issue on the git-training-demo repository for adding yourself to the list of authors in the CITATION.cff file. Make a note of the Issue number to use as the prefix for your feature branch name.

Make sure you are on the main branch. Create your feature branch:

BASH

$ git switch -c 7_add-citation-fitzroy

OUTPUT

Switched to a new branch '7_add-citation-fitzroy'

Add your name to the CITATION.cff file, underneath any existing author names:

BASH

$ nano CITATION.cff
$ cat CITATION.cff

OUTPUT

cff-version: 1.2.0
message: "Met Office Colleagues and Partners"
authors:
- family-names: "Theodorakis"
  given-names: "Dimitrios"
  orcid: "https://orcid.org/0000-0001-9288-1332"
- family-names: "FitzRoy"
  given-names: "Robert"
title: "Met Office Git Training Demo"
version: 2.0.4
doi: 10.4321/zenodo.1234
date-released: 2024-09-23
url: "https://github.com/MetOffice/git-training-demo"

Add and commit your changes:

BASH

$ git add CITATION.cff
$ git commit -m "Adds Robert Fitzroy as an author"

OUTPUT

[7_add-citation-fitzroy a3c5e13] "Adds Robert Fitzroy as an author"
 1 file changed, 2 insertions(+)

Push your changes to your GitHub fork:

BASH

$ git push

OUTPUT

Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 354 bytes | 354.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:mo-fitzroy/git-training-demo.git
   f87bb5c..a3c5e13  7_add-citation-fitzroy -> 7_add-citation-fitzroy
branch '7_add-citation-fitzroy' set up to track 'origin/7_add-citation-fitzroy'.

Open a Pull Request


Head back to your fork on GitHub and open a PR to contribute your changes upstream to the main git-training-demo repository. Fill out the template like you did in the previous episode.

At the bottom of your PR GitHub is telling us This branch has conflicts that must be resolved and the conflicting file is CITATION.cff.

Go to the main git-training-demo repositories code tab and look at the contents of CITATION.cff:

OUTPUT

cff-version: 1.2.0
message: "Met Office Colleagues and Partners"
authors:
- family-names: "Theodorakis"
  given-names: "Dimitrios"
  orcid: "https://orcid.org/0000-0001-9288-1332"
- family-names: "Hogan"
  given-names: "Emma"
title: "Met Office Git Training Demo"
version: 2.0.4
doi: 10.4321/zenodo.1234
date-released: 2024-09-23
url: "https://github.com/MetOffice/git-training-demo"

Someone else has added their name before our PR could be merged. These changes now conflict with the one you made.

If you’re working through this lesson on your own, you won’t see a conflict. You should still follow the materials to learn what to do when you encounter a conflict.

Blame

GitHub lets you see who made changes to the file you are looking at in the Code viewer. Click on the Blame button:

A screenshot of a users weather repository showing blame on the forecast.md file in the Code viewer.

The image above shows blame on the weather repositories forecast.md file. The far left shows how long ago the commit was that changed those lines. Then the commit message is displayed before the file contents. Click on the little page icon after a commit message to see previous commits which altered the same lines. Try using blame on the CITATION.cff file to see who added the conflicting change.

Compare changes

In the git-novice lesson we learnt how to diff between commits on the same branch. You can also look at diffs between branches even if they are on a different fork using GitHub.

To see a difference for a repository add /compare to the end of the repository url.

  1. Navigate to: https://github.com/MetOffice/git-training-demo/compare/, and click on the compare across forks link.
  2. Click on the head repository dropdown and find your fork.
  3. Click on the compare dropdown and select your feature branch.

A diff should appear comparing your fork’s feature branch with the upstream repositories main branch.

Resolving Conflicts


We’re going to resolve the conflict by merging in the main branch into our feature branch 7_add-citation-fitzroy:

gitGraph
    accDescr {A git graph showing the main branch being merged
    into the 7_add-citation-fitzroy branch to resolve merge conflicts.}
    commit id: 'cdb7fa6'
    branch 7_add-citation-fitzroy
    commit id: 'a3c5e13 Adds Robert Fitzroy as an author'
    checkout main
    commit id: 's7dja9o'
    checkout 7_add-citation-fitzroy
    merge main

First we need to make sure our fork’s main branch is in sync with the upstream repository. Navigate to your fork on GitHub and click on the Sync fork button.

In your local copy of your fork, update the main branch:

BASH

$ git switch main
$ git pull

Switch back to the feature branch:

BASH

$ git switch 7_add-citation-fitzroy

OUTPUT

Switched to branch '7_add-citation-fitzroy'

Merge in the main branch:

BASH

$ git merge main

OUTPUT

Auto-merging CITATION.cff
CONFLICT (content): Merge conflict in CITATION.cff
Automatic merge failed; fix conflicts and then commit the result.

Git is warning us about the conflict. We are still merging, if you run git status you will see:

BASH

$ git status

OUTPUT

On branch 7_add-citation-fitzroy
Your branch is up to date with 'origin/7_add-citation-fitzroy'.

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   CITATION.cff

no changes added to commit (use "git add" and/or "git commit -a")

The message tells us both modified the CITATION.cff file since both main and our feature branch modified this file.

If we look at the CITATION.cff file now:

BASH

$ cat CITATION.cff

OUTPUT

cff-version: 1.2.0
message: "Met Office Colleagues and Partners"
authors:
- family-names: "Theodorakis"
  given-names: "Dimitrios"
  orcid: "https://orcid.org/0000-0001-9288-1332"
<<<<<<< HEAD
- family-names: "FitzRoy"
  given-names: "Robert"
=======
- family-names: "Hogan"
  given-names: "Emma"
>>>>>>> dabb4c8c450e8475aee9b14b4383acc99f42af1d
title: "Met Office Git Training Demo"
version: 2.0.4
doi: 10.4321/zenodo.1234
date-released: 2024-09-23
url: "https://github.com/MetOffice/git-training-demo"

Our change is preceded by <<<<<<< HEAD. Git has then inserted ======= as a separator between the conflicting changes and marked the end of the content downloaded from GitHub with >>>>>>>. (The string of letters and digits after that marker identifies the commit we’ve just downloaded.)

It is now up to us to edit this file to remove these markers and reconcile the changes. We can do anything we want: keep the change made in the local repository, keep the change made in the remote repository, write something new to replace both, or get rid of the change entirely. Let’s replace both so that the file looks like this:

BASH

$ cat CITATION.cff

OUTPUT

cff-version: 1.2.0
message: "Met Office Colleagues and Partners"
authors:
- family-names: "Theodorakis"
  given-names: "Dimitrios"
  orcid: "https://orcid.org/0000-0001-9288-1332"
- family-names: "Hogan"
  given-names: "Emma"
- family-names: "FitzRoy"
  given-names: "Robert"
title: "Met Office Git Training Demo"
version: 2.0.4
doi: 10.4321/zenodo.1234
date-released: 2024-09-23
url: "https://github.com/MetOffice/git-training-demo"

To finish merging, we add CITATION.cff to the changes being made by the merge and then commit:

BASH

$ git add CITATION.cff 
$ git status

OUTPUT

On branch 7_add-citation-fitzroy
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

	modified:   CITATION.cff

BASH

$ git commit

OUTPUT

[7_add-citation-fitzroy 312c561] Merge branch 'main' into 7_add-citation-fitzroy

Now we can push our changes to GitHub:

BASH

$ git push

OUTPUT

Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 385 bytes | 128.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:mo-fitzroy/git-training-demo.git
   a3c5e13..312c561  7_add-citation-fitzroy -> 7_add-citation-fitzroy

Your PR on GitHub should now be ready to merge, after review of course.

Avoiding Conflict


Git’s ability to resolve conflicts is very useful, but conflict resolution costs time and effort, and can introduce errors if conflicts are not resolved correctly. If you find yourself resolving a lot of conflicts in a project, consider these technical approaches to reducing them:

  • Pull from upstream more frequently, especially before starting new work
  • Use topic branches to segregate work, merging to main via a PR when complete
  • Make smaller more atomic commits
  • Push your work when it is done and encourage your team to do the same to reduce work in progress and, by extension, the chance of having conflicts
  • Where logically appropriate, break large files into smaller ones so that it is less likely that two authors will alter the same file simultaneously

Conflicts can also be minimized with project management strategies:

  • Clarify who is responsible for what areas with your collaborators
  • Discuss what order tasks should be carried out in with your collaborators so that tasks expected to change the same lines won’t be worked on simultaneously
  • If the conflicts are stylistic churn (e.g. tabs vs. spaces), establish a project convention that is governing and use code style tools (e.g. htmltidy, perltidy, rubocop, etc.) to enforce, if necessary

Conflicts on Non-textual files

What does Git do when there is a conflict in an image or some other non-textual file that is stored in version control?

Let’s try it with your weather repository. Suppose you take a picture of the television forecast and call it forecast.jpg. Jimmy has already created a feature branch to add the image in.

BASH

$ git switch add_forecast_image

If you do not have an image file of forecast available, you can create a dummy binary file like this:

BASH

$ head --bytes 1024 /dev/urandom > forecast.jpg
$ ls -lh forecast.jpg

OUTPUT

-rw-r--r-- 1 mo-eormerod 57095 1.0K Mar  8 20:24 forecast.jpg

ls shows us that this created a 1-kilobyte file. It is full of random bytes read from the special file, /dev/urandom.

Now, suppose you add forecast.jpg to your repository:

BASH

$ git add forecast.jpg
$ git commit -m "Add picture of forecast"

OUTPUT

[add_forecast_image 8e4115c] Add picture of forecast
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 forecast.jpg

Suppose that Jimmy has added a similar picture in the meantime. His is a picture of a forecast from Chile, but it is also called forecast.jpg. When you try to push, you get a familiar message:

BASH

$ git push

OUTPUT

To https://github.com/mo-eormerod/weather.git
 ! [rejected]        add_forecast_image -> add_forecast_image (fetch first)
error: failed to push some refs to 'https://github.com/mo-eormerod/weather.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

We’ve learned that we must pull first and resolve any conflicts:

BASH

$ git pull

When there is a conflict on an image or other binary file, git prints a message like this:

OUTPUT

$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/mo-eormerod/weather.git
 * branch            add_forecast_image     -> FETCH_HEAD
   6a67967..439dc8c  add_forecast_image     -> origin/add_forecast_image
warning: Cannot merge binary files: forecast.jpg (HEAD vs. 439dc8c08869c342438f6dc4a2b615b05b93c76e)
Auto-merging forecast.jpg
CONFLICT (add/add): Merge conflict in forecast.jpg
Automatic merge failed; fix conflicts and then commit the result.

The conflict message here is mostly the same as it was for forecast.md, but there is one key additional line:

OUTPUT

warning: Cannot merge binary files: forecast.jpg (HEAD vs. 439dc8c08869c342438f6dc4a2b615b05b93c76e)

Git cannot automatically insert conflict markers into an image as it does for text files. So, instead of editing the image file, we must check out the version we want to keep. Then we can add and commit this version.

On the key line above, Git has conveniently given us commit identifiers for the two versions of forecast.jpg. Our version is HEAD, and Jimmy’s version is 439dc8c0.... If we want to use our version, we can use git checkout:

BASH

$ git checkout HEAD forecast.jpg
$ git add forecast.jpg
$ git commit -m "Use image of just the local forecast"

OUTPUT

[add_forecast_image 21032c3] Use image of just the local forecast

If instead we want to use Jimmy’s version, we can use git checkout with Jimmy’s commit identifier, 439dc8c0:

BASH

$ git checkout 439dc8c0 forecast.jpg
$ git add forecast.jpg
$ git commit -m "Use image of just the local forecast"

OUTPUT

[add_forecast_image da21b34] Use image of forecast with nachos instead of just forecast

We can also keep both images. The catch is that we cannot keep them under the same name. But, we can check out each version in succession and rename it, then add the renamed versions. First, check out each image and rename it:

BASH

$ git checkout HEAD forecast.jpg
$ git mv forecast.jpg forecast-only.jpg
$ git checkout 439dc8c0 forecast.jpg
$ mv forecast.jpg forecast-chile.jpg

Then, remove the old forecast.jpg and add the two new files:

BASH

$ git rm forecast.jpg
$ git add forecast-only.jpg
$ git add forecast-chile.jpg
$ git commit -m "Use two images: local forecast and Chile forecast"

OUTPUT

[add_forecast_image 94ae08c] Use two images: local forecast and Chile forecast
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 forecast-chile.jpg
 rename forecast.jpg => forecast-only.jpg (100%)

Now both images of forecast are checked into the repository, and forecast.jpg no longer exists.

Key Points

  • Conflicts occur when two or more people change the same lines of the same file.
  • The version control system does not allow people to overwrite each other’s changes blindly, but highlights conflicts so that they can be resolved.