Move reset into try block so rollback covers all failure paths

Hoist mainBranch lookup before the new-commits check to avoid a
duplicate call, and report clearly when rollback itself fails so
the user knows to consult git reflog.
This commit is contained in:
Chris Wanstrath 2026-04-10 08:19:58 -07:00
parent 27afe67aec
commit f1825c65da

View File

@ -12,20 +12,20 @@ export async function action(branch: string) {
die(`Branch "${branch}" has unsaved changes. Run "sandlot save ${branch}" first.`) die(`Branch "${branch}" has unsaved changes. Run "sandlot save ${branch}" first.`)
} }
if (!await git.hasNewCommits(worktree)) {
const main = await git.mainBranch(worktree) const main = await git.mainBranch(worktree)
if (!await git.hasNewCommits(worktree)) {
die(`Branch "${branch}" has no commits beyond ${main}.`) die(`Branch "${branch}" has no commits beyond ${main}.`)
} }
const main = await git.mainBranch(worktree)
const base = await git.mergeBase(main, "HEAD", worktree) const base = await git.mergeBase(main, "HEAD", worktree)
const originalHead = await git.headRef(worktree) const originalHead = await git.headRef(worktree)
const spin = spinner("Squashing", branch) const spin = spinner("Squashing", branch)
try {
await git.resetSoft(base, worktree) await git.resetSoft(base, worktree)
try {
spin.text = "Starting container" spin.text = "Starting container"
await vm.ensure((msg) => { spin.text = msg }) await vm.ensure((msg) => { spin.text = msg })
@ -43,17 +43,16 @@ export async function action(branch: string) {
"Write a commit message summarizing all changes. Subject line: imperative mood, max 72 characters, no period. Add a blank line then a concise body with the key changes as bullet points. Output only the raw commit message, no quotes or markdown.", "Write a commit message summarizing all changes. Subject line: imperative mood, max 72 characters, no period. Add a blank line then a concise body with the key changes as bullet points. Output only the raw commit message, no quotes or markdown.",
) )
if (gen.exitCode === 0 && gen.stdout.trim()) { const msg = (gen.exitCode === 0 && gen.stdout.trim()) || `squash ${branch}`
await git.commit(gen.stdout.trim(), worktree) await git.commit(msg, worktree)
} else {
await git.commit(`squash ${branch}`, worktree)
}
spin.succeed(`Squashed ${branch} into a single commit`) spin.succeed(`Squashed ${branch} into a single commit`)
} catch (err) { } catch (err) {
await git.resetSoft(originalHead, worktree).catch(() => {}) const restored = await git.resetSoft(originalHead, worktree).then(() => true).catch(() => false)
const message = err instanceof Error ? err.message : String(err) const message = err instanceof Error ? err.message : String(err)
spin.fail(`Squash failed, changes restored: ${message}`) spin.fail(restored
? `Squash failed, changes restored: ${message}`
: `Squash failed and rollback failed — check "git reflog" in the worktree: ${message}`)
process.exit(1) process.exit(1)
} }
} }