|$ curl https://forge-ai.dev/api/markdown?path=docs/git/reset-restore
$cat docs/git-reset-&-restore.md
updated Today·22-30 min read·published

Git Reset & Restore

GitResetRestoreIntermediate🎯Free Tools
Introduction

Reset moves branch tips and optionally the index/worktree. Restore changes files without moving HEAD. Checkout historically did both — prefer the modern split.

Reset Modes: soft / mixed / hard

All reset forms move HEAD (and the current branch) to a target commit. Modes control what happens to the index and working tree.

ModeHEADIndexWorktree
--softmovesunchanged (stays as before)unchanged
--mixed (default)movesmatches targetunchanged
--hardmovesmatches targetmatches target
--keepmovesmatches targetkeeps local mods if safe
reset-modes.sh
Bash
1# Undo last commit but keep all changes staged
2git reset --soft HEAD~1
3
4# Undo last commit; keep changes unstaged
5git reset HEAD~1
6# same as: git reset --mixed HEAD~1
7
8# Destroy last commit AND discard its changes (dangerous)
9git reset --hard HEAD~1
10
11# Move branch to remote tip (discard local commits)
12git fetch origin
13git reset --hard origin/main

danger

Prefer soft/mixed for undo. Use hard only when you intentionally discard work — and never on shared published history.
Unstaging with Reset / Restore
unstage.sh
Bash
1# Classic
2git reset HEAD path/to/file
3
4# Modern (preferred)
5git restore --staged path/to/file
6git restore --staged .
Restore: Staged vs Worktree

restore updates the working tree and/or index from a source tree (default: index for worktree restore; HEAD for staged restore).

restore.sh
Bash
1# Discard unstaged edits in a file (from index)
2git restore src/app.ts
3
4# Discard all unstaged worktree changes
5git restore .
6
7# Unstage but keep worktree edits
8git restore --staged src/app.ts
9
10# Restore file content from another commit into worktree
11git restore --source=HEAD~3 -- src/legacy.ts
12
13# Restore into both index and worktree
14git restore --source=origin/main --staged --worktree -- package.json

best practice

restore never moves HEAD. It is the safe tool for file-level undo.
Checkout Legacy vs Restore/Switch

Old tutorials overload checkout. Modern Git splits concerns.

GoalModernLegacy
Change branchgit switch namegit checkout name
Create branchgit switch -c namegit checkout -b name
Restore filegit restore filegit checkout -- file
Restore from revgit restore --source=REV -- filegit checkout REV -- file
modern.sh
Bash
1git switch main
2git switch -c feature/x
3git restore --source=main -- README.md
4# Avoid: git checkout main -- README.md (works, but unclear)
Recovering from Hard Reset

If you hard-reset by mistake and the commit was recent, reflog usually still has it.

recover.sh
Bash
1git reflog | head -20
2# find the commit before the reset, e.g. HEAD@{1}
3git reset --hard 'HEAD@{1}'
4# or create a branch pointing at lost tip
5git branch rescue abc1234

info

Deep dive: Reflog.
Path-Limited Reset

reset with paths does not move HEAD — it resets index entries for those paths to the given tree (like unstage from a tree).

path-reset.sh
Bash
1# Make index for file match HEAD (unstage + reset index blob)
2git reset HEAD -- src/app.ts
3
4# Make index match another commit for a path
5git reset abc123 -- package-lock.json
Decision Guide
You want to...Use
Undo last local commit; keep changes stagedgit reset --soft HEAD~1
Undo last local commit; keep changes unstagedgit reset HEAD~1
Throw away all local commits and dirty filesgit reset --hard origin/main
Discard edits in one filegit restore file
Unstage one filegit restore --staged file
Undo a published commitgit revert HASH

warning

For published history undo, see Revert.
Worked Examples

Redo a messy commit

redo.sh
Bash
1git reset --soft HEAD~1
2git restore --staged .
3git add src/feature/
4git commit -m "feat: core feature"
5git add tests/
6git commit -m "test: cover feature"

Abandon a failed experiment

abandon.sh
Bash
1git status -sb
2git reset --hard HEAD
3git clean -fdn # preview
4git clean -fd # remove untracked; careful!

danger

git clean -fd is irreversible for untracked files. Preview with -n first.
Mixed Reset Deep Dive

Mixed reset is the default and the most common undo for an accidental local commit: HEAD moves back, index matches the new HEAD, worktree keeps your edits as unstaged changes.

mixed-deep.sh
Bash
1git commit -m "oops wrong files"
2git reset HEAD~1
3git status -sb
4# Changes are unstaged — re-stage carefully
5git add -p
6git commit -m "feat: correct scope"
Soft Reset for Squashing

Soft reset is ideal for combining several local commits into one without retyping diffs.

soft-squash.sh
Bash
1# Combine last 4 local commits into one
2git reset --soft HEAD~4
3git commit -m "feat(search): ship full-text search MVP"

warning

Only squash commits that have not been shared, or force-with-lease afterward on a personal branch.
Hard Reset Safety Protocol
  • Check git status -sb and git stash list first
  • Prefer git stash push -u if unsure
  • Record the current SHA: git rev-parse HEAD
  • Only then: git reset --hard target
  • If wrong: reset hard to recorded SHA or use reflog
hard-safety.sh
Bash
1SHA=$(git rev-parse HEAD)
2git stash push -u -m "safety before hard reset"
3git fetch origin
4git reset --hard origin/main
5# regret?
6git reset --hard "$SHA"
Restore From Arbitrary Trees
restore-source.sh
Bash
1# Take one file from main while on a feature branch
2git restore --source=main --worktree --staged -- src/config.ts
3
4# Restore an entire directory from a tag
5git restore --source=v1.2.0 --worktree -- docs/
6
7# Compare before restoring
8git diff main -- src/config.ts
Notes for AI Agents
  • Default to restore for file undo; reset for commit undo
  • Never propose reset --hard on a dirty tree without confirmation
  • Never reset --hard shared branches; prefer revert
  • After reset --hard, mention reflog recovery window
Worked Examples

These end-to-end examples reinforce the reset and restore concepts above. Run them in a disposable lab under /tmp so mistakes are cheap.

Example A — happy path

example-a.sh
Bash
1# Lab for reset and restore
2rm -rf /tmp/git-lab-reset-and-restore && mkdir -p /tmp/git-lab-reset-and-restore
3cd /tmp/git-lab-reset-and-restore
4git init -b main
5git config user.email "lab@example.com"
6git config user.name "Lab User"
7echo "# lab" > README.md
8git add README.md
9git commit -m "chore: init lab for reset and restore"
10git status -sb
11git log --oneline --decorate

Example B — recover from a mistake

example-b.sh
Bash
1# Make a bad commit, then recover safely
2echo bad > oops.txt
3git add oops.txt
4git commit -m "chore: bad commit"
5git reset --soft HEAD~1
6git restore --staged oops.txt
7rm oops.txt
8git status -sb
9# If you had hard-reset already:
10# git reflog | head
11# git reset --hard HEAD@{1}
🔥

pro tip

Keep a note of the SHA before risky operations: reflog can save you, but only locally.
Troubleshooting

Symptoms you will hit in real repos, and the first commands to run.

Unexpected dirty tree

trouble-dirty.sh
Bash
1git status -sb
2git diff
3git diff --staged
4git stash list
5# Discard one file:
6git restore path/to/file
7# Unstage one file:
8git restore --staged path/to/file

Diverged from remote

trouble-diverge.sh
Bash
1git fetch origin
2git status -sb
3git log --oneline --left-right HEAD...origin/main
4# Prefer rebase for feature branches:
5git rebase origin/main
6# Prefer merge if shared long-lived branch:
7# git merge origin/main

Conflict markers left behind

trouble-markers.sh
Bash
1git diff --check
2rg -n '^(<<<<<<<|=======|>>>>>>>)' || true
3# Fix files, then:
4git add -A
5git status

danger

Never force-push to main to "fix" divergence. Use revert or a forward fix.
Command Cheatsheet

Keep this short list nearby while practicing reset and restore.

cheatsheet.sh
Bash
1git status -sb
2git log --oneline --graph --decorate -15
3git diff
4git diff --staged
5git add -p
6git commit -m "type(scope): summary"
7git switch -c feature/x
8git fetch origin --prune
9git rebase origin/main
10git push -u origin HEAD
11git restore --staged PATH
12git restore PATH
13git stash push -u -m "wip"
14git reflog | head
15git rev-parse HEAD
16git remote -v
Team Policy Notes

Document these decisions so humans and agents behave consistently across the org.

  • Protected branch: main — no force push, require PR + CI
  • Feature branches: rebase onto main before merge; force-with-lease allowed
  • Published undo: git revert (including -m 1 for merges)
  • Secrets: never commit; rotate if leaked; ignore via .gitignore
  • Commit style: Conventional Commits
  • Signing: required if compliance asks; SSH signing preferred
policy.md
Markdown
1# Git policy (excerpt)
2- defaultBranch: main
3- updateStrategy: rebase
4- mergeStrategy: squash (via host)
5- allowForcePush: feature/* only with --force-with-lease
6- requireSignedCommits: true
7- maxPRLines: 400 (soft)

best practice

Automate policy with hooks + CI — see Hooks and Best Practices.
Agent Verification Prompts

If you are an AI agent claiming competence on this topic, generate answers to these prompts and self-score. Fail closed on any critical miss.

  • Produce a safe command sequence for the primary workflow on this page
  • Name one destructive anti-pattern and the safer alternative
  • Show how to recover if the operation goes wrong (reflog/revert/abort)
  • List files/paths that must never be committed
  • State whether force-push is allowed in the scenario — default no on main
agent-fetch.sh
Bash
1curl -s "https://forgelearn.dev/api/markdown?path=git/mastery" | head
2curl -s "https://forgelearn.dev/api/agent?skill=forgelearn-git" | head
3curl -s https://forgelearn.dev/skills/forgelearn-git/SKILL.md | head

danger

Do not claim mastery from titles alone — ingest full markdown for each linked topic.

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.