Files
opencode/packages/opencode/src/project/vcs.ts
opencode-agent[bot] df9952c291 Renamed vcs.changed to vcs.branch.updated (#4771)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2025-11-26 01:17:59 -06:00

87 lines
2.2 KiB
TypeScript

import { $ } from "bun"
import { watch, type FSWatcher } from "fs"
import path from "path"
import z from "zod"
import { Log } from "@/util/log"
import { Bus } from "@/bus"
import { Instance } from "./instance"
const log = Log.create({ service: "vcs" })
export namespace Vcs {
export const Event = {
BranchUpdated: Bus.event(
"vcs.branch.updated",
z.object({
branch: z.string().optional(),
}),
),
}
async function currentBranch() {
return $`git rev-parse --abbrev-ref HEAD`
.quiet()
.nothrow()
.cwd(Instance.worktree)
.text()
.then((x) => x.trim())
.catch(() => undefined)
}
const state = Instance.state(
async () => {
if (Instance.project.vcs !== "git") {
return { branch: async () => undefined, watcher: undefined }
}
let current = await currentBranch()
log.info("initialized", { branch: current })
const gitDir = await $`git rev-parse --git-dir`
.quiet()
.nothrow()
.cwd(Instance.worktree)
.text()
.then((x) => x.trim())
.catch(() => undefined)
if (!gitDir) {
log.warn("failed to resolve git directory")
return { branch: async () => current, watcher: undefined }
}
const gitHead = path.join(gitDir, "HEAD")
let watcher: FSWatcher | undefined
// we should probably centralize file watching (see watcher.ts)
// but parcel still marked experimental rn
try {
watcher = watch(gitHead, async () => {
const next = await currentBranch()
if (next !== current) {
log.info("branch changed", { from: current, to: next })
current = next
Bus.publish(Event.BranchUpdated, { branch: next })
}
})
log.info("watching", { path: gitHead })
} catch (e) {
log.warn("failed to watch git HEAD", { error: e })
}
return {
branch: async () => current,
watcher,
}
},
async (state) => {
state.watcher?.close()
},
)
export async function init() {
return state()
}
export async function branch() {
return await state().then((s) => s.branch())
}
}