Skip to content

Commit 30890fd

Browse files
fix(client): reload VCS info when the branch changes (#58982)
Co-authored-by: vimtor <36263538+vimtor@users.noreply.github.com>
1 parent 5be4b16 commit 30890fd

3 files changed

Lines changed: 58 additions & 11 deletions

File tree

‎packages/client/src/solid/data.ts‎

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,14 +1284,10 @@ export function createData(config: CreateDataInput) {
12841284
refresh(() => result.location.skill.sync(location))
12851285
break
12861286
case "vcs.branch.updated":
1287-
setStore("location", locationKey(location), (data) => ({
1288-
vcs: {
1289-
branch: {
1290-
...data?.vcs?.branch,
1291-
current: event.data.branch,
1292-
},
1293-
},
1294-
}))
1287+
// The event names only the current branch. Reload the rest: the default branch and provider can change with
1288+
// it, and a read served before the location's VCS provider loaded has neither.
1289+
result.location.vcs.invalidate(location)
1290+
refresh(() => result.location.vcs.sync(location))
12951291
break
12961292
case "form.created":
12971293
if (store.session.form[event.data.form.sessionID]?.some((form) => form.id === event.data.form.id)) break

‎packages/client/test/solid-data.test.ts‎

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,10 +811,55 @@ test("refreshes references for the location an update names", async () => {
811811
}
812812
})
813813

814+
test("reloads VCS info when the branch changes", async () => {
815+
const listeners = new Set<Parameters<CreateDataInput["event"]["listen"]>[0]>()
816+
const location = { directory: "/worktree", project: { id: "project", directory: "/worktree", canonical: "/main" } }
817+
// A location's first read can land before its VCS provider loads, and so has no branches.
818+
const responses = [{ branch: {} }, { provider: "git", branch: { current: "feature", default: "dev" } }]
819+
const api = OpenCode.make({
820+
baseUrl: "http://opencode.local",
821+
fetch: async () => Response.json({ location, data: responses.shift() }),
822+
})
823+
const setup = createRoot((dispose) => ({
824+
data: createData({
825+
api: () => api,
826+
directory: location.directory,
827+
event: {
828+
on: () => () => {},
829+
listen(handler) {
830+
listeners.add(handler)
831+
return () => listeners.delete(handler)
832+
},
833+
},
834+
connection: { status: () => "connected" },
835+
}),
836+
dispose,
837+
}))
838+
839+
try {
840+
await setup.data.location.vcs.sync()
841+
expect(setup.data.location.vcs.info()).toEqual({ branch: {} })
842+
843+
const updated: OpenCodeEvent = {
844+
id: "evt_vcs.branch.updated",
845+
created: 1,
846+
type: "vcs.branch.updated",
847+
location: { directory: location.directory },
848+
data: { branch: "feature" },
849+
}
850+
listeners.forEach((listener) => listener({ name: updated.type, details: updated }))
851+
await wait(() => setup.data.location.vcs.info()?.provider === "git")
852+
expect(setup.data.location.vcs.info()).toEqual({ provider: "git", branch: { current: "feature", default: "dev" } })
853+
} finally {
854+
setup.dispose()
855+
}
856+
})
857+
814858
test("preserves sibling catalogs through location preload, branch, shell, and websearch updates", async () => {
815859
const listeners = new Set<Parameters<CreateDataInput["event"]["listen"]>[0]>()
816860
const location = { directory: "/project", project: { id: "project", directory: "/project", canonical: "/project" } }
817861
const requests: string[] = []
862+
let branch = "main"
818863
const api = OpenCode.make({
819864
baseUrl: "http://opencode.local",
820865
fetch: async (input, init) => {
@@ -824,7 +869,7 @@ test("preserves sibling catalogs through location preload, branch, shell, and we
824869
if (pathname === "/api/project") return Response.json([])
825870
if (pathname === "/api/location") return Response.json(location)
826871
if (pathname === "/api/vcs")
827-
return Response.json({ location, data: { branch: { current: "main", default: "main" } } })
872+
return Response.json({ location, data: { branch: { current: branch, default: "main" } } })
828873
if (pathname === "/api/reference")
829874
return Response.json({
830875
location,
@@ -871,7 +916,9 @@ test("preserves sibling catalogs through location preload, branch, shell, and we
871916
expect(references?.map((reference) => [reference.name, reference.path])).toEqual([["docs", "/docs"]])
872917
expect(setup.data.shell.get(shell.id)).toBe(first)
873918

919+
branch = "feature"
874920
emit({ type: "vcs.branch.updated", location, data: { branch: "feature" } })
921+
await wait(() => setup.data.location.vcs.info()?.branch.current === "feature")
875922
expect(setup.data.location.vcs.info()?.branch).toEqual({ current: "feature", default: "main" })
876923
expect(setup.data.location.reference.list()).toBe(references)
877924
emit({ type: "shell.created", location, data: { info: { ...shell, id: "sh_second" } } })
@@ -896,6 +943,8 @@ test("preserves sibling catalogs through location preload, branch, shell, and we
896943
"/api/project",
897944
"/api/reference",
898945
"/api/session/active",
946+
// The branch update reloads VCS info.
947+
"/api/vcs",
899948
"/api/vcs",
900949
"/api/websearch/provider",
901950
])

‎packages/tui/test/cli/tui/data.test.tsx‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,14 @@ test("does not preload session summaries into the data context", async () => {
113113
}
114114
})
115115

116-
test("syncs VCS info and applies branch updates", async () => {
116+
test("syncs VCS info and reloads it on branch updates", async () => {
117117
const events = createEventStream()
118+
let branch = "main"
118119
const calls = createFetch((url) => {
119120
if (url.pathname !== "/api/vcs") return undefined
120121
return json({
121122
location: { directory, project: { id: "proj_test", directory: worktree, canonical: worktree } },
122-
data: { branch: { current: "main", default: "main" } },
123+
data: { branch: { current: branch, default: "main" } },
123124
})
124125
}, events)
125126
let data!: ReturnType<typeof useData>
@@ -143,6 +144,7 @@ test("syncs VCS info and applies branch updates", async () => {
143144

144145
try {
145146
await wait(() => data.location.vcs.info()?.branch.current === "main")
147+
branch = "feature"
146148
emitEvent(events, {
147149
id: "evt_vcs_branch",
148150
created: Date.now(),

0 commit comments

Comments
 (0)