Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/opencode/src/file/watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { git } from "@/util/git"

const SUBSCRIBE_TIMEOUT_MS = 10_000

const GIT_STATUS_CHANGE_DEBOUNCE = 200

declare const OPENCODE_LIBC: string | undefined

export namespace FileWatcher {
Expand All @@ -30,6 +32,7 @@ export namespace FileWatcher {
event: z.union([z.literal("add"), z.literal("change"), z.literal("unlink")]),
}),
),
GitStatusChanged: BusEvent.define("git.status.changed", z.object({ directory: z.string() })),
}

const watcher = lazy((): typeof import("@parcel/watcher") | undefined => {
Expand Down Expand Up @@ -108,6 +111,32 @@ export namespace FileWatcher {
}
}

if (Instance.project.vcs === "git") {
const result = await git(["rev-parse", "--absolute-git-dir"], {
cwd: Instance.directory,
})
if (result.exitCode === 0 && result.text().trim()) {
const gitDir = result.text().trim()
const directory = path.dirname(gitDir)
let debounceTimer: ReturnType<typeof setTimeout> | undefined
const handleGitStatusChange: ParcelWatcher.SubscribeCallback = (err) => {
if (err) return
if (debounceTimer) clearTimeout(debounceTimer)
debounceTimer = setTimeout(() => {
Bus.publish(Event.GitStatusChanged, { directory: directory })
}, GIT_STATUS_CHANGE_DEBOUNCE)
}
const pending = w.subscribe(gitDir, handleGitStatusChange, { ignore: [], backend })
const sub = await withTimeout(pending, SUBSCRIBE_TIMEOUT_MS).catch((err) => {
log.error("failed to subscribe to .git change", { error: err, cwd: Instance.directory })
if (debounceTimer) clearTimeout(debounceTimer)
pending.then((s) => s.unsubscribe()).catch(() => {})
return undefined
})
if (sub) subs.push(sub)
}
}

return { subs }
},
async (state) => {
Expand Down
39 changes: 38 additions & 1 deletion packages/opencode/src/project/vcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { Log } from "@/util/log"
import { Instance } from "./instance"
import { FileWatcher } from "@/file/watcher"
import { git } from "@/util/git"
import { Session } from "@/session"
import { Storage } from "@/storage/storage"
import { Snapshot } from "@/snapshot"

const log = Log.create({ service: "vcs" })

Expand Down Expand Up @@ -41,7 +44,11 @@ export namespace Vcs {
const state = Instance.state(
async () => {
if (Instance.project.vcs !== "git") {
return { branch: async () => undefined, unsubscribe: undefined }
return {
branch: async () => undefined,
unsubscribe: undefined,
unsubscribeGitStatus: undefined,
}
}
let current = await currentBranch()
log.info("initialized", { branch: current })
Expand All @@ -56,13 +63,43 @@ export namespace Vcs {
}
})

const unsubscribeGitStatus = Bus.subscribe(FileWatcher.Event.GitStatusChanged, async (evt) => {
const directory = evt.properties.directory
log.info("git status change", { directory: directory })

const changedFiles = await Snapshot.diffFull("HEAD", "", path.join(directory, ".git"))
const sessions = [...Session.list({ directory: directory })]
for (const s of sessions) {
try {
await Storage.write(["session_diff", s.id], changedFiles)
await Session.setSummary({
sessionID: s.id,
summary: {
additions: changedFiles.reduce((sum, f) => sum + f.additions, 0),
deletions: changedFiles.reduce((sum, f) => sum + f.deletions, 0),
files: changedFiles.length,
},
})
Bus.publish(Session.Event.Diff, { sessionID: s.id, diff: changedFiles })
} catch (e) {
log.error("failed to handle git status change", {
error: e,
sessionID: s.id,
directory: directory,
})
}
}
})

return {
branch: async () => current,
unsubscribe,
unsubscribeGitStatus,
}
},
async (state) => {
state.unsubscribe?.()
state.unsubscribeGitStatus?.()
},
)

Expand Down
8 changes: 5 additions & 3 deletions packages/opencode/src/snapshot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export namespace Snapshot {
const prune = "7.days"

function args(git: string, cmd: string[]) {
return ["--git-dir", git, "--work-tree", Instance.worktree, ...cmd]
return ["--git-dir", git, "--work-tree", Instance.worktree, ...cmd.filter(Boolean)]
}

export function init() {
Expand Down Expand Up @@ -265,8 +265,10 @@ export namespace Snapshot {
ref: "FileDiff",
})
export type FileDiff = z.infer<typeof FileDiff>
export async function diffFull(from: string, to: string): Promise<FileDiff[]> {
const git = gitdir()
export async function diffFull(from: string, to: string, git?: string): Promise<FileDiff[]> {
if (! git) {
git = gitdir()
}
const result: FileDiff[] = []
const status = new Map<string, "added" | "deleted" | "modified">()

Expand Down
Loading