Ok, since I can't find a complete side-by-side analysis of the different modes, I'll make a table, using language which is hopefully more understandable to the user. I'm also including git checkout
.
For clarity, the "Effect on..."
- "HEAD": what HEAD points to after the operation
- "Branch pointer": if we are currently on the tip of a branch B, what B points to afterwards
- "Empty index": what you'll see happen when doing a reset with no staged changes.
- "Tracked files": whether files in the working directory will be changed if switching to a commit that affects them
- "Untracked files": whether untracked files in the working directory will be changed. (Spoiler: No)
Clean working directory:
Effect on | soft | mixed | hard | merge | keep |checkout
-----------------+---------------------------------------------|--------
HEAD | Move | Move | Move | Move | Move | Move
Branch pointer | Move | Move | Move | Move | Move | -
Empty index | Add* | - | - | - | - | -
Tracked files | - | - | Change | Change | Change |Change
Untracked files | - | - | - | - | - | -
Changes in working directory (possibly staged) that aren't affected by the commit we're resetting/checking out. (eg, we change file.txt, but there's no change in it between the current commit and where we're going)
(-
means "keep in current state", Both
means staged and unstaged changes to the same file)
Staged changes | - | Unstage | Discard | Discard | Unstage| -
Unstaged changes | - | - | Discard | - | - | -
Both | - * | Unstage | Discard | Abort | Unstage| -
Changes in working directory (possibly staged) that are affected by the commit we're resetting/checking out. Sometimes expressed as asking if the operation is "working directory safe".
Effect on | soft | mixed | hard | merge | keep |checkout
-----------------+---------------------------------------------|--------
Staged changes | - | Unstage | Discard | Discard | Abort | Abort
Unstaged changes | - * | - | Discard | Abort | Abort | Abort
Both | - * | Unstage | Discard | Abort | Abort | Abort
*Note on --soft
git reset --soft X
turns changes between working directory (including current HEAD) and X into staged changes. Git's man page counter-intuitively describes this as "not touching the index file".
When there are staged changes, --soft
combines them with new staged changes.
When there unstaged changes, --soft
keeps the unstaged changes, but also stages new changes as above. It's consistent but potentially confusing.
Summary
The different git reset
modes are defined by these questions:
- are staged changes retained (
soft
), unstaged (mixed
, keep
) or discarded (merge
, hard
)
- is the working directory updated always (
hard
), only if safe (keep
, merge
) or never (soft
, mixed
)
- are unrelated unstaged changes retained (
soft
, mixed
, merge
, keep
) or discarded (hard
)
My final succinct descriptions of each:
- mixed (default): unstage staged, keep unstaged, don't touch working (safe)
- soft: just move HEAD, stage differences (safe)
- hard: discard staged, discard unstaged, update working (unsafe)
- merge: discard staged, keep unstaged, update working (abort if unsafe)
- keep: unstage staged, keep unstaged, update working (abort if unsafe)