Reset Hard Readback

Lists exactly what git reset --hard, clean, checkout -- . or stash drop would destroy, and offers to stash first.

StopsUncommitted work wiped by git reset

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/reset-hard-readback.json

The files land in your project. They're yours to edit.

Usage

reset-hard-readback-usage.tsx
import { ResetHardReadback } from "@/components/ui/reset-hard-readback"

<ResetHardReadback
  command={command}
  status={status}
  numstat={NUMSTAT}
  commits={"e07d55b Retry UPI collect on timeout\n5be0d13 Show amount in words on confirm\n1f2a9e0 Bump checkout copy"}
  stashes={[{ ref: "stash@{0}", sha: "4b8e2a1c9d7f3e5a0b6c8d2e4f1a3b5c7d9e0f2a", message: "On feature/checkout: half-done coupon field", files: [{ path: "src/checkout/Coupon.tsx", added: 41, removed: 3 }, { path: "src/lib/coupon.ts", added: 18, removed: 0 }] }]}
  onStash={(cmd) => wait(() => (setLog(`Ran: ${cmd}`), setStatus(CLEAN)))}
  onConfirm={(cmd) => wait(() => setLog(`Ran: ${cmd}`))}
  onCancel={() => setLog("Cancelled. Nothing ran.")}
/>

What it catches

  • Tracked edits that would be discarded, with +/- line counts
  • Staged new files and untracked/ignored files, secrets like .env and keys listed first
  • Commits the branch would move past, with unpushed ones marked
  • Stash entries dropped, with the sha to recover them
  • What the command leaves alone (e.g. submodules without --recurse-submodules)

Props

ResetHardReadback props
PropTypeDefault
command* (required)

The command about to run, e.g. `git reset --hard origin/main`, `git clean -fdx`, `git checkout -- .`, `git stash drop`.

string—
status* (required)

Output of `git status --porcelain=v2 --branch --ignored` (v1 and -z work too). Add --ignored for clean -x.

string—
numstat

Output of `git diff HEAD --numstat` for the +/- line counts.

string—
commits

For reset --hard to another commit: `git log --oneline <target>..HEAD` (or parsed commits), newest first.

string | Commit[]—
unpushed

The exact unpushed commits: `git rev-list

string | string[]—
stashes

For stash drop/clear: the stash list, newest first.

StashEntry[]—
onStash

Shows "Stash first". Receives the stash command that keeps everything at risk; may return a Promise.

(command: string) => void | Promise<void>—
onConfirm

Runs the command. May return a Promise; the button shows a pending state and ignores repeat clicks.

(command: string) => void | Promise<void>—
onCancel

Shows a Cancel button.

() => void—
maxRows

Rows to list per section before "Show more".

number6
messages

Any visible string.

Partial<ResetHardMessages>—

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Each loss group is a section labelled by its heading; the list is a labelled group
  • Long groups expand with a button instead of truncating silently
  • Outcome announced via role=status
  • Confirm receives focus when the readback opens; buttons set aria-busy while pending

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: In tooling that runs these commands for users, keep a reflog/backup and require explicit confirmation server-side; this component only describes the loss.

  • Only as complete as the status, numstat, commits and stashes you pass
  • Parses common forms of reset, clean, checkout/restore and stash drop; unusual flags may not be modelled
  • Does not run git; onStash and onConfirm are yours

What it stops

Uncommitted work wiped by git reset

Also known as

git reset --hard · git clean -fdx · git stash · discard changes · working tree · cli confirm · terminal · uncommitted changes

Field type

Dependencies

Pairs well with