Conflicts

The last section went over one scenario where two different changesets are brought together. git used the 'recursive' strategy to smash the changes together and we got something called a 'merge commit' (again, more on this later).

git was able to merge the two histories together because they were mostly independent. One change was to the script word_count.py, the other was creating a file called README.md. Not much overlap there. What about if there are changes made to the same file in two places?

Time to find out!

Edit on GitHub

Edit the file word_count.py using GitHub's online editor and change the first line of the script to read:

happy = input("Enter a statement to check word frequency: ")

Commit your changes on GitHub.

Edit locally

Using nano, edit word_count.py and change the first line of the script to read:

happy = input("Enter a phrase to word count:")

Commit your changes to this file locally.

We now have changes made to the same file in two different places. What happens if we push?

$ git push
To github.com:gforsyth/wordcount.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'git@github.com:gforsyth/wordcount.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

The same thing as before -- if there are changes on a remote you have to pull them in before you can push.

$ git pull
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:gforsyth/wordcount
   2f53c34..a717ad5  master     -> origin/master
Auto-merging word_count.py
CONFLICT (content): Merge conflict in word_count.py
Automatic merge failed; fix conflicts and then commit the result.

Ahh, that's different...

git is very clever, but if you make changes to the same line in two different places, how can git know which change should take precedence? In this case, it needs human input to decide how to proceed.

We're in uncharted waters, what should we do?

When in doubt, git status.

$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
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:   word_count.py

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

status tells us that the two repositories have diverged. And we have to fix the conflicts and then run git commit.

How do we know what the conflicts are?

Resolving conflicts

git status told us that the file word_count.py is the trouble maker. Let's look at it.

$ cat word_count.py 
<<<<<<< HEAD
happy = input("Enter a phrase to word count: ")
=======
happy = input("Enter a statement to check word frequency: ")
>>>>>>> a717ad59e716184700199769a1f17b2f92874167

words = happy.split()

counts = {}
for word in words:
    counts[word] = counts.get(word, 0) + 1

print("The word frequency of your statement is: ")
print(counts)

Because git didn't know how to fix the conflict, it has highlighted the problem area for us by surrounding it with chevrons.

The two versions of the same line are separated by the =s

There are also labels telling us where each version is from: HEAD refers to the local repository, the commit hash is the commit we made on GitHub.

To resolve the conflict, open the file in nano and edit it to combine the two versions of the input statement in whatever way you think is best. Make sure to remove all of the chevrons and the equal signs when you are done.

We ended up with this:

$ cat word_count.py 
happy = input("Enter a phrase to check word frequency: ")

words = happy.split()

counts = {}
for word in words:
    counts[word] = counts.get(word, 0) + 1

print("The word frequency of your statement is: ")
print(counts)

Now that we have manually resolved the conflict, what do we do? git status told us, let's check again:

$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
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:   word_count.py

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

Right, we need to stage the changes me made.

$ git add word_count.py

And now it's time to commit!

$ git commit
Merge branch 'master' of github.com:gforsyth/wordcount

# Conflicts:
#   word_count.py
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#   .git/MERGE_HEAD
# and try again.


# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Your branch and 'origin/master' have diverged,
# and have 1 and 1 different commits each, respectively.
#   (use "git pull" to merge the remote branch into yours)
#
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#   modified:   word_count.py
#

That's different. When you git commit after resolving a conflict, git will autofill a message for you. You are welcome to use the automatic message, or replace it with something else, it's up to you.

Save and quit and we should see the following:

[master d31e6b9] Merge branch 'master' of github.com:gforsyth/wordcount

We have resolved the conflict! That wasn't so bad, was it? Now that we fixed the conflict, we just have to push it back up to GitHub.

$ git push
Counting objects: 6, done.
Delta compression using up to 12 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 608 bytes | 0 bytes/s, done.
Total 6 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:gforsyth/wordcount.git
   a717ad5..d31e6b9  master -> master