Merge main into your branch first, resolve any conflicts there, then merge your branch into main. Doing it in that order means conflicts get resolved on your branch instead of on the one everyone else is using.
The command is git merge. The part worth learning is the sequence, because merging directly into main and hitting conflicts leaves main in a conflicted state while your colleagues are trying to work.
That is recoverable, and it is also entirely avoidable by reversing the order.
Table of contents
- The safe sequence
- Fast-forward, no-ff, and what your history looks like
- Resolving a conflict
- Getting out of a merge that went wrong
- Merge or rebase
- Merging and what deploys from main
- How this fits the rest of the stack
- FAQ
The safe sequence
Five steps, in this order:
# 1. Get the current main
git switch main
git pull origin main
# 2. Bring main into your branch -- conflicts surface HERE
git switch feature/checkout-flow
git merge main
# 3. Resolve anything that conflicts, then test
# 4. Merge into main, which is now a clean fast-forward
git switch main
git merge feature/checkout-flow
# 5. Push
git push origin main
Step 2 is the whole point. Any conflict between your work and everyone else’s is resolved on your branch, where you can take your time, run the tests, and start over if the resolution goes badly. By step 4 there is nothing left to conflict, so the merge into main is trivial.
Skipping step 1 is the other common error. Merging a stale local main into your branch resolves conflicts against a version of the codebase that no longer exists, and the real conflicts appear later anyway.
Fast-forward, no-ff, and what your history looks like
When main has not moved since you branched, Git does not create a merge commit — it just moves the pointer forward. That is a fast-forward, and it leaves no record that a branch existed.
Whether you want that record is a team decision:
git merge feature/x # fast-forward when possible
git merge --no-ff feature/x # always create a merge commit
git merge --ff-only feature/x # fail rather than create a merge commit
--no-ff keeps the branch visible in history, so git log --graph shows which commits belonged to which piece of work and reverting the whole feature is one git revert -m 1 against the merge commit. The cost is a busier history.
--ff-only is worth knowing as a safety check rather than a style: it refuses to merge if a real merge would be needed, which tells you your branch is behind before you find out the messy way.
Resolving a conflict
A conflict means both branches changed the same lines and Git will not guess. It marks the file and stops:
<<<<<<< HEAD
const TIMEOUT = 30;
=======
const TIMEOUT = 60;
>>>>>>> feature/checkout-flow
Above the ======= is the branch you are on. Below it is the branch being merged in. Edit the file to the version you want, remove all three marker lines, then:
git status # what is still unmerged
git add src/config.js
git commit # completes the merge
Two things people get wrong. Resolving does not mean picking a side — often the correct result is neither version, and you write something new. And leaving a stray >>>>>>> in the file is astonishingly easy; grep for the markers before committing:
git diff --check
grep -rn '^<<<<<<<\|^>>>>>>>' src/
Getting out of a merge that went wrong
Nothing here is unrecoverable. Know these before you need them:
# Cancel an in-progress merge, back to before you started
git merge --abort
# Undo a completed merge that has NOT been pushed
git reset --hard ORIG_HEAD
# Undo a merge that HAS been pushed -- creates a new commit, safe for shared history
git revert -m 1 <merge-commit-sha>
The distinction in the last two matters. reset --hard rewrites history, which is fine on a local branch and destructive on a shared one — everyone else’s history now disagrees with yours. Once a merge is on the remote, revert is the correct tool because it adds a commit rather than removing one.
-m 1 on the revert means “keep the first parent”, which for a merge into main means keeping main’s line of history and undoing the branch’s changes. It is almost always what you want.
And if you are ever genuinely lost, git reflog lists every position HEAD has been in. Commits are rarely actually gone.
Merge or rebase
The other way to get your branch up to date is git rebase main, which replays your commits on top of current main instead of creating a merge commit.
Rebase gives a linear history that is easier to read and bisect. It also rewrites your commits — new SHAs — which is fine on a branch only you are using and disruptive on one someone else has pulled.
A workable default: rebase your own unshared feature branches to keep them tidy, merge into main so the integration point is recorded. And never rebase a branch that others are working on, unless everyone involved has agreed and knows what --force-with-lease is.
Note that most hosted platforms have a merge queue or equivalent that handles this for you on pull requests, which is worth turning on for a busy repository — it retests the merged result rather than the branch, catching the case where two independently green branches break when combined.
Merging and what deploys from main
For most teams, merging into main is the deployment trigger. That gives the sequence above a practical edge: a conflict resolved carelessly on your branch is a bug, while the same conflict resolved carelessly on main is a bug that ships.
Two habits help. Run the tests after step 2, not just before — the merged result is code that has never existed before, and passing on both branches separately does not mean passing combined. And know how to roll back, because occasionally you merge something that is only wrong in production.
On RunxBuild, services and static sites build from a connected GitHub repository with a build log per deploy and rollback to a previous deploy, so a bad merge is a rollback rather than a hurried revert-and-redeploy while the site is down.
How this fits the rest of the stack
Pull main, merge main into your branch, resolve and test there, then merge into main — that ordering keeps every conflict off the branch your team depends on. Know merge --abort, revert -m 1 and reflog before you need them. And since merging into main is usually what triggers a deploy, having a rollback path matters; the RunxBuild hosting calculator shows what the service doing that deploying costs to run.
Useful related references:
- git push origin main: What Each Word Does and Why It Fails
- How to View HTML Updates in a GitHub Branch Before You Merge
- Git Rename Branch: The Local Move, the Remote Move, and the Part That Breaks CI
- Services on RunxBuild
FAQ
What is the correct order to merge a branch into main?
Pull the latest main, switch to your branch and merge main into it, resolve conflicts and test there, then switch to main and merge your branch in. That way conflicts are resolved on your branch rather than on the one your team is using.
How do I resolve a merge conflict?
Open each marked file, choose or write the correct result, and delete the <<<<<<<, ======= and >>>>>>> marker lines. Then git add the file and git commit. Run git diff --check first to catch markers left behind by accident.
How do I undo a merge?
Use git merge --abort for an in-progress merge, git reset --hard ORIG_HEAD for a completed merge you have not pushed, and git revert -m 1 <sha> for one you have. Only the last is safe on shared history, because it adds a commit instead of rewriting.
Should I use merge or rebase?
Rebase your own unshared feature branches for a linear history, and merge into main so the integration point is recorded. Never rebase a branch other people have pulled, since it rewrites commit SHAs and their history will no longer match yours.
What does git merge —no-ff do?
It creates a merge commit even when a fast-forward would be possible, keeping the branch visible in history. That makes git log --graph more informative and lets you revert a whole feature with a single git revert -m 1 against the merge commit.