Conflicts

Overview

Teaching: 15 min
Exercises: 0 min
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 can work in parallel, it’s likely someone’s going to step on someone else’s toes. This will even happen with a single person: if you are updating a file on two computers or two branches, you could make different changes to each copy. Version control helps us manage these conflicts by giving us tools to resolve overlapping changes.

Even if you prefer working on the command line, this is one area where you might want to consider using a GUI tool. Command line Git is not great at resolving merge conflicts. So that you can decide for yourself, we have included both the command line instructions and an illustration of resolving a merge conflict using GitKraken below.

To see how we can resolve conflicts, we must first create one. So that you can practice resolving merge conflicts on your own, you are going to create a conflict between two branches in your local repository. However, you could also test this out by having both you and a collaborator make a change to the same file in a remote repository.

To start, create a new branch in your local repository called update-plan. The file README.md currently looks like this in both branches of your repository.

# about your new image collection

The `cats-human-situations.csv` file contains metadata for three image objects.
The original metadata from the source institutions has been abbreviated and made
messier so you have something to clean up!

The images are in the `images/` subdirectory of this repository.

Cleanup plan:
- Get rid of all caps in titles
- Standardize dates
- Standardize dimensions

Let’s add a line to the copy in master branch. Open README.md in your text editor and then add the following text at the bottom of the file:

- Reconcile subjects

Save the file and commit your changes.

$ git add README.md
$ git commit -m "add subject reconciliation to plan"
[master 9025b6e] add subject reconciliation to plan
 1 file changed, 1 insertion(+), 1 deletion(-)

Now let’s switch to the update-plan branch and make a different change to that copy of the README.md without incorporating the change from the master branch first.

$ git checkout update-plan
Switched to branch 'update-plan'

Open README.md in your text editor and then add the following text at the bottom of the file:

- Add access permissions

Save the file and commit your changes. Notice that it was no problem to commit the changes on the update-plan branch, but what happens if we try and merge the two?

First, switch back to master branch.

$ git checkout master
Switched to branch 'master'

Now let’s try to merge the update-plan branch.

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

The Conflicting Changes

Git tells us there is a conflict, and marks that conflict in the affected file. Git detects that the changes made in one copy overlap with those made in the other and stops us from trampling on our previous work.

Open README.me in your text editor.

# about your new image collection

The `cats-human-situations.csv` file contains metadata for three image objects.
The original metadata from the source institutions has been abbreviated and made
messier so you have something to clean up!

The images are in the `images/` subdirectory of this repository.

Cleanup plan:
- Get rid of all caps in titles
- Standardize dates
- Standardize dimensions
<<<<<<< HEAD
- Reconcile subjects.
=======
- Add access permissions
>>>>>>> update-plan

Our change on the master branch, the one in HEAD—is preceded by <<<<<<<. Git has then inserted ======= as a separator between the conflicting changes and marked the end of the content from the update-plan branch with >>>>>>> followed by the name of the branch.

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 master, keep the change made in update-plan, write something new to replace both, or get rid of the change entirely. Since these are both tasks we want to do, let’s keep them both. Update the file so it looks like this:

# about your new image collection

The `cats-human-situations.csv` file contains metadata for three image objects.
The original metadata from the source institutions has been abbreviated and made
messier so you have something to clean up!

The images are in the `images/` subdirectory of this repository.

Cleanup plan:
- Get rid of all caps in titles
- Standardize dates
- Standardize dimensions
- Reconcile subjects.
- Add access permissions

To finish merging, we add save and close the file. Then add README.md to the changes being made by the merge:

git add README.md

Let’s use git status to see what is going on.

$ git status
On branch master
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

	modified:   README.md

Now let’s commit those changes to complete the merge.

git commit -m "Merge changes from update-plan branch"
[master 7bc42af] Merge changes from update-plan branch

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:

Conflicts can also be minimized with project management strategies:

Resolving Merge Conflict with GUI

Let’s see how one would resolve the same merge conflict using GitKraken.

GitKraken demo

Conflicts When Working with Remotes

If your merge conflict is with a remote repository because you are attempting to push changes prior to pulling in new changes from the remote, you will receive a message similar to this:

$ git push origin master
To https://github.com/ccline/cats-as-data.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/ccline/cats-as-data.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

What you have to do in this situation is pull the changes from the remote, merge them into your local copy, and then push that. Start by pulling:

$ git pull origin master
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 3 (delta 1)
Unpacking objects: 100% (3/3), done.
From https://github.com/ccline/cats-as-data
 * branch            master     -> FETCH_HEAD
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

git pull tells us there’s a conflict, and marks that conflict in the affected file the same way we saw above when we tried to merge our two local branches. To fix you would repeat the same steps you followed above.

First you would open the file in your text editor and remove the markers and reconcile the changes. You’d then finish merging by adding and committing those changes.

git add README.md

git commit -m "Merge changes from remote"
[master 7bc42af] Merge changes from remote

Finally, you’d push those changes back to the remote repository.

$ git push origin master
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 697 bytes, done.
Total 6 (delta 2), reused 0 (delta 0)
To https://github.com/ccline/cats-as-data.git
   dabb4c8..2abf2b1  master -> master

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?

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

$ git pull origin master
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/ccline/cats-as-data.git
* branch            master     -> FETCH_HEAD
6a67967..439dc8c  master     -> origin/master
warning: Cannot merge binary files: 822811.jpg (HEAD vs. 439dc8c08869c342438f6dc4a2b615b05b93c76e)
Auto-merging 822811.jpg
CONFLICT (add/add): Merge conflict in 822811.jpg
Automatic merge failed; fix conflicts and then commit the result.

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

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

Git cannot automatically insert conflict markers into a non-text file as it does for text files. So, instead of editing the 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 822811.jpg. Our version is HEAD, and our Collaborator’s version is 439dc8c0.... If we want to use our version, we can use git checkout:

$ git checkout HEAD 822811.jpg
$ git add 822811.jpg
$ git commit -m "Use un-cropped version image instead of cropped"
[master 21032c3] Use un-cropped version image instead of cropped

If instead we want to use our Collaborator’s version, we can use git checkout with the Collaborators’s commit identifier, 439dc8c0:

$ git checkout 439dc8c0 822811.jpg
$ git add 822811.jpg
$ git commit -m "Use cropped version instead of un-cropped"
[master da21b34] Use cropped version instead of un-cropped

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:

$ git checkout HEAD 822811.jpg
$ git mv 822811.jpg 822811-uncropped.jpg
$ git checkout 439dc8c0 822811.jpg
$ mv 822811.jpg 822811-cropped.jpg

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

$ git rm 822811.jpg
$ git add 822811-uncropped.jpg
$ git add 822811-cropped.jpg
$ git commit -m "Use two images: cropped and un-cropped"
[master 94ae08c] Use two images: cropped and un-cropped
2 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 822811-uncropped.jpg
rename 822811.jpg => 822811-cropped.jpg (100%)

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

Key Points

  • Conflicts occur when two or more people change the same file(s) at the same time.

  • The version control system does not allow people to overwrite each other’s changes blindly, but highlights conflicts so that they can be resolved.