Welcome back, intrepid developer! In our previous chapters, we learned the magic of branching – how to create separate lines of development to work on features or fixes without disturbing the main codebase. We even touched upon merging, bringing those separate lines back together. But what happens when two brilliant minds (or even one mind working on two branches!) make conflicting changes to the exact same part of the same file?

That, my friend, is where merge conflicts come in. They might sound scary, but they’re a completely normal and expected part of collaborative development. This chapter is your trusty guide to understanding why conflicts happen, how Git tells you about them, and most importantly, how to confidently resolve them. By the end, you’ll not only fix conflicts but understand the underlying logic, turning a potential headache into a simple puzzle.

Ready to become a conflict resolution champion? Let’s dive in!

What Exactly is a Merge Conflict?

Imagine you and a colleague are both working on the same recipe file. You decide to change the amount of sugar from “1 cup” to “3/4 cup” for a healthier version. At the exact same time, your colleague, also trying to improve the recipe, changes the sugar amount to “1.5 cups” for extra sweetness. When you both try to submit your changes, Git, being a smart but not psychic tool, sees two different instructions for the same line. It doesn’t know which one to pick!

This exact scenario is a merge conflict. Git is designed to automatically merge changes when they don’t overlap. If you change line 5 and your colleague changes line 10 in the same file, Git can happily combine those. But when changes occur on the same lines or very close to each other, Git pauses and asks for your human intervention. It says, “Hey, I found a discrepancy here, and I need you to tell me which version is correct, or how to combine them.”

Conflicts are a sign of active development and collaboration, not a mistake! They are Git’s way of ensuring no code is accidentally overwritten and that the final merged version is exactly what the developers intend.

Setting the Stage: Creating Our First Conflict

To learn how to resolve conflicts, we first need to create one. This will give us a hands-on feel for the process. We’ll simulate a simple scenario with two “developers” (you, switching between branches).

First, let’s make sure we’re in a clean Git repository. If you’re following along from previous chapters, you might have one ready. If not, let’s quickly set one up:

  1. Create a new directory and initialize Git:

    mkdir git-conflict-demo
    cd git-conflict-demo
    git init
    

    What’s happening?

    • mkdir git-conflict-demo: Creates a new folder named git-conflict-demo.
    • cd git-conflict-demo: Changes your current directory into this new folder.
    • git init: Initializes an empty Git repository in this folder. This creates a hidden .git directory where Git stores all its version control magic.
  2. Create an initial file and commit it to our main branch:

    echo "Hello, Git World!" > hello.txt
    echo "This is the initial version of our file." >> hello.txt
    git add hello.txt
    git commit -m "Initial commit: Add hello.txt"
    

    What’s happening?

    • echo "..." > hello.txt: Creates a new file hello.txt and puts the first line of text into it.
    • echo "..." >> hello.txt: Appends the second line of text to hello.txt.
    • git add hello.txt: Stages hello.txt for the next commit.
    • git commit -m "...": Records the changes in the staged file as a new commit with a descriptive message. Our main branch now has its first commit.

Now, let’s simulate two parallel changes.

  1. Create a new branch for “Feature A”:

    git checkout -b feature-a
    

    What’s happening?

    • git checkout -b feature-a: Creates a new branch named feature-a and immediately switches your working directory to that branch. Any changes you make now will be on feature-a.
  2. Make a change on feature-a:

    Open hello.txt in your favorite text editor (like VS Code, Sublime Text, or even Notepad) and change the second line.

    Original hello.txt on feature-a:

    Hello, Git World!
    This is the initial version of our file.
    

    Change it to:

    Hello, Git World!
    This is the awesome version for Feature A.
    

    Save the file.

  3. Commit the change on feature-a:

    git add hello.txt
    git commit -m "Feature A: Updated file content"
    

    What’s happening?

    • We’ve now committed a change to hello.txt specifically on the feature-a branch.
  4. Switch back to main and make a conflicting change:

    git checkout main
    

    What’s happening?

    • git checkout main: Switches your working directory back to the main branch. Notice that hello.txt reverts to its original state before the feature-a changes. This is the power of Git!

    Now, open hello.txt again while on the main branch.

    Original hello.txt on main:

    Hello, Git World!
    This is the initial version of our file.
    

    Change the same second line to:

    Hello, Git World!
    This is the main branch's important update.
    

    Save the file.

  5. Commit the change on main:

    git add hello.txt
    git commit -m "Main: Important update for the file"
    

    What’s happening?

    • We now have two distinct commit histories. Both main and feature-a have modified the same line of hello.txt since their common ancestor. This is the perfect recipe for a conflict!

The Moment of Collision: Encountering a Conflict

Now for the grand finale – let’s try to merge feature-a into main.

git merge feature-a

You should immediately see output similar to this:

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

Whoa! Git explicitly tells us: CONFLICT (content): Merge conflict in hello.txt. This is Git’s way of saying, “Hold on, human! I need your help here.” The merge process has been paused, and it’s waiting for you to resolve the conflict.

Understanding Conflict Markers

When a conflict occurs, Git modifies the conflicted file (hello.txt in our case) by adding special markers to highlight the conflicting sections. Let’s open hello.txt again and see what Git has done:

Hello, Git World!
<<<<<<< HEAD
This is the main branch's important update.
=======
This is the awesome version for Feature A.
>>>>>>> feature-a

These are conflict markers, and understanding them is key to resolution:

  • <<<<<<< HEAD: This marks the beginning of the conflicting changes from your current branch (the one you are merging into), which is main in this case. HEAD always refers to the tip of your current branch.
  • =======: This is the separator. Everything above it (<<<<<<< HEAD to =======) represents the changes from your current branch. Everything below it (======= to >>>>>>> feature-a) represents the incoming changes from the branch you are merging.
  • >>>>>>> feature-a: This marks the end of the conflicting changes from the branch you are merging from (feature-a).

So, Git presents both versions of the conflicting code, neatly sectioned off, and leaves it to you to decide the final version.

Resolving the Conflict: Step-by-Step

Your task is to edit hello.txt, remove all conflict markers (<<<<<<<, =======, >>>>>>>), and leave only the code you want to keep.

Let’s say for this exercise, we want to combine both ideas: we want the “awesome version” but also acknowledge it’s an “important update.”

  1. Edit the conflicted file:

    Open hello.txt again. You’ll see the conflict markers.

    Hello, Git World!
    <<<<<<< HEAD
    This is the main branch's important update.
    =======
    This is the awesome version for Feature A.
    >>>>>>> feature-a
    

    Modify the file to the desired state. For example, let’s keep the “awesome version” and add a note about the “important update”:

    Hello, Git World!
    This is the awesome version for Feature A, and it includes the main branch's important update.
    

    Crucially, ensure all <<<<<<<, =======, and >>>>>>> lines are completely removed.

    Save the file.

  2. Tell Git the conflict is resolved by staging the file:

    After saving the merged file, you need to tell Git that you’ve finished resolving the conflict. You do this by staging the file, just like any other change.

    First, let’s check the git status to see the conflict state:

    git status
    

    You’ll see something like:

    On branch main
    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:   hello.txt
    
    no changes added to commit (use "git add" and/or "git commit -a")
    

    Git clearly states Unmerged paths and suggests use "git add <file>..." to mark resolution. Let’s do that:

    git add hello.txt
    

    Now, check git status again:

    git status
    

    Output:

    On branch main
    All conflicts fixed but you are still merging.
      (use "git commit" to conclude merge)
    
    Changes to be committed:
            modified:   hello.txt
    

    Great! Git now knows you’ve handled the conflict in hello.txt.

  3. Commit the merge:

    The final step is to commit the merge. Git will pre-populate a commit message for you, which is usually quite descriptive and includes the branches involved in the merge.

    git commit
    

    This will open your default text editor (like Vim, Nano, or your configured Git editor) with a pre-filled commit message:

    Merge branch 'feature-a'
    
    # Conflicts:
    #       hello.txt
    #
    # It looks like you may be committing a merge.
    # If this is not correct, please remove the file
    #       .git/MERGE_MSG
    # 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 main
    # All conflicts fixed but you are still merging.
    #
    # Changes to be committed:
    #       modified:   hello.txt
    #
    

    You can accept this default message or modify it to add more context about how you resolved the conflict. For now, let’s just save and exit the editor (e.g., in Vim, type :wq and press Enter).

    After committing, you’ll see output like:

    [main <some_hash>] Merge branch 'feature-a'
    

    Congratulations! You have successfully resolved your first merge conflict!

    You can verify the history with git log --oneline --graph:

    git log --oneline --graph
    

    You should see a clear merge commit, showing the two branches coming back together.

Mini-Challenge: Double the Trouble!

Let’s try another conflict, this time with a slightly different scenario.

  1. Ensure you’re on main and feature-a is merged.

    git checkout main
    

    (You should already be on main).

  2. Create a new branch feature-b:

    git checkout -b feature-b
    
  3. Add a new line to hello.txt on feature-b: Open hello.txt and add a third line.

    Hello, Git World!
    This is the awesome version for Feature A, and it includes the main branch's important update.
    This line is for Feature B.
    

    Save and commit:

    git add hello.txt
    git commit -m "Feature B: Added new line"
    
  4. Switch back to main and add a different new line at the same position:

    git checkout main
    

    Open hello.txt and add a third line, but make it different from feature-b.

    Hello, Git World!
    This is the awesome version for Feature A, and it includes the main branch's important update.
    This line is for the main branch's new feature.
    

    Save and commit:

    git add hello.txt
    git commit -m "Main: Added another new line"
    
  5. Attempt to merge feature-b into main:

    git merge feature-b
    

    Challenge: Resolve this conflict independently.

    • Hint: Remember the conflict markers! Decide which version of the third line you want (or combine them) and remove the markers. Then git add and git commit.
    • What to observe/learn: You’ll see that conflicts can happen even with new lines if they try to occupy the same logical space (e.g., both branches added a “third line” to a file that only had two). The process of resolution remains the same.

Common Pitfalls & Troubleshooting

Merge conflicts are part of Git’s safety net, but it’s easy to stumble while learning to navigate them. Here are a few common pitfalls and how to troubleshoot them:

  1. Forgetting to git add after resolving the file:

    • Pitfall: You meticulously edit the file, remove all conflict markers, save it, and then immediately try to git commit. Git will complain, saying “no changes added to commit (use ‘git add’ and/or ‘git commit -a’)”.
    • Troubleshooting: Git needs you to explicitly tell it that the conflict is resolved and the file is ready. Always run git add <conflicted-file> after editing and saving it. git status is your best friend here, always showing you the current state of the merge.
  2. Accidentally discarding changes or accepting the wrong version:

    • Pitfall: In the heat of resolution, you might accidentally delete a crucial line, or blindly accept HEAD when you really needed the incoming changes (or vice-versa).
    • Troubleshooting:
      • During resolution: If you realize your mistake before committing the merge, you can simply re-edit the file. If you want to completely restart the conflict resolution for a specific file, you can use git checkout --ours <file> to revert to your current branch’s version, or git checkout --theirs <file> to revert to the incoming branch’s version. Be careful: these overwrite the file with one version, potentially losing combined work.
      • Aborting the entire merge: If you’re completely lost and want to start over, you can abort the entire merge operation: git merge --abort. This will revert your repository to the state it was in before you started the merge, discarding any conflict resolutions you’ve made.
      • After committing: If you’ve already committed the merge and realize you made a mistake, you’ll need to use git revert <merge-commit-hash> to undo the merge, or potentially git reset --hard <previous-commit-hash> (use git reset --hard with extreme caution, especially in shared repositories, as it rewrites history).
  3. Confusion between HEAD and the incoming branch:

    • Pitfall: It’s easy to forget which side of the ======= belongs to HEAD (your current branch) and which belongs to the merging branch.
    • Troubleshooting: Always remember:
      • <<<<<<< HEAD to ======= is YOURS (the branch you’re currently on, which you’re merging into).
      • ======= to >>>>>>> <branch-name> is THEIRS (the branch you’re merging from).
    • Take a moment to read the code in each section and understand its origin before making a decision.

Summary

Phew! You’ve just navigated the thrilling world of merge conflicts and emerged victorious. Let’s recap what you’ve learned:

  • Merge conflicts occur when Git cannot automatically reconcile diverging changes to the same lines of code in different branches.
  • Git pauses the merge process and marks the conflicted files with <<<<<<< HEAD, =======, and >>>>>>> <branch-name> to show you the conflicting sections.
  • To resolve a conflict, you must manually edit the conflicted file(s), remove all conflict markers, and leave only the desired final code.
  • After editing, you use git add <file> to stage the resolved file, signaling to Git that you’ve fixed it.
  • Finally, you complete the merge with a git commit, which records the resolution as a new merge commit in your history.
  • git status is your constant companion during conflict resolution, guiding you on the next steps.
  • If things go sideways, git merge --abort is your escape hatch to restart the merge.

Resolving conflicts is a fundamental skill in collaborative development. The more you practice, the more intuitive it becomes. Don’t be afraid of them; embrace them as an opportunity to ensure your codebase stays clean and correct!

In our next chapter, we’ll dive deeper into another powerful Git reordering technique: Rebasing. Get ready to learn how to rewrite history (carefully!) for a cleaner, linear project history.

References

This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.