mime-todo-cli/lib/git.ts
Tiara Rodney 932d4ad420
init
2026-03-15 03:02:41 +01:00

95 lines
2.9 KiB
TypeScript

// Git helpers for branch validation and commit operations
import { execSync } from "child_process"
export function getCurrentBranch(cwd = process.cwd()): string {
return execSync("git rev-parse --abbrev-ref HEAD", { cwd, encoding: "utf-8" }).trim()
}
export function isClean(cwd = process.cwd()): boolean {
const status = execSync("git status --porcelain", { cwd, encoding: "utf-8" }).trim()
return status === ""
}
export function commitFile(path: string, message: string, cwd = process.cwd()): void {
execSync(`git add ${path}`, { cwd })
execSync(`git commit -m ${JSON.stringify(message)}`, { cwd })
}
export function commitFileWithBody(path: string, header: string, body: string, cwd = process.cwd()): void {
execSync(`git add ${path}`, { cwd })
const msg = `${header}\n\n${body}`
execSync(`git commit -m ${JSON.stringify(msg)}`, { cwd })
}
export function branchExists(branch: string, cwd = process.cwd()): boolean {
try {
execSync(`git rev-parse --verify ${branch}`, { cwd, stdio: "pipe" })
return true
} catch {
return false
}
}
// Parse the issue branch name into type and ID
export function parseIssueBranch(branch: string): { type: string; id: number } | null {
const match = branch.match(/^(feature|bugfix|hotfix)\/(\d+)$/)
if (!match) return null
return { type: match[1], id: Number(match[2]) }
}
export interface GitCommit {
hash: string
subject: string
body: string
}
// Get commits on current branch since it diverged from a base branch
export function getCommitsSinceDiverge(base = "develop", cwd = process.cwd()): GitCommit[] {
return parseGitLog(`${base}..HEAD`, cwd)
}
// Get commits from a ref spec (e.g. HEAD~3..HEAD)
export function getCommitsFromRef(refSpec: string, cwd = process.cwd()): GitCommit[] {
return parseGitLog(refSpec, cwd)
}
// Get all commits on a branch
export function getAllCommits(branch: string, cwd = process.cwd()): GitCommit[] {
return parseGitLog(branch, cwd)
}
function parseGitLog(range: string, cwd: string): GitCommit[] {
const SEP = "---COMMIT-SEP---"
const format = `%H%n%s%n%b%n${SEP}`
let output: string
try {
output = execSync(
`git log ${range} --format=${JSON.stringify(format)}`,
{ cwd, encoding: "utf-8" }
)
} catch {
return []
}
const commits: GitCommit[] = []
const entries = output.split(SEP).filter(e => e.trim())
for (const entry of entries) {
const lines = entry.trim().split("\n")
if (lines.length < 2) continue
commits.push({
hash: lines[0],
subject: lines[1],
body: lines.slice(2).join("\n").trim(),
})
}
return commits
}
// Check if a commit subject is a todo transition
export function parseTodoTransition(subject: string): { issueId: number; status: string } | null {
const match = subject.match(/^todo\((\d+)\):\s*(\S+)/)
if (!match) return null
return { issueId: Number(match[1]), status: match[2] }
}