git reset --soft/--mixed/--hard: The Three Modes
git reset moves the branch tip; --soft keeps the index and working tree, --mixed (default) resets the index but keeps the working tree, and --hard discards both.
The single most consequential difference in git reset is the mode, because --hard throws away uncommitted work. This page lays out exactly what each mode touches so a CI cleanup step does not delete more than intended.
What it does
git reset <commit> repoints the current branch to <commit>. --soft stops there, leaving your changes staged. --mixed also resets the index to match (changes become unstaged). --hard additionally overwrites the working tree to match the commit, discarding all uncommitted changes.
Common usage
# undo last commit, keep changes staged
git reset --soft HEAD~1
# unstage everything but keep edits
git reset --mixed HEAD # or just: git reset
# discard all local changes and match origin/main exactly
git reset --hard origin/mainOptions
| Mode | Moves branch | Resets index | Resets working tree |
|---|---|---|---|
| --soft | yes | no | no |
| --mixed (default) | yes | yes | no |
| --hard | yes | yes | yes |
| --keep | yes | yes | keeps local edits, aborts if they conflict |
| --merge | yes | yes | keeps unmerged changes, resets the rest |
In CI
git reset --hard origin/<branch> is the standard way to force a runner workspace to an exact state, but it silently destroys uncommitted artifacts; pair it with git clean to remove untracked files too. After --hard, the prior commits are still recoverable from the reflog (on a non-fresh clone) until gc runs.
Common errors in CI
"fatal: ambiguous argument 'origin/main'" means the ref was not fetched (shallow or missing remote-tracking ref). "error: Entry '<file>' not uptodate. Cannot merge." appears with --keep/--merge when local edits conflict. On a detached HEAD, reset moves HEAD itself, not a branch, which can surprise scripts expecting a branch to move.