67 lines
2.4 KiB
TypeScript
67 lines
2.4 KiB
TypeScript
import { existsSync } from "fs"
|
|
import { rm } from "fs/promises"
|
|
import { $ } from "bun"
|
|
|
|
/** Get the repo root from a working directory. */
|
|
export async function repoRoot(cwd?: string): Promise<string> {
|
|
const result = await $`git rev-parse --show-toplevel`.cwd(cwd ?? ".").text()
|
|
return result.trim()
|
|
}
|
|
|
|
/** Get the current branch name. */
|
|
export async function currentBranch(cwd?: string): Promise<string> {
|
|
const result = await $`git rev-parse --abbrev-ref HEAD`.cwd(cwd ?? ".").text()
|
|
return result.trim()
|
|
}
|
|
|
|
/** Check if a branch exists locally or remotely. Returns "local", "remote", or null. */
|
|
export async function branchExists(branch: string, cwd?: string): Promise<"local" | "remote" | null> {
|
|
const dir = cwd ?? "."
|
|
const local = await $`git show-ref --verify --quiet refs/heads/${branch}`.cwd(dir).nothrow().quiet()
|
|
if (local.exitCode === 0) return "local"
|
|
|
|
await $`git fetch origin`.cwd(dir).nothrow().quiet()
|
|
const remote = await $`git show-ref --verify --quiet refs/remotes/origin/${branch}`.cwd(dir).nothrow().quiet()
|
|
if (remote.exitCode === 0) return "remote"
|
|
|
|
return null
|
|
}
|
|
|
|
/** Create a worktree for the given branch. */
|
|
export async function createWorktree(branch: string, worktreePath: string, cwd: string): Promise<void> {
|
|
// Clean up stale worktree path if it exists
|
|
if (existsSync(worktreePath)) {
|
|
await $`git worktree remove ${worktreePath} --force`.cwd(cwd).nothrow().quiet()
|
|
if (existsSync(worktreePath)) {
|
|
await rm(worktreePath, { recursive: true })
|
|
}
|
|
}
|
|
await $`git worktree prune`.cwd(cwd).nothrow().quiet()
|
|
|
|
const exists = await branchExists(branch, cwd)
|
|
|
|
if (exists === "local") {
|
|
await $`git worktree add ${worktreePath} ${branch}`.cwd(cwd).quiet()
|
|
} else if (exists === "remote") {
|
|
await $`git worktree add ${worktreePath} -b ${branch} origin/${branch}`.cwd(cwd).quiet()
|
|
} else {
|
|
// New branch from current HEAD
|
|
await $`git worktree add -b ${branch} ${worktreePath}`.cwd(cwd).quiet()
|
|
}
|
|
}
|
|
|
|
/** Remove a worktree. */
|
|
export async function removeWorktree(worktreePath: string, cwd: string): Promise<void> {
|
|
await $`git worktree remove ${worktreePath} --force`.cwd(cwd)
|
|
}
|
|
|
|
/** Delete a local branch. */
|
|
export async function deleteLocalBranch(branch: string, cwd: string): Promise<void> {
|
|
await $`git branch -D ${branch}`.cwd(cwd).nothrow()
|
|
}
|
|
|
|
/** Checkout a branch. */
|
|
export async function checkout(branch: string, cwd: string): Promise<void> {
|
|
await $`git checkout ${branch}`.cwd(cwd)
|
|
}
|