Skip to main content
RunBook Academy

Git, CI/CD & GitOpsXV · ResetModes

Reset modes explained — --soft, --mixed, --hard, --merge, --keep

Advanced⏱ ~20 mingit

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

Not yet marked complete on this device.

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:

ModeMoves HEADResets indexResets working tree
--soft <commit>yesnono
--mixed <commit> (default)yesyesno
--hard <commit>yesyesyes
--merge <commit>yesyesonly if no local overwrite
--keep <commit>yesnoyes, 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

  1. 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.
  2. Never --hard on 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.
  3. Reach for --merge when --hard feels right. --merge does the same thing as --hard minus the working-tree clobbering. If you find yourself reaching for --hard because you “want a clean slate”, --merge is 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

  1. Q1. An engineer runs `git reset HEAD~1` with no mode flag on a feature branch. Which trees change?

  2. Q2. Of the five `git reset` modes, only `--hard` rewrites the working tree bytes.

  3. Q3. Name the default mode of `git reset` when no mode flag is given, and explain why that mode is the default.

  4. 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.