Git and Github, Part 2: Conflicts#
OK, now it’s time to learn about one of the best and worst features of git: conflicts!
First, let’s set the stage with a discussion of the problem git is designed to address. Most of us have had the experience of using a service like Dropbox (or Box) and having had more than one person make edits to a shared document at the same time. When that happens, the result is the appearance of a dreaded duplicate of the file with a new file name suffix: “Conflicted Copy”.
Conflicted Copies occur because when two people make edits to the same document at the same time, Dropbox has no idea what edit should take precedence. So rather than solve the problem, it just throws up its hands and says “Pfffft… you deal with it! Here’s one file with one set of edits and another with the other.”
This is a pain for a number of reasons, but the biggest is that Dropbox doesn’t give you any guidance as to how the two documents are actually different! Were the changes mutually exclusive (e.g., both editors changed the title at the same time, but to different things), or were the editors making mutually compatible edits (one person was editing the introduction while the other was editing the conclusion)? How can we know?
How Git Handles Conflicts#
Git addresses conflicts very differently.
First, git doesn’t think about conflicts at the level of documents — it thinks about conflicts at the level of individual lines of text. So if you and your co-author are both editing a LaTeX .tex file at the same time, and one of you is working on the intro and the other is working on the conclusion, no problem! Those are separate lines of the document, so in git’s view no conflict has actually occurred. Git just uses the most recent edits from both of you (where “most recent” is defined in terms of where edits appear in the git commit history).
OK, but what if two people edit the same line at the same time. For example, suppose we had a document titled “GDP and Infant Mortality” (in other words, that was the first line of the document). Now suppose that at the same time:
Computer A changes the title to “GDP & Infant Mortality” (adds an ampersand instead of “and”), and
Computer B changes the title to “Gross Domestic Product and Infant Mortality” (spells out GDP).
Now what?
Well, the first thing to note is that in git, it’s impossible for things to happen at the same time because git isn’t constantly trying to sync files. Instead, one of our users will commit and push their changes first (let’s suppose that’s Computer A). Their edit will then appear on Github, and they will see no edits.
Then, when Computer B tries to push their edits, they will get the following error:
➜ gdprepo git:(main) ✗ git add .
➜ gdprepo git:(main) ✗ git commit -m"update readme"
[main 50e1076] update readme
1 file changed, 1 insertion(+), 2 deletions(-)
➜ gdprepo git:(main) git push
To github.com:nickeubank/gdprepo.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'github.com:nickeubank/gdprepo.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
➜ gdprepo git:(main)
That alone just means that changes have happened to the data on github, not that there’s a conflict. But one then runs git pull, they get:
➜ gdprepo git:(main) git pull
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0), pack-reused 0 (from 0)
Unpacking objects: 100% (3/3), 956 bytes | 239.00 KiB/s, done.
From github.com:nickeubank/gdprepo
2219ad6..8464c84 main -> origin/main
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
➜ gdprepo git:(main) ✗
Resolving a Conflict#
As you can see, git has identified a conflict in README.md! But… it wants me to fix it. How do I do that?
What git has done is actually modified README.md to include BOTH versions — the version created on Computer A, and the version that was created on Computer B that it found on github. As a result, if you open README.md, you’ll find it now looks something like this:
<<<<<<< HEAD
# GDP & Infant Mortality
=======
# Gross Domestic Product and Infant Mortality
>>>>>>> 8464c843db7c51305824a9a4ea939f8d3a062b85
Above the ===== characters is the version you created on Computer A, and below is the version it found on github from Computer B. Note this is JUST text it’s added to the document. Some editors (like VS Code) recognize what it means and may overlay buttons, but at the end of the day it’s just put in some text. Your job is to make the document look the way you want — pick one version and delete the other, or write something new entirely! — then do precisely what you are used to doing: git add ., git commit -m"resolve title conflict", git push.
And that’s it.
So let’s have you induce a conflict and resolve it now.
Exercise 1#
On both Computer A and Computer B, run git pull to ensure you both have the same content on your local computers to start with.
Exercise 2#
On Computer A, add some text to README.md at line 3. Say “By “ and your name. Then add, commit, and push.
Exercise 3#
Now on Computer B add some text to README.md at line 3. Say “By “ and your name. Then add, commit, and push.
(Note that even though your edits aren’t happening at the same moment, because you didn’t run git pull to synchronize your local files with what’s on github after Computer A pushed their changes, you’re making edits at the same place in the repository’s commit history as Computer A was editing in Exercise 2.)
Since Computer A’s changes are on github, you should get an error saying you’re behind the “remote” (github). So the first thing you should do is run git pull. Note that this error can arise whether there are conflicts or not if you’re behind what’s on github.
Default Pull Settings#
You MAY get the following error, depending on what version of git you’ve installed:
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
If you do, type git config --global pull.rebase false just to make that the default and hit enter. Then do git pull again.
Exercise 4#
When the pull runs, you should get an error saying there’s a conflict in README.md.
Open README.md in your editor. What do you see?
Rather than picking one choice or the other, why don’t you write a new line that says “By [your name] and [your partner’s name].” Then add, commit, and push.
Congrats! You fixed your first conflict!