Refactor teardown logic into shared helper
This commit is contained in:
parent
156e4d9590
commit
c85af793cd
|
|
@ -1,33 +1,19 @@
|
||||||
import { join } from "path"
|
|
||||||
import { unlink } from "fs/promises"
|
|
||||||
import * as git from "../git.ts"
|
import * as git from "../git.ts"
|
||||||
import * as vm from "../vm.ts"
|
|
||||||
import * as state from "../state.ts"
|
|
||||||
import { die } from "../fmt.ts"
|
import { die } from "../fmt.ts"
|
||||||
|
import { requireSession, teardownSession } from "./helpers.ts"
|
||||||
|
|
||||||
export async function action(branch: string, opts: { force?: boolean } = {}) {
|
export async function action(branch: string, opts: { force?: boolean } = {}) {
|
||||||
const root = await git.repoRoot()
|
const { root, session } = await requireSession(branch)
|
||||||
const session = await state.getSession(root, branch)
|
|
||||||
|
|
||||||
if (!session) {
|
if (!opts.force && await git.isDirty(session.worktree)) {
|
||||||
die(`No session found for branch "${branch}"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const worktreeAbs = session.worktree
|
|
||||||
|
|
||||||
if (!opts.force && await git.isDirty(worktreeAbs)) {
|
|
||||||
die(`Branch "${branch}" has unsaved changes. Run "sandlot save ${branch}" first, or use -f to force.`)
|
die(`Branch "${branch}" has unsaved changes. Run "sandlot save ${branch}" first, or use -f to force.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
await vm.clearActivity(worktreeAbs, branch)
|
if (!opts.force && await git.isDirty(root)) {
|
||||||
|
die(`Working tree has uncommitted changes that may conflict with checkout. Commit or stash them first, or use -f to force.`)
|
||||||
|
}
|
||||||
|
|
||||||
await git.removeWorktree(worktreeAbs, root)
|
await teardownSession(root, branch, session.worktree)
|
||||||
.catch((e) => console.warn(`⚠ Failed to remove worktree: ${e.message}`))
|
|
||||||
|
|
||||||
await unlink(join(root, '.sandlot', branch))
|
|
||||||
.catch(() => {}) // symlink may not exist
|
|
||||||
|
|
||||||
await state.removeSession(root, branch)
|
|
||||||
|
|
||||||
await git.checkout(branch, root)
|
await git.checkout(branch, root)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,18 @@
|
||||||
import { join } from "path"
|
|
||||||
import { unlink } from "fs/promises"
|
|
||||||
import * as git from "../git.ts"
|
import * as git from "../git.ts"
|
||||||
import * as vm from "../vm.ts"
|
|
||||||
import * as state from "../state.ts"
|
|
||||||
import { die } from "../fmt.ts"
|
import { die } from "../fmt.ts"
|
||||||
|
import { requireSession, teardownSession } from "./helpers.ts"
|
||||||
|
|
||||||
export async function action(branch: string, opts: { force?: boolean } = {}) {
|
export async function action(branch: string, opts: { force?: boolean } = {}) {
|
||||||
const root = await git.repoRoot()
|
const { root, session } = await requireSession(branch)
|
||||||
const session = await state.getSession(root, branch)
|
|
||||||
|
|
||||||
if (!session) {
|
if (!opts.force && await git.isDirty(session.worktree)) {
|
||||||
die(`No session found for branch "${branch}"`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const worktreeAbs = session.worktree
|
|
||||||
|
|
||||||
if (!opts.force && await git.isDirty(worktreeAbs)) {
|
|
||||||
die(`Branch "${branch}" has unsaved changes. Run "sandlot save ${branch}" first, or use -f to force.`)
|
die(`Branch "${branch}" has unsaved changes. Run "sandlot save ${branch}" first, or use -f to force.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
await vm.clearActivity(worktreeAbs, branch)
|
await teardownSession(root, branch, session.worktree)
|
||||||
|
|
||||||
await git.removeWorktree(worktreeAbs, root)
|
|
||||||
.catch((e) => console.warn(`⚠ Failed to remove worktree: ${e.message}`))
|
|
||||||
|
|
||||||
await unlink(join(root, '.sandlot', branch))
|
|
||||||
.catch(() => {}) // symlink may not exist
|
|
||||||
|
|
||||||
await git.deleteLocalBranch(branch, root)
|
await git.deleteLocalBranch(branch, root)
|
||||||
.catch((e) => console.warn(`⚠ Failed to delete branch ${branch}: ${e.message}`))
|
.catch((e) => console.warn(`⚠ Failed to delete branch ${branch}: ${e.message}`))
|
||||||
|
|
||||||
await state.removeSession(root, branch)
|
|
||||||
|
|
||||||
console.log(`✔ Closed session ${branch}`)
|
console.log(`✔ Closed session ${branch}`)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
|
import { unlink } from "fs/promises"
|
||||||
import { $ } from "bun"
|
import { $ } from "bun"
|
||||||
import * as git from "../git.ts"
|
import * as git from "../git.ts"
|
||||||
import * as vm from "../vm.ts"
|
import * as vm from "../vm.ts"
|
||||||
import * as state from "../state.ts"
|
import * as state from "../state.ts"
|
||||||
import { spinner } from "../spinner.ts"
|
import { spinner } from "../spinner.ts"
|
||||||
import { die } from "../fmt.ts"
|
import { die } from "../fmt.ts"
|
||||||
import { action as closeAction } from "./close.ts"
|
|
||||||
import type { Session } from "../state.ts"
|
import type { Session } from "../state.ts"
|
||||||
|
|
||||||
/** Look up a session by branch, dying if it doesn't exist. */
|
/** Look up a session by branch, dying if it doesn't exist. */
|
||||||
|
|
@ -18,6 +18,19 @@ export async function requireSession(branch: string): Promise<{ root: string; se
|
||||||
return { root, session }
|
return { root, session }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Tear down a session: clear activity, remove worktree, unlink symlink, remove state. */
|
||||||
|
export async function teardownSession(root: string, branch: string, worktree: string): Promise<void> {
|
||||||
|
await vm.clearActivity(worktree, branch)
|
||||||
|
|
||||||
|
await git.removeWorktree(worktree, root)
|
||||||
|
.catch((e) => console.warn(`⚠ Failed to remove worktree: ${e.message}`))
|
||||||
|
|
||||||
|
await unlink(join(root, '.sandlot', branch))
|
||||||
|
.catch(() => {}) // symlink may not exist
|
||||||
|
|
||||||
|
await state.removeSession(root, branch)
|
||||||
|
}
|
||||||
|
|
||||||
/** Resolve conflict markers in files using Claude, then stage them. */
|
/** Resolve conflict markers in files using Claude, then stage them. */
|
||||||
export async function resolveConflicts(
|
export async function resolveConflicts(
|
||||||
files: string[],
|
files: string[],
|
||||||
|
|
@ -63,7 +76,8 @@ export async function mergeAndClose(branch: string, opts?: { squash?: boolean })
|
||||||
await squashCommit(branch, root)
|
await squashCommit(branch, root)
|
||||||
}
|
}
|
||||||
spin.succeed(`${label} ${branch} into current branch`)
|
spin.succeed(`${label} ${branch} into current branch`)
|
||||||
await closeAction(branch)
|
if (session) await teardownSession(root, branch, session.worktree)
|
||||||
|
await git.deleteLocalBranch(branch, root).catch(() => {})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +101,8 @@ export async function mergeAndClose(branch: string, opts?: { squash?: boolean })
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
await closeAction(branch)
|
if (session) await teardownSession(root, branch, session.worktree)
|
||||||
|
await git.deleteLocalBranch(branch, root).catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Generate a commit message for a squash-merge via Claude and commit. */
|
/** Generate a commit message for a squash-merge via Claude and commit. */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user