server: reset worktrees against each repo's default branch

This commit is contained in:
Shoubhit Dash
2026-03-25 05:12:29 +05:30
parent 73de13072a
commit 1344a57153
3 changed files with 50 additions and 90 deletions

View File

@@ -19,7 +19,7 @@ export namespace Git {
] as const
const out = (result: { text(): string }) => result.text().trim()
const split = (text: string) => text.split("\0").filter(Boolean)
const nuls = (text: string) => text.split("\0").filter(Boolean)
const fail = (err: unknown) =>
({
exitCode: 1,
@@ -72,52 +72,14 @@ export namespace Git {
readonly stats: (cwd: string, ref: string) => Effect.Effect<Stat[]>
}
const kind = (code?: string): Kind => {
const kind = (code: string): Kind => {
if (code === "??") return "added"
if (code?.includes("U")) return "modified"
if (code?.includes("A") && !code.includes("D")) return "added"
if (code?.includes("D") && !code.includes("A")) return "deleted"
if (code.includes("U")) return "modified"
if (code.includes("A") && !code.includes("D")) return "added"
if (code.includes("D") && !code.includes("A")) return "deleted"
return "modified"
}
const parseStatus = (text: string) =>
split(text).flatMap((item) => {
const file = item.slice(3)
if (!file) return []
const code = item.slice(0, 2)
return [{ file, code, status: kind(code) } satisfies Item]
})
const parseNames = (text: string) => {
const list = split(text)
return list.flatMap((code, idx) => {
if (idx % 2 !== 0) return []
const file = list[idx + 1]
if (!code || !file) return []
return [{ file, code, status: kind(code) } satisfies Item]
})
}
const parseStats = (text: string) =>
split(text).flatMap((item) => {
const a = item.indexOf("\t")
const b = item.indexOf("\t", a + 1)
if (a === -1 || b === -1) return []
const file = item.slice(b + 1)
if (!file) return []
const adds = item.slice(0, a)
const dels = item.slice(a + 1, b)
const additions = adds === "-" ? 0 : Number.parseInt(adds || "0", 10)
const deletions = dels === "-" ? 0 : Number.parseInt(dels || "0", 10)
return [
{
file,
additions: Number.isFinite(additions) ? additions : 0,
deletions: Number.isFinite(deletions) ? deletions : 0,
} satisfies Stat,
]
})
export class Service extends ServiceMap.Service<Service, Interface>()("@opencode/Git") {}
export const layer = Layer.effect(
@@ -233,23 +195,51 @@ export namespace Git {
})
const status = Effect.fn("Git.status")(function* (cwd: string) {
return parseStatus(
return nuls(
yield* text(["status", "--porcelain=v1", "--untracked-files=all", "--no-renames", "-z", "--", "."], {
cwd,
}),
)
).flatMap((item) => {
const file = item.slice(3)
if (!file) return []
const code = item.slice(0, 2)
return [{ file, code, status: kind(code) } satisfies Item]
})
})
const diff = Effect.fn("Git.diff")(function* (cwd: string, ref: string) {
return parseNames(
const list = nuls(
yield* text(["diff", "--no-ext-diff", "--no-renames", "--name-status", "-z", ref, "--", "."], { cwd }),
)
return list.flatMap((code, idx) => {
if (idx % 2 !== 0) return []
const file = list[idx + 1]
if (!code || !file) return []
return [{ file, code, status: kind(code) } satisfies Item]
})
})
const stats = Effect.fn("Git.stats")(function* (cwd: string, ref: string) {
return parseStats(
return nuls(
yield* text(["diff", "--no-ext-diff", "--no-renames", "--numstat", "-z", ref, "--", "."], { cwd }),
)
).flatMap((item) => {
const a = item.indexOf("\t")
const b = item.indexOf("\t", a + 1)
if (a === -1 || b === -1) return []
const file = item.slice(b + 1)
if (!file) return []
const adds = item.slice(0, a)
const dels = item.slice(a + 1, b)
const additions = adds === "-" ? 0 : Number.parseInt(adds || "0", 10)
const deletions = dels === "-" ? 0 : Number.parseInt(dels || "0", 10)
return [
{
file,
additions: Number.isFinite(additions) ? additions : 0,
deletions: Number.isFinite(deletions) ? deletions : 0,
} satisfies Stat,
]
})
})
return Service.of({

View File

@@ -331,9 +331,10 @@ export namespace Server {
},
}),
async (c) => {
const [branch, default_branch] = await Promise.all([Vcs.branch(), Vcs.defaultBranch()])
return c.json({
branch: await Vcs.branch(),
default_branch: await Vcs.defaultBranch(),
branch,
default_branch,
})
},
)

View File

@@ -573,49 +573,18 @@ export namespace Worktree {
throw new ResetFailedError({ message: "Worktree not found" })
}
const remoteList = await Git.run(["remote"], { cwd: Instance.worktree })
if (remoteList.exitCode !== 0) {
throw new ResetFailedError({ message: errorText(remoteList) || "Failed to list git remotes" })
}
const remotes = outputText(remoteList.stdout)
.split("\n")
.map((line) => line.trim())
.filter(Boolean)
const remote = remotes.includes("origin")
? "origin"
: remotes.length === 1
? remotes[0]
: remotes.includes("upstream")
? "upstream"
: ""
const remoteHead = remote
? await Git.run(["symbolic-ref", `refs/remotes/${remote}/HEAD`], { cwd: Instance.worktree })
: { exitCode: 1, stdout: undefined, stderr: undefined }
const remoteRef = remoteHead.exitCode === 0 ? outputText(remoteHead.stdout) : ""
const remoteTarget = remoteRef ? remoteRef.replace(/^refs\/remotes\//, "") : ""
const remoteBranch = remote && remoteTarget.startsWith(`${remote}/`) ? remoteTarget.slice(`${remote}/`.length) : ""
const mainCheck = await Git.run(["show-ref", "--verify", "--quiet", "refs/heads/main"], {
cwd: Instance.worktree,
})
const masterCheck = await Git.run(["show-ref", "--verify", "--quiet", "refs/heads/master"], {
cwd: Instance.worktree,
})
const localBranch = mainCheck.exitCode === 0 ? "main" : masterCheck.exitCode === 0 ? "master" : ""
const target = remoteBranch ? `${remote}/${remoteBranch}` : localBranch
if (!target) {
const base = await Git.defaultBranch(Instance.worktree)
if (!base) {
throw new ResetFailedError({ message: "Default branch not found" })
}
if (remoteBranch) {
const fetch = await Git.run(["fetch", remote, remoteBranch], { cwd: Instance.worktree })
const sep = base.ref.indexOf("/")
if (base.ref !== base.name && sep > 0) {
const remote = base.ref.slice(0, sep)
const branch = base.ref.slice(sep + 1)
const fetch = await Git.run(["fetch", remote, branch], { cwd: Instance.worktree })
if (fetch.exitCode !== 0) {
throw new ResetFailedError({ message: errorText(fetch) || `Failed to fetch ${target}` })
throw new ResetFailedError({ message: errorText(fetch) || `Failed to fetch ${base.ref}` })
}
}
@@ -625,7 +594,7 @@ export namespace Worktree {
const worktreePath = entry.path
const resetToTarget = await Git.run(["reset", "--hard", target], { cwd: worktreePath })
const resetToTarget = await Git.run(["reset", "--hard", base.ref], { cwd: worktreePath })
if (resetToTarget.exitCode !== 0) {
throw new ResetFailedError({ message: errorText(resetToTarget) || "Failed to reset worktree to target" })
}