Git, CI/CD & GitOpsXV · ResetModes
Reset modes explained — --soft, --mixed, --hard, --merge, --keep
What you'll learn
- List the five reset modes and identify which trees each one moves
- Predict the resulting state of HEAD, the index, and the working tree after each mode
- Recognise --mixed as the default mode when no flag is given
- Choose between --soft, --mixed, --hard, --merge, and --keep for a given scenario
- Identify --hard as the only mode that touches the working tree
Prerequisites
Verified against Git 2.55.x teaching target; 2.40+ minimum · GitHub Actions continuous service; Aug 2026 documentation baseline · Argo CD v3.5.x teaching target; v3.0+ minimum · Flux v2.9.x · Sigstore Cosign v3.1.x · SLSA v1.2 · OCI Distribution Specification v1.1 · Git LFS v3.7.1 · Kubernetes (cross-course target) 1.36.x
git reset looks like one command but it is really five commands in
a trench coat. The single flag --mode decides what the command
does to each of the three trees — HEAD, the index, and the working
tree. Picking the wrong mode is the source of most “I lost my
work” stories in Git; picking the right mode makes reset a precise
surgical tool. The five modes are --soft, --mixed, --hard,
--merge, and --keep, and they answer the same question — “how
far back do I want to unwind?” — with five different blast radii.
The five modes and their blast radius
Each mode starts from the same place: a target commit
(git reset <commit>, where <commit> is required for --soft,
--hard, --merge, and --keep, and optional for --mixed).
The mode then decides which trees get rewound to match that
commit’s tree:
flowchart TB
A["target commit"] --> M{"which mode?"}
M --> S["--soft\nHEAD only"]
M --> X["--mixed (default)\nHEAD + index"]
M --> H["--hard\nHEAD + index + WT"]
M --> G["--merge\nHEAD + index, WT if safe"]
M --> K["--keep\nHEAD + WT, refuse if diverged"]
The matrix form makes the same point without the diagram noise:
| Mode | Moves HEAD | Resets index | Resets working tree |
|---|---|---|---|
--soft <commit> | yes | no | no |
--mixed <commit> (default) | yes | yes | no |
--hard <commit> | yes | yes | yes |
--merge <commit> | yes | yes | only if no local overwrite |
--keep <commit> | yes | no | yes, only if no local overwrite |
The two least-common modes — --merge and --keep — exist for
exactly the cases where --hard is too destructive and --mixed
is too conservative. --merge aborts with an error if any working
tree file would be overwritten by the reset, which is the safe
behaviour when the working tree holds uncommitted edits that
matter. --keep is the mirror image: it resets the index and
working tree together but refuses if either would clobber a local
modification.
COMMIT=abc1234
git reset --soft $COMMIT
git reset $COMMIT # default mode is --mixed
git reset --hard $COMMIT # destructive
git reset --merge $COMMIT # safe-ish
git reset --keep $COMMIT # safe-ish mirror of --merge
What “moves HEAD” actually means
“Moves HEAD” means the current branch pointer is rewritten to
point at <commit>. Whatever commits were strictly between the
previous tip and <commit> are now unreachable from any branch.
They are not deleted — the objects persist in .git/objects/,
and .git/logs/HEAD (the reflog) still records the old tip — but
they are eligible for garbage collection once the reflog entry
expires. This is the history-rewrite effect that makes reset
unsafe on pushed branches.
COMMIT=abc1234
git log --oneline -5 # tip before reset
git reset --mixed $COMMIT
git log --oneline -5 # tip after reset — the intervening commits are gone from history
What “resets the index” actually means
“Resets the index” means every entry in the staging area is
rewritten to match <commit>’s tree. Files that were staged but
did not exist in <commit> are unstaged. Files that existed in
<commit> but were not staged are now staged. The working tree
bytes are not changed by this step alone — the index simply stops
recording the working tree as “different from HEAD”.
This is the move behind the most common staging mistake: “I added
a file I should not have added.” git reset --mixed (or just
git reset) is exactly the operation that undoes the git add
without touching the file on disk.
What “resets the working tree” actually means
“Resets the working tree” means every file in the working
directory is overwritten with the version from <commit>’s tree.
Uncommitted edits are destroyed; untracked files (anything Git has
never been told about) are left alone. This is the only mode that
touches the working tree, and it is the source of every “git reset
ate my work” story.
—merge and —keep in practice
The two conditional modes are useful when the working tree holds
work that should not be lost but the branch needs to be rewound.
--merge is the right answer when the working tree changes are
unrelated to the target commit’s changes (a clean reset is safe;
a clobbering reset is refused). --keep is the right answer when
you want the working tree to match the target commit but you do
not want to lose local edits that would be overwritten.
Production discipline
- Default to
--mixed. The mode you do not specify is the one that does not touch the working tree. Unlearning this is the most common senior-engineer mistake on infrastructure repositories. - Never
--hardon a pushed branch. The mode is destructive on the index and the working tree and the branch pointer. On a shared branch, every collaborator must reconcile. - Reach for
--mergewhen--hardfeels right.--mergedoes the same thing as--hardminus the working-tree clobbering. If you find yourself reaching for--hardbecause you “want a clean slate”,--mergeis almost always the safer tool.
Cross-course references
- Git, CI/CD & GitOps — Part II (Three trees model) — the HEAD/index/working-tree model that this lesson refines.
- Git, CI/CD & GitOps — Part VI (Resetting and restore) — the index-level reset operations covered before the full mode survey here.
- Git, CI/CD & GitOps — Part XIV (Revert) — the additive alternative to reset; the contrast is reset (subtractive, history-rewriting) versus revert (additive, history-preserving).
- Linux for Production Sysadmins — Part XXVII (Backup and Recovery) — the analogue is filesystem restore granularity: pick the coarseness that loses the least work.
Quiz
Knowledge check · 4 questions
Q1. An engineer runs `git reset HEAD~1` with no mode flag on a feature branch. Which trees change?
Q2. Of the five `git reset` modes, only `--hard` rewrites the working tree bytes.
Q3. Name the default mode of `git reset` when no mode flag is given, and explain why that mode is the default.
Q4. Pick the right `git reset` mode for an engineer who needs to undo a commit but preserve uncommitted working tree edits, and explain why each alternative mode is wrong.
An engineer has just committed a Terraform plan to a feature branch and then realised the commit message is wrong. The working tree has additional uncommitted edits to a separate file that should not be lost. The engineer wants to redo the commit with a corrected message. The branch has not been pushed.
Passing score: 75%. Answers are checked in this browser.