git merge abort cancels an in-progress merge and puts your branch back exactly where it was before you ran git merge — but only while the merge is still in progress. Once a merge finishes and becomes a commit, git merge --abort stops working entirely, and you need a different command depending on whether that commit is still local or already pushed somewhere else. This guide covers all three situations precisely: the merge you haven't committed, the one you have but haven't pushed, and the one that's already shared. It also covers the parts most guides skip — git merge --quit, MERGE_HEAD mechanics, autostash behavior, and what happens when you try to abort a squash merge. If your merge went through fine and you just want to double-check what actually changed, diffing two files across branches is the fast way to confirm before you move on.

The Fast Answer: Three Situations, Three Commands

Three different situations get called "git cancel merge," "git undo merge," or "abort merge" in practice, and each one needs a different command. Mixing them up is the single biggest source of confusion in this area, so start here before anything else.

# 1. Merge is IN PROGRESS — conflict markers in your files, no commit made yet
git merge --abort

# 2. Merge is COMMITTED but not pushed — you have a merge commit only you can see
git reset --hard HEAD~1

# 3. Merge is COMMITTED and PUSHED — other people may already have it
git revert -m 1 <merge-commit-hash>

Check which situation you're in with one command: git status. If it reports "You have unmerged paths" or "All conflicts fixed but you are still merging," you're in situation 1. If git status shows a clean working tree and git log -1 shows a merge commit at the tip, you're in situation 2 or 3, and the only question left is whether that commit exists anywhere besides your machine — check with git log origin/main..HEAD; if the merge commit shows up in that list, it hasn't been pushed, and situation 2 applies. Everything below expands on why these three commands are the right ones, plus git merge --quit as a fourth option that behaves differently from all three.

Merge state, the .git files present, and the command that applies Not merging clean tree no MERGE_HEAD nothing to undo Merging — in progress conflicts → resolved, staged MERGE_HEAD, MERGE_MSG, ORIG_HEAD all present git merge --abort works Committed merge commit made MERGE_HEAD gone git reset --hard HEAD~1 Pushed / shared commit on origin others may have it git revert -m 1 <hash> merge commit push MERGE_HEAD is what --abort checks for — present only during "Merging" Fast-forward / conflict-free merges skip straight to "Committed"
MERGE_HEAD exists only during "Merging" — that's the exact window where git merge --abort works; once a commit lands, the right undo depends on whether that commit has reached origin yet.

What "A Merge Is In Progress" Actually Means

Git doesn't track "merge in progress" as an abstract flag. It writes real files into your repository's .git directory, and their presence or absence is the actual mechanism that decides whether git merge --abort has anything to do.

# While a merge is unresolved, these exist in .git/
.git/MERGE_HEAD    # commit(s) being merged in — this is the file --abort checks for
.git/MERGE_MSG     # draft commit message, pre-filled with conflict markers if any
.git/MERGE_MODE    # present for --no-ff merges, empty otherwise
.git/ORIG_HEAD     # where HEAD pointed before the merge started
.git/AUTO_MERGE    # (Git 2.35+) tree from the last conflict auto-resolution pass

MERGE_HEAD is the one that matters most: it's the file that makes a merge "in progress" in Git's own definition. It holds the commit hash (or hashes, for an octopus merge) of whatever you're merging in. git commit checks for its existence — if it's there, the commit you're about to make is treated as a merge commit with multiple parents; once the commit completes, Git deletes MERGE_HEAD. That deletion is the exact moment git merge --abort stops being available, because --abort's own documentation defines it as failing "if there is no merge in progress," which Git checks by looking for that file.

ORIG_HEAD is a separate, more general-purpose file — it's not merge-specific. git merge, git reset, git rebase, and git pull all overwrite it with wherever HEAD pointed right before that command ran. It's useful as a one-shot "go back" reference (covered in the recovery section below), but only immediately after the operation you care about — the next history-changing command clobbers it.

git merge --abort: The Command for a Merge You Haven't Committed

This is the command people mean almost every time they search "git cancel merge" or "git abort merge with conflicts." It applies exactly when MERGE_HEAD exists — which is any point between running git merge and running git commit to finish it, including after every conflict is resolved and staged but not yet committed. Canceling the conflict and canceling the merge are the same act, so there is no separate git cancel merge conflict command to look for — the conflict only exists as part of the merge Git is holding open, and clearing the merge clears it.

git merge feature-branch
# CONFLICT (content): Merge conflict in src/api/retry.ts
# Automatic merge failed; fix conflicts and then commit the result.

git merge --abort
# working tree and MERGE_HEAD are gone — back to pre-merge state

Mechanically, git merge --abort is documented as equivalent to git reset --merge MERGE_HEAD when a merge is in progress. It does three things: restores every tracked file the merge touched to its pre-merge content, removes MERGE_HEAD, MERGE_MSG, and MERGE_MODE, and leaves HEAD exactly where it was — no new commit, no branch change. Untouched files stay untouched. If Git auto-stashed anything before the merge started (see the autostash section below), that stash gets reapplied as the last step.

Git's own documentation for --abort includes a real caveat worth repeating exactly: it "can fail to reconstruct the original (pre-merge) changes" if you had uncommitted changes to files the merge itself needed to modify. In practice this is rare, since git merge already refuses to start if it detects an unresolvable conflict between an uncommitted local change and the incoming merge — but it's why running git status right after an abort is a good habit, not paranoia. It also confirms the abort left you on the branch you started from; checking the current branch covers the other commands that answer that question when git status output gets long.

Why Abort Sometimes Fails — and What to Do Instead

The single most common mistake with git merge abort: running it after the merge commit was already made. Git's answer is blunt and easy to grep for:

git merge --abort
# fatal: There is no merge to abort (MERGE_HEAD missing).

That error means exactly what it says — MERGE_HEAD is gone because the merge already finished and became a real commit. --abort has nothing left to cancel; "abort" only makes sense for something still in flight. Aborting a merge with conflicts works fine; running the same command against a merge that already committed cleanly, or that you resolved and committed minutes ago, does not — and this is the fork in the road that decides everything else in this guide. There is no force abort merge flag to reach for either: --abort either finds a MERGE_HEAD to work with or it doesn't, and no option overrides that check. The vocabulary varies — git abandon merge, git stop merge and git exit merge all name the same intent — but past this point the command doesn't, and it depends only on whether the merge commit left your machine:

A less common variant of the same failure: running --abort from a squash merge. git merge --squash never writes MERGE_HEAD in the first place — it's covered separately below, because the fix isn't the same as the ordinary "already committed" case.

Which command undoes this merge? Merge in progress? (MERGE_HEAD exists?) git status Squash merge staged? (no MERGE_HEAD written) git merge --squash Commit pushed yet? (pushed to origin?) log origin..HEAD git merge --abort clears MERGE_HEAD git reset --hard HEAD unstages the squash changes reset --hard HEAD~1 not pushed revert -m 1 <hash> pushed Yes No Yes No No Yes Squash merges never write MERGE_HEAD — treat them as staged changes, not a merge Once pushed, reset --hard would diverge from what others have — revert only
Three yes/no checks — merge in progress, squash staged, commit pushed — resolve to exactly one correct command every time.

git merge --quit vs --abort: Keeping the Work, Dropping the Merge

git merge --quit, added in Git 2.23 (August 2019), is the option almost nobody knows about and almost every other guide skips entirely. It solves a specific problem --abort can't: you're mid-merge, you've resolved some or all of the conflicts by hand, and you want to stop being "in a merge" — without losing the edits you just made to the working tree.

git merge --quit
# clears MERGE_HEAD, MERGE_MSG, MERGE_MODE
# leaves the working tree and index exactly as they are — no reset

The difference in one sentence: --abort resets the working tree back to pre-merge state and then clears the merge metadata; --quit clears the merge metadata and leaves the working tree alone. After --quit, git status no longer shows you as mid-merge — but any files you edited during conflict resolution are still sitting there, modified, exactly as you left them. You've stopped Git from treating the situation as an active merge without throwing away a single keystroke of resolution work.

Reach for it when you've done real conflict-resolution work you don't want to redo, but you've decided this isn't going to become a merge commit right now — maybe you want to commit the resolved files as a regular commit instead, stash them for later, or split the work into a smaller change first. --abort is for "this merge was a mistake, erase it." --quit is for "stop calling this a merge, but keep what I already fixed." Most git abandon merge advice only ever mentions --abort, which throws the resolution work out along with the merge; --quit is the half that keeps it.

Undoing a Merge You Already Committed

Once git commit finishes a merge, MERGE_HEAD is gone and --abort is off the table. If that merge commit is still local — you haven't pushed, and nobody else can have pulled it — this is the simpler of the two post-commit situations, because rewriting history is safe when only you can see it. It's also what people are after when they search git undo last merge or git undo local merge: one merge commit sitting at the tip of a branch nobody else has fetched. The mechanics are the same ones behind undoing the last commit — a merge commit is still just a commit at the tip — with one extra wrinkle: it has two parents, which is what changes the answer the moment that commit reaches a remote.

# Confirm the merge commit is unpushed
git log origin/main..HEAD
# shows the merge commit if it hasn't reached the remote

# Remove it
git reset --hard HEAD~1

A search for "git reset merge" lands on both halves of this: git reset --merge is the mid-merge form --abort runs internally, and git reset --hard HEAD~1 is the post-commit one. The second moves your branch pointer back one commit — off the merge commit, onto whatever was there before you ran git merge — and overwrites every tracked file in the working directory to match. This is a genuinely destructive command: unlike --abort's reset --merge behavior, plain --hard does not preserve unrelated uncommitted changes. Anything uncommitted gets wiped, no exceptions, no autostash safety net. If you have work in progress unrelated to the merge, commit or stash it first. For the fuller picture of what --hard, --soft, and --mixed each preserve, see the dedicated guide on git reset --hard.

ORIG_HEAD works here too, immediately after the merge: git reset --hard ORIG_HEAD does the identical thing to HEAD~1 right after a merge commit, since Git set ORIG_HEAD to your pre-merge position when the merge ran. The difference matters once you've run anything else since — another reset, a rebase, a pull — because each of those overwrites ORIG_HEAD with its own "before" position, silently making it point somewhere other than pre-merge. HEAD~1 stays correct regardless of what ran since, as long as the merge commit is still the single most recent commit on the branch.

Same merge commit, different safety rules once it reaches origin M merge commit committed, not pushed log origin/main..HEAD git push M on origin/main others may have fetched it no longer local-only git reset --hard HEAD~1 safe — nobody else has it git revert -m 1 <hash> reset --hard would diverge now
The only thing that changes between the two states is whether origin/main already has commit M — that alone decides between reset --hard and revert.

Undoing a Merge You Already Pushed: git revert -m

If the merge commit already reached a shared remote — anyone else could have fetched or pulled it — git reset --hard is the wrong tool. Rewriting a branch other people have based work on forces everyone to reconcile diverging history, usually with a confusing force-push. The safe move is git revert, which adds a new commit undoing the change instead of deleting the old one — the only git undo merge move that stays safe on a branch other people already have.

Merge commits need one extra flag that a plain commit revert doesn't. Try it without the flag and Git refuses outright:

git revert a1b2c3d
# error: commit a1b2c3d is a merge but no -m option was given.

That error exists because a merge commit has two (or more) parents, and "undo this commit" is ambiguous without saying which parent to treat as the baseline. -m 1 tells git revert to use parent 1 — by convention, the branch you were on when you ran git merge (main, typically) — as mainline, and produce a commit that removes everything the merge introduced relative to that parent. The git-revert documentation spells out the parent-numbering rule in full:

git revert -m 1 a1b2c3d
# creates a new commit undoing the merge's changes
# the original merge commit a1b2c3d stays in history, untouched

The merge commit itself is never deleted — revert only ever adds. History keeps a permanent, honest record: the feature merged in at commit A, then got reverted at commit B. If you later decide the feature should ship after all, reverting a revert of a merge needs its own extra care around which parent is mainline the second time; the full walkthrough, including the exact gotcha with re-merging after a revert, is in the guide to reverting a revert commit.

Reverting a merge needs -m to say which parent is the baseline A main (before merge) merge commit M (2 parents) R revert commit undoes M's changes parent 1 — mainline git revert -m 1 F feature (last commit) parent 2 — feature branch git revert a1b2c3d error: no -m option was given git revert -m 1 a1b2c3d -m 1 = mainline is the baseline
M has two parents — mainline A and feature-tip F — so revert needs -m 1 to know which side is the baseline; the result, R, undoes M's changes without deleting M itself.

Cancel Merge on GitHub: What the UI Can and Can't Do

"Cancel merge GitHub" almost always means one of two different requests, and GitHub's interface draws a hard line between them.

Before the merge button is clicked: canceling is trivial — close the pull request without merging it, or push more commits and merge later. This is the only case where cancel merge GitHub means what it sounds like: nothing has touched the target branch yet, so there's nothing to undo. Closing the PR leaves both copies of the branch alive, so deleting the local branch is a separate cleanup step once the work is genuinely dead.

After the merge button is clicked: there is no GitHub Merged badge button that deletes the merge from history. What GitHub does offer, on a merged PR's page, is a Revert button — and it does exactly what the section above described from the terminal: it opens a brand-new pull request whose diff is the equivalent of git revert -m 1 against the merge commit. You still have to review and merge that second PR for the revert to take effect, as GitHub's own documentation on reverting a pull request confirms. The original merge commit is never removed; a second commit on top cancels its effect. That's the actual mechanism behind "how to revert a merged pull request" — a new PR, not an un-merge.

There is one narrow exception where GitHub does let you truly erase a merge from a branch's history: force-pushing to that branch directly (with sufficient permissions and no branch protection blocking it) after resetting locally with the commands from the two sections above. That's a repository-history rewrite, not a GitHub feature — GitHub just doesn't stop you from pushing it, unless branch protection rules do. On any branch with more than one contributor, that's the same "don't rewrite shared history" problem as the local case, just enforced through push permissions instead of good judgment.

Stashes, Autostash, and Squash Merges: The Edge Cases

Three edge cases trip people up specifically because most tutorials never mention them at all.

MERGE_AUTOSTASH. Since Git 2.27 (June 2020), merge.autoStash exists as a config option — off by default, so you have to opt in. Once it's set (or you pass --autostash directly), starting a merge with uncommitted local changes no longer blocks the merge with "error: Your local changes... would be overwritten." Instead, Git stashes those changes automatically before the merge starts. If the merge succeeds, the stash reapplies automatically at the end. If you run git merge --abort, that same autostash reapplies as the last step of the abort — your pre-merge uncommitted changes come back untouched, exactly as if the merge never happened. This is genuinely useful and genuinely undocumented in most guides: check whether you've enabled it with git config merge.autoStash.

# Enable autostash for every future merge
git config --global merge.autoStash true

# Or use it for one merge only
git merge --autostash feature-branch

Autostash is also where the git reset merge distinction earns its keep. git reset --merge, the command --abort is built on, respects MERGE_AUTOSTASH the same way. Plain git reset --hard, by contrast, does not restore an autostash — if you abandon a merge with --hard instead of --abort, check git stash list for a leftover autostash entry that never got reapplied, since --hard skips that step.

Squash merges have no MERGE_HEAD at all. git merge --squash feature-branch takes every commit on feature-branch, combines their changes into your working directory and index as one staged changeset — and deliberately does not write MERGE_HEAD, because a squash merge produces a single-parent commit, not a real merge commit. Try to abort one the normal way and Git tells you there's nothing to abort, even though you're clearly mid-operation with staged changes sitting there:

git merge --squash feature-branch
git merge --abort
# fatal: There is no merge to abort (MERGE_HEAD missing).

The fix is to treat it like any other staged-but-uncommitted change, not like a merge: git reset --merge HEAD unstages the squash changes and restores tracked files, or git reset --hard HEAD for the same effect when you don't care about preserving anything else uncommitted. Either works because there's no MERGE_HEAD-gated state to clean up — just ordinary staged changes against a normal, single-parent HEAD.

git stash and an in-progress merge don't mix the way people expect. Stashing the conflict, aborting, then unstashing later sounds like a clean workaround, but it's less reliable than it looks. Running git stash while conflict markers are still unresolved in your files stashes the conflicted state, but git status will still show you as mid-merge afterward in some Git versions, because plain stash doesn't touch MERGE_HEAD. If you want out of the merge and want to preserve your resolution attempts, git merge --quit (covered above) is the more predictable tool than stashing a half-resolved conflict and hoping to reconstruct it later.

Recovering From the Wrong Undo

Ran git reset --hard when you meant --merge? Aborted, then realized you actually wanted that merge? As long as the commits existed at some point, git reflog almost always gets them back — Git doesn't delete commit objects immediately, it just stops pointing at them, and unreferenced objects stick around until garbage collection runs (default gc.pruneExpire is 2 weeks for unreachable objects).

git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~1
# 9f8e7d6 HEAD@{1}: merge feature-branch: Merge made by the 'ort' strategy.
# ...

# Get the merge commit back
git reset --hard HEAD@{1}
# or, by hash, directly:
git reset --hard 9f8e7d6

git reflog lists every position HEAD has occupied in this local repository recently, newest first, each one addressable as HEAD@{n}. Find the entry from right before the reset or abort you regret, then git reset --hard onto that entry's hash restores the branch to that exact state — files, index, and all. This works for undoing an over-aggressive --hard reset, restoring a merge you aborted and then wanted back, or recovering from almost any local history mistake, with one caveat: the reflog is local-only, per-clone, and typically retained for 90 days by default (gc.reflogExpire) — it's not a substitute for pushing real work to a remote.

If a plain git reset --hard discarded uncommitted changes that were never committed anywhere — never part of any commit object — the reflog can't help, because there was never a commit to point back to. That's the one case with no recovery path, which is exactly why the earlier sections repeatedly flag --hard's lack of an uncommitted-work safety net compared to --abort or --merge.

git reflog remembers where HEAD has been — reset --hard onto the right entry undoes the mistake $ git reflog HEAD@0 reset: moving to HEAD~1 HEAD@1 merge feature-branch: Merge made by the 'ort' strategy HEAD@2 commit: earlier work... … older entries continue git reset --hard HEAD@1 restores files, index, and HEAD to exactly that reflog moment Reflog is local-only, kept ~90 days by default (gc.reflogExpire). It's not a substitute for pushing real work to a remote.
Find the entry from right before the reset or abort you regret, then reset --hard onto its hash — files, index, and HEAD all come back.

Reading the Merge Before You Abort It

Before you decide whether to abort, quit, or push through a conflict, it usually helps to actually see the two versions of a conflicted file rather than parse <<<<<<< markers in place. Git exposes both sides directly through its index, no merge tool required:

# Your side (stage 2) and the incoming side (stage 3) of a conflicted file
git show :2:src/api/retry.ts > /tmp/ours.ts
git show :3:src/api/retry.ts > /tmp/theirs.ts

# Or, if you already aborted and want to see what the merge would have changed
git show HEAD~1:src/api/retry.ts > /tmp/before.ts
git show HEAD:src/api/retry.ts   > /tmp/after.ts

:2:path and :3:path pull the "ours" and "theirs" blobs straight out of the merge conflict's index stages — the exact two versions Git couldn't reconcile automatically. Pasting both into a two-pane viewer with syntax highlighting reads faster than scanning inline conflict markers, especially in a file with several separate conflict regions. Diff Checker, a free Chrome extension, is built for exactly this: paste one version into each pane and it lines them up with word-level highlighting across 17 languages, with a "Show Diff Only" mode that collapses everything that didn't change so a long file doesn't bury the two or three lines that actually conflict. Everything runs client-side in the browser — nothing gets uploaded, which matters when the file in question is proprietary code you're not about to paste into a random web form.

This isn't a git client and doesn't read your repository — it's a plain two-pane text compare, and you feed it whatever git show already extracted. That's deliberately narrow: for checking out the branches involved in the first place, see checking out a remote branch, and for a broader walkthrough of comparing arbitrary file versions from the command line, git diff between two files covers the full set of flags. Once you've resolved a conflict and want a last look before committing, the same ours/theirs extraction trick works after a cherry-pick conflict too — the stages are populated the same way.

Every Command, Side by Side

Every git merge abort option and alternative from this guide in one table — what each actually does, when it's the right call, and exactly how much uncommitted work it risks.

Command What it does When to use Risk to uncommitted work
git merge --abort Resets tracked files to pre-merge state, clears MERGE_HEAD/MSG/MODE, reapplies any autostash Merge is unresolved or resolved-but-uncommitted (MERGE_HEAD still exists) Low — preserves unrelated pre-merge uncommitted changes
git merge --quit Clears MERGE_HEAD/MSG/MODE only — leaves working tree and index exactly as they are You've done real conflict-resolution work and want to stop being "mid-merge" without losing it None — nothing in the working tree is touched
git reset --merge MERGE_HEAD The explicit form of what --abort runs internally, mid-merge Same as --abort; rarely typed directly, useful to know it's the same mechanism Low — same preservation behavior as --abort
git reset --hard ORIG_HEAD Moves HEAD and working tree back to wherever they were immediately before the merge ran Right after a completed merge commit, before anything else touches ORIG_HEAD High — discards all uncommitted changes, no exceptions
git reset --hard HEAD~1 Moves the branch pointer back one commit and overwrites the working tree to match Merge is committed but not pushed — nobody else could have it High — discards all uncommitted changes, no exceptions
git revert -m 1 <hash> Adds a new commit that undoes the merge's changes; original merge commit stays in history Merge commit is already pushed or shared with others — never rewrite shared history None — purely additive, doesn't touch the working tree beyond the revert's own changes
git checkout -m . Re-checks-out conflicted paths, restoring Git's automatic (non-conflicting) merge result and re-inserting conflict markers where needed You edited a conflicted file's markers by hand and want to throw away just that edit, not the whole merge Low — scoped to files passed, not the whole merge state
Three ways to leave a merge, and what each one touches git merge --abort MERGE_HEAD cleared WORKING TREE reset to pre-merge state UNCOMMITTED WORK autostash reapplied — safe git merge --quit MERGE_HEAD cleared WORKING TREE left exactly as-is UNCOMMITTED WORK untouched — nothing reset git reset --hard HEAD~1 MERGE_HEAD already gone (post-commit) WORKING TREE overwritten to match HEAD~1 UNCOMMITTED WORK discarded — no exceptions
--abort and --quit both only apply while MERGE_HEAD exists; reset --hard HEAD~1 is the post-commit tool, and it's the only one of the three with no safety net for uncommitted work.

For the canonical, source-level reference on every flag covered here, see the official git-merge documentation and git-reset documentation. One case in this area isn't an undo at all: if the merge itself was right and only its commit message came out wrong, amending the last commit rewrites that message without touching either parent.

Frequently Asked Questions

How do I undo a merge in Git?

It depends what stage the merge is at. Mid-merge, with conflict markers still in your files, run git merge --abort to throw away the merge and go back to exactly where you were. If the merge already completed and produced a merge commit, and you haven't pushed it, git reset --hard HEAD~1 removes that commit and puts you back on the pre-merge state. If the merge commit is already pushed and other people may have it, don't rewrite history — run git revert -m 1 <merge-commit-hash> instead, which creates a new commit that undoes the merge's changes without deleting the original commit.

What's the difference between git merge --abort and git reset?

git merge --abort only works while a merge is actively in progress — it reads MERGE_HEAD, which Git deletes the moment the merge finishes (successfully or as a commit). It's roughly equivalent to git reset --merge MERGE_HEAD: it restores tracked files to their pre-merge state and preserves unrelated uncommitted changes that existed before the merge started. Plain git reset --hard HEAD~1, by contrast, works on a merge that's already committed — it moves the branch pointer back one commit and wipes the working directory to match, discarding any uncommitted changes with no exceptions. Once a merge is committed, --abort refuses to run at all, with "fatal: There is no merge to abort."

How do I revert a merged pull request on GitHub?

GitHub's pull request page shows a Revert button after a PR merges, but it doesn't un-merge anything — it opens a new pull request containing the equivalent of git revert -m 1 against the merge commit. Merging that new PR adds a commit that undoes the changes; the original merge commit stays in history. There is no GitHub UI action that deletes a merge after the fact once it's on a shared branch — that's what git revert exists for. Before the merge happens, closing the PR without merging is the actual cancel.

What happens to uncommitted changes when I abort a merge?

If you had uncommitted changes before running git merge and you enabled autostash (merge.autoStash, available since Git 2.27 but off by default), Git stashes them as MERGE_AUTOSTASH and reapplies that stash once git merge --abort finishes, so they come back untouched. Without autostash, Git refuses to start a merge that would overwrite those changes in the first place. Uncommitted changes that appeared during the merge itself — your resolved or half-resolved conflict edits — are discarded; --abort resets tracked files to their pre-merge contents. Untracked files Git didn't need to touch during the merge are left alone either way. The one documented edge case: if you had uncommitted changes to files the merge itself needed to touch, Git's docs warn --abort can fail to perfectly reconstruct the original state — rare, but worth a git status check afterward.

How do I handle a merge conflict without aborting?

Open each file git status lists under "Unmerged paths," resolve the conflict markers (<<<<<<<, =======, >>>>>>>) by hand or with git checkout --ours <file> for your side and --theirs for the incoming one, then git add <file> for each resolved file and git commit with no message argument to let Git generate the merge commit message. Reading both versions of a conflicted file side by side before touching the markers — with git show :2:path for your side and git show :3:path for theirs — makes the resolution faster than parsing markers directly in a lot of cases.

Can I abort a merge after resolving conflicts but before committing?

Yes. As long as MERGE_HEAD still exists in .git — which is true right up until you run git commitgit merge --abort works, even if every conflict is already resolved and staged. It discards the staged resolutions along with everything else and returns you to the pre-merge state. This is the same command whether you're bailing at the first conflict marker or after resolving all of them and having second thoughts.