amgit

Modified version of Pro Git

View the Project on GitHub AllenDowney/amgit

Git Merge Conflicts with Minimal Pain

The setup

Suppose you are working on a software project with one other person. You and your partner share a repository on GitHub; you have a local copy of the repo on your laptop, and your partner has a local copy on their laptop.

Initially, there is a file named README.md in the repo, your local copy, and your partner’s local copy; and all three versions are identical.

Now suppose you and your partner want to revise this file. There are several ways you might collaborate:

(1) Time sharing: You could talk to each other and agree that you will not edit the same file at the same time. Instead, one of you will edit the file in your local copy, push the change to the repo, and then tell your partner that you are done. The other partner will pull the modified file, make additional changes, and then push them back to the repo.

(2) Space sharing: You could work on separate files at the same time. Each of you could edit their own files, push changes to the repo, and pull the other partner’s changes from the repo.

(3) [NOT RECOMMENDED] Work in branches: Before you start making changes, you could create a branch, push changes to the branch, and then work on merging the branches in the future.

(4) [NOT RECOMMENDED] Work on the same files, in the same branch, at the same time.

For people who are relatively new to Git, working on relatively small projects, and working part time, we STRONGLY recommend choosing option (1) or (2).

People with more Git experience will try to make you feel bad about these options, and they will badger you into choosing option (3). Ignore them.

However, even if you intend to choose (1) or (2), you are likely to make a mistake at some point and accidentally choose (4). In that case, you are likely to encounter a merge conflict.

What is a merge conflict?

In this document, we use “merge conflict” to refer to any scenario where you and your partner have changed the same file and you need to reconcile your changes.

With Jupyter notebooks, in particular, you should be aware that running a notebook generally modifies the contents, which can cause a merge conflict.

We’ll consider two scenarios, depending on whether the change you have made has been committed.

Scenario I: Local change, not committed

Suppose your partner has modified README.md and pushed the change to the repo. In your local copy, you have also modified README.md, but you have not yet committed the change. Let’s see what happens, and what you can do to resolve it.

1) Clone a repo on GitHub to make a local copy. If you have a partner, have them clone a local copy, too.

2) Tell your partner to edit README.md, commit the change, and push it to the repo.
Or, if you are working alone, you can simulate the same effect by viewing the repo on GitHub, editing README.md, making a small change, and committing the change.

3) In your local copy, edit README.md and save the change but don’t commit it. If you run git status, you should see:

modified:   README.md

4) If you run git pull, you should see:

error: Your local changes to the following files would be overwritten by merge:
	README.md
Please, commit your changes or stash them before you can merge.

This is a merge conflict. There are several ways you might proceed, depending on what changes you and your partner have made.

A: If you don’t care about the changes you have made in your local copy, the easiest thing to do is delete the file that would be overwritten by the merge. Try this:

$ rm README.md
$ git pull

The git pull should succeed now: it should pull the modified version of README.md from the repo, along with any other changes that are in the repo but not in your local copy.

B: If you don’t care about the changes your partner made and you want to replace them with your changes, the easiest thing to do is upload your modified file to GitHub. View your repo in a web browser, navigate to the directory that contains the conflicted file, and press the “Upload files” button. Navigate to your modified file and upload it. Now delete your local copy of the file and git pull again.

B2: Again, if you don’t care about the changes your partner made and you want to replace them with your changes, you can

$ mv README.md README.md.mine
$ git pull
$ mv README.md.mine README.md

And then add README.md, commit and push.

C: If there are changes in both versions that you want to keep, the easiest thing to do is merge them by hand. You can do that by running

$ mv README.md README.md.mine
$ git pull
$ meld README.md README.md.mine

meld is a graphical program that compares files and makes it easy to merge changes from the two of them until you have what you want.
If you don’t already have it installed, run

$ sudo apt-get install meld

Or use whatever text editor you like to move the changes you want to keep from README.md.mine to README.md.

Then add, commit, and push the merged version of README.md as usual.

Scenario II: Local change, committed

Suppose your partner has modified README.md and pushed the change to the repo. In your local copy, you have also modified README.md, and you have committed the change.
Let’s see what happens, and what you can do to reconcile it.

1) Clone a repo on GitHub to make a local copy. If you have a partner, have them clone a local copy, too.

2) Tell your partner to edit README.md, commit the change, and push it to the repo. Or, if you are working alone, you can simulate the same effect by viewing the repo on GitHub, editing README.md, making a small change, and committing the change.

3) In your local copy, edit README.md, save the change, and commit it. If you run git status, you should see:

On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

4) Run git push. You should see

! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'https://github.com/AllenDowney/blair-walden-project.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.

Before you can push your changes to the repo, you have to pull the changes from the repo, resolve any conflicts, and then push your changes back to the repo.

5) Run git pull. You should see

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

This is the first example we have seen that is really a merge conflict in the sense that Git is aware that there is a conflict. If you run git status, you should see

both modified:      README.md

6) Edit README.md. You will see that Git has combined the changes from the repo and your local copy:

<<<<<<< HEAD
text you added to your local copy
=======
text your partner pushed to the repo
>>>>>>> 98663bb97c1053ed8cc0cc3961a16972a2656e4f

In this example, HEAD refers to the most recent commit in your local copy, and the long hexadecimal string refers to the commit in the repo.

You can use any text editor to edit this file and resolve the conflict as you see fit. Save the resolved file.

Now you should be able to add, commit, and push the resolved file.

7) As an alternative to Step 6, you can use git mergetool. First, configure git to use meld, rather than whatever inchoate beast from the depths it uses by default:

$ git config merge.tool meld

Then run git mergetool. You should see:

Merging:
README.md

Normal merge conflict for 'README.md':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (meld):

If you hit return, it should launch meld and show you three versions of your file. Edit until the local version is what you want, then add, commit, and push it.