refactor: map VCS providers by operation#446
Conversation
885213b to
9ddecc7
Compare
9ddecc7 to
f8fdc03
Compare
Greptile SummaryThis PR refactors the VCS adapter layer from a monolithic
Confidence Score: 4/5Safe to merge — the refactoring correctly preserves all three adapters' load and watch-signature behavior, and the untracked-file and large-file paths produce the same output order as before. The operation-map dispatch, gitSource extraction, and untracked-file helper split are all logically equivalent to the switch-dispatch they replace. The only non-trivial concerns are the stash-show error message now depending on registry order (benign today, fragile under future extension) and the src/core/vcs/index.ts (stash-show error message registry dependency) and src/core/vcs/gitSource.ts (readGitSpec fallback allocation). Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant L as loaders.ts
participant V as vcs/index.ts
participant A as VcsAdapter (Git/Jj/Sl)
participant U as vcs/untracked.ts
participant G as vcs/gitSource.ts
L->>V: loadVcsReview(adapter, operation, ctx)
V->>V: getVcsOperation(adapter, operation)
V->>A: handler.load(operation.input, ctx)
A->>U: inspectLargeUntrackedFile / buildGit/FilesystemUntrackedDiffFile
A->>G: createGitFileSourceFetcher(specs)
G-->>A: FileSourceFetcher
A-->>V: "VcsPatchResult { patchText, extraFiles, sourceFetcherBuilder }"
V-->>L: VcsPatchResult
L->>L: normalizePatchChangeset(patchText)
L->>L: "adapterFiles = extraFiles.map(f => { ...f, agent: findAgentFileContext })"
L-->>L: "Changeset { files: [parsedFiles, ...adapterFiles] }"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant L as loaders.ts
participant V as vcs/index.ts
participant A as VcsAdapter (Git/Jj/Sl)
participant U as vcs/untracked.ts
participant G as vcs/gitSource.ts
L->>V: loadVcsReview(adapter, operation, ctx)
V->>V: getVcsOperation(adapter, operation)
V->>A: handler.load(operation.input, ctx)
A->>U: inspectLargeUntrackedFile / buildGit/FilesystemUntrackedDiffFile
A->>G: createGitFileSourceFetcher(specs)
G-->>A: FileSourceFetcher
A-->>V: "VcsPatchResult { patchText, extraFiles, sourceFetcherBuilder }"
V-->>L: VcsPatchResult
L->>L: normalizePatchChangeset(patchText)
L->>L: "adapterFiles = extraFiles.map(f => { ...f, agent: findAgentFileContext })"
L-->>L: "Changeset { files: [parsedFiles, ...adapterFiles] }"
|
| return createFileSourceFetcher( | ||
| { old: spec, new: { kind: "none" } }, | ||
| { maxSourceBytes }, | ||
| ).getFullText("old"); | ||
| } |
There was a problem hiding this comment.
Roundabout delegation for
"none"/"fs" specs
When spec.kind is neither "git-blob" nor "git-index", readGitSpec constructs a full FileSourceFetcher — including an internal Map cache — only to immediately call getFullText("old") and discard the fetcher. For the "none" case this allocates an object just to return null; for the "fs" case the cached result is never reused. A direct early-return branch (if (spec.kind === "none") return null; return readFsSpec(spec, maxSourceBytes)) would eliminate the indirection without changing behaviour. This requires exporting readFsSpec from fileSource.ts, but logSourceDiagnostic and readStreamTextWithLimit are already exported in this PR.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/core/vcs/gitSource.ts
Line: 129-133
Comment:
**Roundabout delegation for `"none"`/`"fs"` specs**
When `spec.kind` is neither `"git-blob"` nor `"git-index"`, `readGitSpec` constructs a full `FileSourceFetcher` — including an internal `Map` cache — only to immediately call `getFullText("old")` and discard the fetcher. For the `"none"` case this allocates an object just to return `null`; for the `"fs"` case the cached result is never reused. A direct early-return branch (`if (spec.kind === "none") return null; return readFsSpec(spec, maxSourceBytes)`) would eliminate the indirection without changing behaviour. This requires exporting `readFsSpec` from `fileSource.ts`, but `logSourceDiagnostic` and `readStreamTextWithLimit` are already exported in this PR.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| const supportingAdapter = vcsAdapters.find((candidate) => candidate.operations[operationKind]); | ||
| if (operationKind === "stash-show" && supportingAdapter) { | ||
| return new HunkUserError(`\`hunk stash show\` requires ${supportingAdapter.name} VCS mode.`, [ | ||
| `Set \`vcs = "${supportingAdapter.id}"\` in Hunk config, then try again.`, | ||
| ]); |
There was a problem hiding this comment.
stash-show redirect message is sensitive to adapter registry order
vcsAdapters.find(...) returns the first adapter in [JjVcsAdapter, SaplingVcsAdapter, GitVcsAdapter] that has a "stash-show" entry. Today only GitVcsAdapter carries that operation, so the message correctly says "requires Git VCS mode." If stash-show were added to JjVcsAdapter or SaplingVcsAdapter before GitVcsAdapter in a future change, the error would suggest using the wrong provider — e.g., "requires Jujutsu VCS mode." — without the user having any Git stash to inspect. The original code hardcoded the Git requirement for stash-show, which was semantically more precise. Consider retaining a direct reference (GitVcsAdapter.id / GitVcsAdapter.name) or adding a comment explaining the ordering assumption.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/core/vcs/index.ts
Line: 137-141
Comment:
**`stash-show` redirect message is sensitive to adapter registry order**
`vcsAdapters.find(...)` returns the *first* adapter in `[JjVcsAdapter, SaplingVcsAdapter, GitVcsAdapter]` that has a `"stash-show"` entry. Today only `GitVcsAdapter` carries that operation, so the message correctly says "requires Git VCS mode." If `stash-show` were added to `JjVcsAdapter` or `SaplingVcsAdapter` before `GitVcsAdapter` in a future change, the error would suggest using the wrong provider — e.g., "requires Jujutsu VCS mode." — without the user having any Git stash to inspect. The original code hardcoded the Git requirement for stash-show, which was semantically more precise. Consider retaining a direct reference (`GitVcsAdapter.id` / `GitVcsAdapter.name`) or adding a comment explaining the ordering assumption.
How can I resolve this? If you propose a fix, please make it concise.
Summary
GitVcsAdapter,JjVcsAdapter,SaplingVcsAdapter) through the sharedVcsAdaptershape.VcsDiffCommandInput,VcsShowCommandInput, andVcsStashShowCommandInput.VcsModestring union and route default/selected providers through the adapter registry.loaders.tsno longer branches on provider ids.src/core/vcs/gitSource.ts, leavingfileSource.tsprovider-neutral for shared fetcher primitives and filesystem reads.Validation
bun run typecheckbun testbun run test:integrationbun run test:tty-smokegit diff --checkThis PR description was generated by Pi using GPT-5