Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Resolve Git Merge Conflicts Like a Pro

By Kokil Thapa | Last reviewed: September 2026

Two developers edit the same line in routes/web.php, and your next git merge stops cold. That is a merge conflict — Git refusing to guess which change wins. To resolve Git merge conflicts like a pro, you need a repeatable workflow, not panic edits. This guide walks through real markers, three-way merges, tool choices, and the checks I run before any merge or rebase lands on main. Whether you ship Laravel apps, WordPress themes, or CI pipelines, the mechanics are identical; only the file paths change.

What causes Git merge conflicts during a merge or pull?

Git merges when histories diverge. It auto-combines changes that touch different lines or different files. A conflict appears when both branches change the same hunk and Git cannot pick a winner without losing work.

Common triggers on production teams include parallel feature branches, hotfixes on main while a long-lived branch rots, and formatting sweeps that rewrite entire files. Renames and moves cause painful conflicts too — Git may treat a moved file as delete-plus-add.

On a legal-tech portal I built, two developers edited the same Blade partial for a booking form. One added Khalti payment fields; the other refactored validation markup. Same file, overlapping lines — classic conflict material.

Why Merge Conflicts Happenmain branchcommit A → Bfeature branchcommit A → Csame filesame lines changedCONFLICTmanual fix neededAlso: renames, binary files, deleted vs modified, whole-file reformats
Git merge conflicts appear when two branches change the same region and automatic three-way merge cannot decide the result.

Understanding the trigger helps you prevent repeats. Short-lived branches, smaller commits, and consistent branching strategy choices reduce conflict volume more than any mergetool ever will.

How do you read Git conflict markers in a file?

Conflicted files contain marker blocks. Git inserts them; you must remove every marker before committing. Leaving even one marker breaks PHP parsing and fails CI instantly.

Standard marker anatomy

A typical block looks like this inside a Laravel controller:

<<<<<<< HEAD
    return redirect()->route('bookings.index')
        ->with('success', 'Booking saved.');
=======
    return redirect()->route('bookings.show', $booking)
        ->with('status', 'Booking created.');
>>>>>>> feature/booking-redirect

The rules are fixed:

  • <<<<<<< HEAD starts your current branch version (where you ran merge).
  • ======= separates the two sides.
  • >>>>>>> branch-name ends the incoming branch version.
  • Everything outside the markers is already merged — do not delete unrelated lines.

HEAD during a merge is the branch you checked out before merging. If you run git merge feature/x on main, HEAD is main. That trips people who merge in the wrong direction. Run git branch --show-current first.

Multiple conflicts in one file

Large files may contain several marker blocks. Search for <<<<<<< until zero remain. A regex tester helps validate patterns if you script checks in CI.

Conflict Marker Anatomy<<<<<<< HEAD (current branch code starts)YOUR versionkeep, discard, or rewrite======= separatorTHEIR versionincoming branch code>>>>>>> feature/branch (markers must be deleted)
Every Git merge conflict block has HEAD code, a separator, and incoming branch code — remove all markers after editing.

What is the step-by-step workflow to resolve Git merge conflicts?

Pros follow the same sequence every time. Speed comes from habit, not shortcuts that skip verification.

  1. Confirm state. Run git status. Git lists unmerged paths under "Unmerged paths".
  2. Choose scope. Decide whether to finish, abort, or use a mergetool.
  3. Edit each file. Open conflicted paths. Combine logic; delete markers.
  4. Stage resolved files. Run git add path/to/file.php per file.
  5. Complete the merge. Run git merge --continue or commit if Git opens an editor.
  6. Verify. Run tests, lint, and a quick smoke test on affected routes.

Commands you will use daily

# See conflicted files
git status

# List only unmerged paths
git diff --name-only --diff-filter=U

# Abort and return to pre-merge state
git merge --abort

# After fixing each file
git add app/Http/Controllers/BookingController.php

# Finish when all conflicts are staged
git merge --continue

# See conflict hunks with more context
git diff

During a Deployer 7 deploy on shared EC2 infrastructure, I once merged a hotfix while a feature branch was mid-merge locally. Staging only the resolved files — not unrelated WIP — kept the merge commit clean and avoided pushing half-fixed conflict markers to GitLab CI.

Using git mergetool

GUI tools show base, ours, and theirs side by side. Configure once:

git config --global merge.tool vimdiff
# or: meld, kdiff3, vscode (via extension)

git mergetool

Mergetool writes resolved files and often stages them. Still run git status before continuing. Official reference: git-merge documentation.

Three-Way Merge ModelBASE (common ancestor)OURScurrent branchTHEIRSincoming branchRESOLVED FILEcombine both intents correctly
Pro merge conflict resolution compares BASE, OURS, and THEIRS — then writes a fourth version that preserves both changes where possible.

Should you keep ours, theirs, or rewrite the conflict hunk?

Blindly picking one side loses work. The goal is a correct third version, not speed.

SituationBest actionExample
Same bug fix, different wordingPick one clean versionIdentical validation rule in a Form Request
Additive features on same areaMerge both logicallyPayment gateway + SMS notification in checkout
Refactor vs featureRe-apply feature on new structureController split while another dev added a method
Config/env valuesNever auto-merge secrets.env.example keys from both branches manually
Lock files (composer.lock)Regenerate after mergeRun composer update nothing or reinstall deps
Binary assets (PNG, PDF)Choose one file or external assetLogo swap — Git cannot merge pixels

For composer.lock on Laravel 13 projects (PHP 8.3+), I resolve composer.json by hand, delete the lock conflict, then run:

composer install
composer update package/name --with-dependencies  # only if needed
git add composer.json composer.lock

Never commit a lock file you did not regenerate locally. Production drift causes deploy failures that look like application bugs.

On projects using secrets scanning in Git and CI, scan again after resolving .env.example or config conflicts. A bad paste can expose API keys.

Checkout shortcuts (use with care)

# Take entire file from current branch
git checkout --ours path/to/file.php

# Take entire file from incoming branch
git checkout --theirs path/to/file.php

# Then edit if needed and stage
git add path/to/file.php

These commands replace the whole file. They work for generated assets or when one branch clearly owns the file. They are dangerous for controllers where both sides added methods you still need.

How do you resolve merge conflicts during rebase, pull, and cherry-pick?

Conflict markers look the same. Only the Git command to finish or abort changes.

  • git pull (merge mode): finish with git merge --continue after staging.
  • git pull --rebase: fix files, git add, then git rebase --continue. Abort with git rebase --abort.
  • git rebase main: conflicts replay commit by commit. You may fix the same file multiple times.
  • git cherry-pick <sha>: one commit applied; continue with git cherry-pick --continue.

Rebase rewrites history. If you already pushed the branch, you need a force push — coordinate with your team first. Read when to rebase versus merge before choosing a default workflow.

During rebase, HEAD moves through replayed commits. Marker labels still say branch names, but "ours" and "theirs" swap meaning compared to a merge. When confused, read the hunk content — not the label alone.

Merge Conflict Decision Flowgit status: CONFLICTwrong branch?merge --abortmergetool GUImany filesmanual editgit add + merge --continue / rebase --continuetests + push + CI greenphpunit, pint, npm build if assets changed
Resolve Git merge conflicts like a pro by choosing abort, mergetool, or manual edit — then always verify before push.

What advanced techniques prevent repeat merge conflicts?

Resolution skill matters. Prevention saves more hours across a team.

Enable rerere for repeated conflicts

git rerere (reuse recorded resolution) stores how you fixed a conflict. Git replays that fix if the same conflict appears again — common during long rebases.

git config --global rerere.enabled true
git config --global rerere.autoupdate true

It is not magic. It helps when the same hunk fights you on every rebase of a stale branch.

Merge main into feature often

Sync early and often:

git checkout feature/booking-flow
git fetch origin
git merge origin/main
# resolve small conflicts now, not one giant conflict at PR time

This pairs well with Git hooks that run checks before push. Catch syntax errors from bad merges before they hit remote.

CI and pipeline gotchas

After resolving conflicts in GitLab CI config or Deployer recipes, run the pipeline on your branch. I have seen merged .gitlab-ci.yml files with duplicate job keys that passed locally but failed on the runner.

File-permission conflicts on Linux deploy targets are a separate problem. If merges touch executable bits, see how to stop Git from tracking file permissions to reduce noise.

Recover when a merge goes wrong

If you committed a bad resolution, git reflog shows prior HEAD positions. You can reset to pre-merge state if you have not pushed yet:

git reflog
git reset --hard HEAD@{1}

Details: recover lost commits with git reflog. After push, prefer a revert commit over force-pushing shared branches.

Platform-specific notes

Azure DevOps and GitHub both surface conflicts in the web UI for simple line edits. For Laravel apps with nested Blade logic, resolve locally. Web editors miss context from related partials and service providers.

Teams on Azure Repos with branch policies may require merge commits or squash merges. Know your target format before you start — squash merges hide intermediate conflict commits but still require clean resolution in the final diff.

Real project patterns

On trek booking systems with Livewire components, conflicts cluster in Livewire classes and shared Blade layouts. Fix the PHP class first, then re-run frontend builds with Vite 8.x if JS imports changed.

On legal information portals, content and routing conflicts often overlap. Keep slug changes from one branch and new route definitions from another — broken routes hurt SEO faster than a failed deploy.

For ongoing maintenance, many clients use support and maintenance retainers partly because merge debt on long-lived forks costs more than proactive syncs.

Key Takeaways

  • Run git status first; know whether you are in merge, rebase, or cherry-pick before editing.
  • Delete every <<<<<<<, =======, and >>>>>>> marker — leftover markers break builds.
  • Combine both sides when features are additive; regenerate composer.lock instead of hand-merging it.
  • Use git merge --abort or git rebase --abort when you are on the wrong branch or need a reset.
  • Always run tests and CI after resolution; merge commits with conflict fixes still need the same quality bar as feature work.
  • Prevent conflicts with short-lived branches, frequent syncs from main, and a team-wide branching policy.

People Also Ask

Can Git resolve merge conflicts automatically?

Git auto-merges non-overlapping changes in the same file. When the same lines change, it stops and marks the file conflicted. Tools like git mergetool assist you, but a human (or carefully configured rerere) must confirm the final logic. No reliable tool understands your business rules.

What happens if I commit without fixing conflict markers?

PHP, JavaScript, and YAML parsers fail on marker lines. CI fails immediately. In production, you might ship a white screen or 500 error. Some teams add a pre-commit hook searching for <<<<<<< — cheap insurance.

Is it better to merge or rebase to avoid conflicts?

Neither avoids conflicts when the same lines change. Rebase replays commits and can multiply conflict rounds. Merge produces one conflict session but adds a merge commit. Pick based on team policy and whether the branch is shared — not based on conflict avoidance myths.

How do I resolve conflicts in a pull request on GitHub?

GitHub offers "Resolve conflicts" in the web UI for simple text files. Use it for copy or config tweaks. For application code with tests and dependencies, check out the branch locally, resolve with your IDE, run the test suite, then push. The PR updates automatically.

Ship clean merges on every project

Merge conflicts are normal on active codebases — Laravel apps, WooCommerce shops, and CI-driven deploy pipelines all hit them. The difference between a ten-minute fix and a production incident is process: read markers carefully, merge intent not labels, regenerate lock files, and verify before push. If you want help tightening Git workflow, deploy pipelines, or custom software delivery on your stack, get in touch via the contact page. You can also browse the portfolio for examples of production systems maintained with GitLab CI and Deployer, or read more on the blog about AI-assisted code review in CI and fixing Git ignore problems that often surface during messy merges.

Frequently Asked Questions

Git stops merging when two branches change the same lines and cannot pick a winner without losing work.

Git auto-merges non-overlapping changes. Same-line edits need manual resolution; mergetool and rerere assist but cannot judge your business logic.

Conflicts appear when both branches edit the same hunk and Git's three-way merge cannot combine them safely. Common triggers include parallel feature branches, hotfixes on main while a long-lived branch sits stale, formatting sweeps that rewrite whole files, and renames Git treats as delete-plus-add. On production Laravel teams, two developers editing the same Blade partial or routes/web.php line is classic conflict material. Short-lived branches and frequent syncs from main reduce volume more than any mergetool.

Git inserts fixed marker blocks you must remove before committing. >>>>>> branch-name ends the incoming branch version. Everything outside markers is already merged — do not delete unrelated lines. During merge, HEAD is the branch you checked out before merging, which trips people who merge in the wrong direction. Run git branch --show-current first. Large files may have multiple blocks — search for

Pros follow the same sequence every time. Confirm state with git status and list unmerged paths via git diff --name-only --diff-filter=U. Edit each conflicted file, combine logic correctly, and delete every marker. Stage each resolved file with git add path/to/file.php. Finish with git merge --continue or commit if Git opens an editor. Verify with tests, lint, and a smoke test on affected routes. Stage only resolved files — not unrelated WIP — to keep the merge commit clean before pushing to GitLab CI.

Blindly picking one side loses work. The goal is a correct third version combining both changes where features are additive — like payment gateway plus SMS notification in the same checkout area. Pick one clean version when fixes are identical with different wording. For refactors versus features, re-apply the feature on the new structure. Never auto-merge secrets in .env or config — combine .env.example keys manually and rescan if CI runs secrets scanning. Use git checkout --ours or --theirs only when one branch clearly owns the whole file; they are dangerous for controllers where both sides added methods you still need.

Conflict markers look identical; only the finish and abort commands change. After git pull in merge mode, stage fixes and run git merge --continue. After git pull --rebase or git rebase main, fix files, git add, then git rebase --continue — abort with git rebase --abort. Rebase replays commits one by one, so you may fix the same file multiple times. Cherry-pick uses git cherry-pick --continue after staging. During rebase, ours and theirs swap meaning compared to merge — read hunk content, not labels alone. Coordinate before force-pushing rebased branches already on remote.

Never hand-merge composer.lock. Resolve composer.json conflicts manually, delete the lock conflict entirely, then regenerate locally with composer install. Run composer update package/name --with-dependencies only if a specific dependency changed. Stage both composer.json and composer.lock with git add, then finish the merge. Committing a lock file you did not regenerate locally causes production deploy failures that look like application bugs. This applies on Laravel 13 projects requiring PHP 8.3+ the same way it does on any Composer-managed codebase.

PHP, JavaScript, and YAML parsers fail on marker lines. CI fails immediately. In production you might ship a white screen or 500 error. Some teams add a pre-commit hook searching for

Abort when you merged in the wrong direction, checked out the wrong branch, or need a clean reset before starting over. git merge --abort returns to the pre-merge state without a partial commit. If you already committed a bad resolution locally and have not pushed, git reflog and git reset --hard HEAD@{1} recover the prior HEAD. After pushing to shared branches, prefer a revert commit over force-pushing main.

git rerere (reuse recorded resolution) stores how you fixed a conflict and replays that fix if the same hunk appears again — common during long rebases of stale branches. Enable it once with git config --global rerere.enabled true and git config --global rerere.autoupdate true. It is not magic: it helps when the same conflict fights you on every rebase, but it cannot replace understanding the underlying code change.

Neither avoids conflicts when the same lines change. Rebase replays commits and can multiply conflict rounds across multiple commits. Merge produces one conflict session but adds a merge commit. Pick based on team policy and whether the branch is shared — not conflict-avoidance myths. Azure Repos branch policies may require merge commits or squash merges; know your target format before starting.

If you committed a bad resolution locally and have not pushed, run git reflog to find prior HEAD positions, then git reset --hard HEAD@{1} to return to pre-merge state. After push to shared branches, prefer a revert commit over force-pushing. For conflicted but uncommitted merges, git merge --abort or git rebase --abort clears the in-progress state cleanly without touching unrelated work.

GitHub offers Resolve conflicts in the web UI for simple text files — useful for copy or config tweaks. For application code with tests and dependencies, check out the branch locally, resolve with your IDE, run the test suite, then push. The PR updates automatically. For Laravel apps with nested Blade logic, resolve locally; web editors miss context from related partials and service providers.

Enable git rerere for repeated hunks during long rebases. Sync main into feature branches often — git fetch origin then git merge origin/main — to resolve small conflicts early instead of one giant conflict at PR time. Use short-lived branches, smaller commits, and a team-wide branching policy. After resolving GitLab CI or Deployer 7 recipe conflicts, run the pipeline on your branch; merged .gitlab-ci.yml files with duplicate job keys pass locally but fail on the runner. Git hooks before push catch syntax errors from bad merges before they hit remote.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: