How to Resolve Git Merge Conflicts — The Guide That Actually Explains What's Happening

By Adhen Prasetiyo

Monday, March 16, 2026 • 8 min read

Code editor showing Git merge conflict with color-coded current and incoming changes

You’re working on a feature branch. You’ve been making solid progress for days. You’re ready to merge your work back into main. You run git merge main to bring in the latest changes, and Git hits you with:

Auto-merging app.js

CONFLICT (content): Merge conflict in app.js

Automatic merge failed; fix conflicts and then commit the result.

You open the file and see this mess:

<<<<<<< HEAD

const port = 3000;

=======

const port = process.env.PORT || 8080;

>>>>>>> main

If your first instinct is to randomly click “Accept Current Change” or “Accept Incoming Change” in your editor, stop. That’s how bugs get shipped to production. Those conflict markers are telling you something specific, and it takes about 30 seconds to understand what they mean.

What the Conflict Markers Mean

Every merge conflict looks the same. Three markers divide the conflict into two sections:

<<<<<<< HEAD

your code (current branch)

=======

their code (incoming branch)

>>>>>>> branch-name

<<<<<<< HEAD — Everything below this line, until the equals signs, is the code from YOUR current branch. This is what you wrote.

======= — The divider between the two versions.

>>>>>>> branch-name — Everything above this line, from the equals signs upward, is the code from the OTHER branch. This is what someone else wrote (or what exists in the branch you’re merging in).

In our example:

<<<<<<< HEAD

const port = 3000;        ← You hardcoded port 3000

=======

const port = process.env.PORT || 8080;  ← Someone else made it configurable

>>>>>>> main

Now you understand the conflict. You wrote const port = 3000. Someone else changed the same line to const port = process.env.PORT || 8080. Git can’t decide which one to keep, so it’s asking you.

The Three Decisions You Can Make

Keep your version: Delete the incoming code and all markers.

const port = 3000;

Keep their version: Delete your code and all markers.

const port = process.env.PORT || 8080;

Combine both (the smart choice): Write new code that incorporates the best of both.

const port = process.env.PORT || 3000;

This keeps the configurable environment variable from the other branch but uses your port 3000 as the default instead of 8080. That’s often the right answer — neither version alone is complete, but together they make something better.

The critical rule: delete ALL conflict markers. Every <<<<<<<, =======, and >>>>>>> must be removed from the file. If you leave even one behind, your code won’t compile.

Step-by-Step Resolution in the Terminal

1. Find all conflicted files:

git status

Look under “Unmerged paths” — those are the files with conflicts.

2. Open each file and resolve the conflict:

Use any text editor — VS Code, Vim, Nano, Sublime, whatever you’re comfortable with. Find the conflict markers, decide what the final code should look like, delete the markers.

3. Test your code:

Before staging, make sure the code actually works. Run your test suite, start the application, or at minimum check that it compiles. Don’t stage broken code.

4. Stage the resolved files:

git add app.js

Or stage everything:

git add .

5. Complete the merge:

git commit

Git opens your editor with a default merge commit message. You can edit it or accept the default. Save and close.

Done. The merge is complete.

Resolving Conflicts in VS Code (The Easy Way)

VS Code makes conflict resolution visual and fast. When you open a file with conflicts, you’ll see:

Green highlight — your code (Current Change)

Blue highlight — their code (Incoming Change)

Above each conflict block, VS Code shows clickable buttons:

Accept Current Change — keeps your code, deletes theirs

Accept Incoming Change — keeps their code, deletes yours

Accept Both Changes — keeps both blocks of code, one after the other

Compare Changes — shows a side-by-side diff

For simple conflicts, clicking one of these buttons is perfectly fine. For complex conflicts where you need to combine code, click inside the conflict zone and edit manually — delete the markers and write the final version.

VS Code’s 3-Way Merge Editor:

For complicated conflicts, VS Code has a powerful merge editor. Look for the “Resolve in Merge Editor” button at the bottom of the file. This opens a three-panel view:

  • Top Left: Your code (Current)
  • Top Right: Their code (Incoming)
  • Bottom: The result

You can check boxes next to specific code blocks to include them in the result, or type directly in the result panel. This is the best tool for conflicts where you need to surgically combine code from both sides.

After resolving, go to the Source Control panel (Ctrl + Shift + G), stage the file (click +), and commit.

Resolving Conflicts on GitHub

If you’re resolving a conflict in a pull request on GitHub:

  1. Go to the pull request page
  2. Click “Resolve conflicts” (if available — GitHub can only resolve simple conflicts)
  3. The web editor shows the conflict markers
  4. Edit the code to resolve the conflict, removing all markers
  5. Click “Mark as resolved”
  6. Click “Commit merge”

If the “Resolve conflicts” button is grayed out, the conflict is too complex for GitHub’s web editor. You need to resolve it locally using the terminal or VS Code.

Resolving Conflicts During a Rebase

Rebasing is different from merging, but the conflict resolution process is almost identical. The difference is in how you finish.

During a merge: resolve → git addgit commit

During a rebase: resolve → git addgit rebase --continue

When you run git rebase main and hit a conflict:

  1. Resolve the conflict in the file (same as above — remove markers, write final code)
  2. Stage the file: git add filename
  3. Continue the rebase: git rebase --continue

Git applies the current commit and moves to the next one. If there are more conflicts, you’ll resolve them one at a time. Each conflict corresponds to a single commit being replayed.

If you get overwhelmed: git rebase --abort cancels everything and returns to the state before the rebase started.

The Emergency Exit

At any point during conflict resolution, if you want to undo everything and go back to where you were before:

During a merge:

git merge --abort

During a rebase:

git rebase --abort

During a cherry-pick:

git cherry-pick --abort

These commands are safe. They undo the merge/rebase/cherry-pick and leave your branch exactly as it was. No data is lost. Use them whenever you feel lost.

Real-World Conflict Patterns

Pattern 1: Both sides edited the same function.

One person added a parameter. Another person changed the return value. You need to keep both changes — the new parameter AND the new return value.

Pattern 2: One side added code above, the other added code below.

Git marks the whole block as a conflict even though the changes don’t actually overlap. Use “Accept Both Changes” — both additions are probably needed.

Pattern 3: One side deleted a file, the other edited it.

Git can’t merge a deletion with an edit. You need to decide: should the file exist or not? If it should exist, keep the edited version. If it shouldn’t, accept the deletion.

Pattern 4: Package-lock.json or yarn.lock conflicts.

Don’t manually resolve lock file conflicts. Accept either version, then run npm install or yarn install to regenerate the lock file. The lock file will be correct based on the current package.json.

How to Reduce Merge Conflicts

You can’t prevent all conflicts, but you can make them less frequent and less painful:

Pull frequently. Run git pull origin main (or git rebase main) regularly while working on your feature branch. Small, frequent syncs create small, easy conflicts. Waiting two weeks to sync creates massive, painful conflicts.

Keep branches short-lived. A branch that lives for two days has far fewer conflicts than a branch that lives for two weeks. Merge small features often rather than large features rarely.

Communicate. If you know a teammate is working on the same file, coordinate. “Hey, I’m refactoring the auth module this afternoon” prevents a lot of headaches.

Don’t reformat entire files. If you change the indentation of a 500-line file from tabs to spaces, every line is “changed.” Anyone else who edits that file will have conflicts on every single line. Keep formatting changes in separate, dedicated commits.

Merge conflicts are not bugs. They’re not failures. They’re Git’s way of saying “I found two different versions of this code and I’m not smart enough to decide which one is right — you need to make that call.” Once you understand what the markers mean, resolving conflicts becomes routine. It takes 30 seconds for simple ones, a few minutes for complex ones, and you never need to fear them again.

Step-by-Step Guide

1

Find which files have conflicts

After a failed merge run git status in your terminal. Files with conflicts will be listed under Unmerged paths. Each file shows both modified status meaning Git started the merge but could not finish it because both branches changed the same lines. You need to manually resolve each conflicted file before you can complete the merge.

2

Open the file and read the conflict markers

Open the conflicted file in any text editor. You will see markers that look like this. The section between HEAD and the equals signs is your current branch code. The section between the equals signs and the branch name is the incoming code from the other branch. Your job is to decide what the final code should look like. You might keep your version keep their version combine both or write something entirely new.

3

Remove all conflict markers and write the final code

Delete all the conflict markers including the less than signs the equals signs and the greater than signs. Leave only the final code that you want. Make sure the code compiles or runs correctly after your edit. You cannot leave any conflict markers in the file because they will break your code.

4

Stage the resolved files and complete the merge

After resolving all conflicts in all files run git add followed by the filename for each resolved file. Or run git add period to stage all changes. Then run git commit to complete the merge. Git will create a merge commit with a default message. You can edit this message or accept the default. The merge is now complete.

5

Abort the merge if you want to start over

If you get overwhelmed or realize you made a mistake during conflict resolution you can abort the entire merge by running git merge --abort. This returns your branch to the exact state it was in before you started the merge. No changes are made. You can then try again when you are ready.

Frequently Asked Questions

Why do merge conflicts happen?
Merge conflicts happen when two branches modify the same lines in the same file. Git can automatically merge changes to different parts of a file but when changes overlap on the same lines it cannot decide which version to keep. It stops and asks you to make the decision. This is normal and expected in team development. It does not mean anyone did something wrong.
How do I resolve merge conflicts in VS Code?
VS Code detects conflicts automatically and highlights them with colors. Above each conflict you see clickable buttons. Accept Current Change keeps your code. Accept Incoming Change keeps the other branch code. Accept Both Changes combines both versions. You can also click Open in Merge Editor for a three-way view showing your code their code and the result side by side. After choosing click the file in the Source Control panel and stage it to mark as resolved.
How do I resolve conflicts during a rebase?
Rebase conflicts work the same way as merge conflicts but the resolution process is slightly different. After resolving the conflict in the file run git add on the resolved file then run git rebase --continue instead of git commit. Git will apply the current commit and move to the next one. If there are more conflicts you repeat the process. If you want to cancel the rebase entirely run git rebase --abort.
How do I prevent merge conflicts?
You cannot prevent all merge conflicts but you can reduce them. Pull from the main branch frequently so your branch stays up to date. Keep branches short-lived and merge them quickly. Communicate with your team about who is working on which files. Use smaller more focused commits rather than large changes. These practices reduce the chance of two people editing the same lines.
Adhen Prasetiyo

Research Bug bounty at javahack team

Research Bug bounty Profesional

Web Development Research Bug Hunter
View all articles →