git revert -n and -m: Safe Undo in Shared History
git revert <commit> records a new commit that reverses the changes of <commit>, the safe undo for shared branches; -n stages without committing and -m reverts a merge.
Unlike reset, revert does not rewrite history, so it is the right tool on branches others have pulled. The flags worth knowing are -n to batch several reverts into one commit and -m to revert a merge at all.
What it does
git revert computes the inverse of a commit and applies it as a new commit, leaving the original in history. -n (--no-commit) applies the inverse to the index/working tree without committing, so you can revert several commits and commit once. -m N reverts a merge relative to parent N.
Common usage
git revert abc1234
# revert several commits into one new commit
git revert -n abc1234 def5678
git commit -m "Revert bad batch"
# revert a merge commit (keep the first-parent line)
git revert -m 1 9f8e7d6Options
| Flag | What it does |
|---|---|
| -n / --no-commit | Apply the inverse without creating a commit |
| -m <parent-number> | Revert a merge relative to that parent |
| --continue | Resume after resolving conflicts |
| --abort | Cancel the in-progress revert |
| -e / --edit | Edit the generated commit message |
| --no-edit | Accept the default message (good for CI) |
In CI
revert needs a committer identity; set git config user.email/user.name in the job. Use --no-edit so it does not block on an editor. Reverting a merge with -m is one-directional: re-merging the same branch later will not re-introduce the changes unless you revert the revert, a common surprise in release pipelines.
Common errors in CI
"error: commit <sha> is a merge but no -m option was given" means add -m 1. "error: could not revert <sha>... after resolving the conflicts" is a conflict; resolve and --continue or --abort. "fatal: empty commit set passed" means the revert produced no change (already reverted).
Using this in CI
CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.
- uses: actions/checkout@v4
with:
fetch-depth: 0 # history, tags, and git describe all need this
- run: |
git rev-parse --is-shallow-repository # expect false
git rev-parse --abbrev-ref HEAD # prints HEAD when detached