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.
59 lines
2.0 KiB
TypeScript
59 lines
2.0 KiB
TypeScript
import * as git from "../git.ts"
|
|
import * as vm from "../vm.ts"
|
|
import { spinner } from "../spinner.ts"
|
|
import { die } from "../fmt.ts"
|
|
import { requireSession } from "./helpers.ts"
|
|
|
|
export async function action(branch: string) {
|
|
const { session } = await requireSession(branch)
|
|
const worktree = session.worktree
|
|
|
|
if (await git.isDirty(worktree)) {
|
|
die(`Branch "${branch}" has unsaved changes. Run "sandlot save ${branch}" first.`)
|
|
}
|
|
|
|
const main = await git.mainBranch(worktree)
|
|
|
|
if (!await git.hasNewCommits(worktree)) {
|
|
die(`Branch "${branch}" has no commits beyond ${main}.`)
|
|
}
|
|
|
|
const base = await git.mergeBase(main, "HEAD", worktree)
|
|
const originalHead = await git.headRef(worktree)
|
|
|
|
const spin = spinner("Squashing", branch)
|
|
|
|
try {
|
|
await git.resetSoft(base, worktree)
|
|
|
|
spin.text = "Starting container"
|
|
await vm.ensure((msg) => { spin.text = msg })
|
|
|
|
spin.text = "Generating commit message"
|
|
const diff = await git.diffStaged(worktree)
|
|
|
|
if (!diff.trim()) {
|
|
await git.resetSoft(originalHead, worktree)
|
|
spin.fail("No changes after squash")
|
|
return
|
|
}
|
|
|
|
const gen = await vm.claudePipe(
|
|
diff,
|
|
"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.",
|
|
)
|
|
|
|
const msg = (gen.exitCode === 0 && gen.stdout.trim()) || `squash ${branch}`
|
|
await git.commit(msg, worktree)
|
|
|
|
spin.succeed(`Squashed ${branch} into a single commit`)
|
|
} catch (err) {
|
|
const restored = await git.resetSoft(originalHead, worktree).then(() => true).catch(() => false)
|
|
const message = err instanceof Error ? err.message : String(err)
|
|
spin.fail(restored
|
|
? `Squash failed, changes restored: ${message}`
|
|
: `Squash failed and rollback failed — check "git reflog" in the worktree: ${message}`)
|
|
process.exit(1)
|
|
}
|
|
}
|