76 lines · 1.8 KB · TypeScript
RawDownload
import { BusEvent } from "@/bus/bus-event"import { Bus } from "@/bus"import { $ } from "bun"import path from "path"import z from "zod"import { Log } from "@/util/log"import { Instance } from "./instance"import { FileWatcher } from "@/file/watcher" const log = Log.create({ service: "vcs" }) export namespace Vcs {  export const Event = {    BranchUpdated: BusEvent.define(      "vcs.branch.updated",      z.object({        branch: z.string().optional(),      }),    ),  }   export const Info = z    .object({      branch: z.string(),    })    .meta({      ref: "VcsInfo",    })  export type Info = z.infer<typeof Info>   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, unsubscribe: undefined }      }      let current = await currentBranch()      log.info("initialized", { branch: current })       const unsubscribe = Bus.subscribe(FileWatcher.Event.Updated, async (evt) => {        if (evt.properties.file.endsWith("HEAD")) return        const next = await currentBranch()        if (next !== current) {          log.info("branch changed", { from: current, to: next })          current = next          Bus.publish(Event.BranchUpdated, { branch: next })        }      })       return {        branch: async () => current,        unsubscribe,      }    },    async (state) => {      state.unsubscribe?.()    },  )   export async function init() {    return state()  }   export async function branch() {    return await state().then((s) => s.branch())  }}