Growing with Git — Merge conflicts

Introduction

Alright, guys — now we’re really starting to gain some traction with Git. In our last tutorial, we overcame our fears of using the Git Bash tool to pull a repository from GitHub, make changes within our local repository, and push the changes back up to our repo. At the end of the tutorial, we touched on one issue: conflicting changes. Let’s spend a bit of time addressing how these arise, and how we can fix them. Onward and upward, friends!

A pull for consistency

One of the amazing features of Git is the ability for multiple people to work on the same project at the same time. So let’s say that you now bring on a colleague to help you develop your software. Since you work at difference paces, your colleague also clones your latest changes to their local machine. To test out the Git process flow, they also update the README.md file and push to the main repository — now your files are outdated!

Not to worry! This is where Git’s pull command comes in handy. To mimic their work, we’ll go into the GitHub repository and manually add some text to the README file by clicking the Edit button (which looks like a pencil), making a change, and committing the change to the README file at the bottom of the screen. I simply added the line, “I’m your colleague. I updated!”.

Now, we can head back over to the Git Bash and enter the command to update (or pull) our repository from the main branch: git pull origin main. Upon execution, the changes from the GitHub repository are pulled and integrated into our existing files. The change is immediate, as shown below:

Now, we’ve incorporated our colleague’s changes and we can continue editing with the newest changes.

Dealing with conflict: complementary changes

Of course, there other considerations. What happens if your colleague is working on the same script, and pulling the script updates your own? For single developers (me), this is rarely a problem I need to worry about. However, in the case it does happen, Git will let you know. For instance, let’s pick up on the same example — we’ve just pulled our colleague’s new code, and we decide to add some changes of our own. Little do we know, however, that they are also adding code to the same file.

In the same spirit as earlier, let’s make changes to the README file on both the remote and local repositories. On the remote repository, I’ve added a line, “How’s your day?”, and I’ve changed text within my local repository: “A new comment” is changed to “A better comment”.

What happens next is rather sophisticated on Git’s part. When my colleague and I have both made changes to the same document in the same repository, it recognizes the difference and asks me to simply commit (save) my changes locally. After I do so, I try to re-pull the repository, which it happily does. It then compares the two files; since our changes do not overlap, it simply makes both changes to the file. Now, both of our files are up to date and my local files show both changes.

Dealing with conflict: Conflicting changes

Great, but what happens if you and your colleague both change the same portion of the script at the same time? Let’s re-edit both the online README and the local README in the same manner. In the online repository, we’ll change “How’s your day?” to “How’s your month?”, and in the local repository, we’ll change “How’s your day?” to “How’s your year?”. Let’s save and commit both, then try to re-pull the online repo.

$ git pull origin main

From https://github.com/ajpung/GrowWithGit
 * branch            main       -> FETCH_HEAD
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

Again, Git tries to auto-merge the files, but now sees that we really do have a conflict on our hands. For that reason, the auto-merge fails and it asks us to fix the conflicts before we try to re-pull.

But what, exactly, is the issue? We could talk to our colleagues and figure out what changed, but this becomes difficult in large, complex codes and coding teams. Another option is to use Git’s diff command. Running this command tells us which lines are giving us the conflict including the offending filenames and text:

$ git diff

diff --cc README.md
index 0f378c4,c5fb307..0000000
--- a/README.md
+++ b/README.md
@@@ -5,4 -5,4 +5,8 @@@ A better comment, saved on 8/4/2022

  I'm your colleague. I updated!

++<<<<<<< HEAD
 +How's your year?
++=======
+ How's your month?
++>>>>>>> cc45ce7a2e9443400f201a900945df7858b0a51f

To resolve the conflict, we have two options. The first option is to change one of the files manually — now that we know what the change is, we can determine the more appropriate version, make the change in the associated file, save/commit the change, and re-pull.

mergetool: Configuration

The second option is to use Git’s merge tool, appropriately named mergetool. Like any tool, though, we want to make sure it is properly configured.

Our first change is going to be to configure what we see when we open the merge tool. Since we have two conflicting repositories, we expect to see both documents represented; but we actually have a third repository that would be nice to see: the original, unchanged text file, which we’ll call the “base” document. We can configure the tool to show the base document using the following command:

$ git config merge.conflictstyle diff3  

We can also determine which text editor mergetool uses when we make changes. Since Vim is a well-known editor, let’s set that as the default text editor:

$ git config merge.tool vimdiff    

And if we really wanted to, we could go one step further and remove the prompt that we see when we first call git mergetool:

$ git config mergetool.prompt false

That’s it! By issuing three simple commands, we’ve configured our merge tool. Let’s see how it works!

mergetool: Demonstration

With Git’s merge tool configured, we’ll go ahead and call the tool via git mergetool. When we run this command, we’re asked to hit Enter again to run the tool; doing so brings up the following screen

The screen is broken up into four windows. From left to right on the top row, we’re shown the local repository (what we changed), the base repository (before either changes were made), and the remote repository (changed by colleague).

On Windows, my cursor is automatically put in the fourth window — the merged version showing both of the conflicts, highlighted in blue. Now, I can move my cursor to the lines I wish to remove and simply delete the characters using the Delete key. I chose to remove the lines containing

======
How's your month?

Since we configured out merge tool to utilize Vim, I know I can save my changes using the :wqa command (for clarity, these three letters are multiple commands issued at the same time to (w) write the changes to file, (q) quit Vim, (a) all buffers).

With the conflict resolved and my changes saved, I’ll go through the same procedure we’ve seen earlier:

  • Perform a status check (git status)
  • Add/stage the changed README file (git add .)
  • Commit changes to my local repository (git commit -m "merged multiple branches")
  • Push the changes to the remote repository (git push origin main)

Conclusion

Whew! Welcome to the other side of conflict, where everything has been neatly resolved, ordered, and nicely tucked away! In this tutorial, we looked at complementary conflicts, where two dissimilar documents can happily co-exist after a merge. We also looked at conflicts that force us to make a judgement call about which changes are correct by comparing two dissimilar documents.

In this last venture, Git’s merge tool (mergetool) became very helpful, directly allowing us to compare the conflicting lines and removing the incorrect portions. After that, it was a simple matter of save, stage, commit, and push!

As I alluded to in our first Git tutorial, there is still much material to cover in learning Git. Remember! One of the most sure-fire ways to learn and build any new skillset is to frequently use it.

I really appreciate you dropping by! Please feel free to subscribe for updates, like, or comment on my material. Have a great week!

Get new content delivered directly to your inbox.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: