History

Add JJ VCS driver support

- Detect and route jj repositories alongside git - Thread VCS kind through status and UI labels - Add jj driver contract coverage and shared freshness helper

Julius Marminge committed 6 months ago

Commit c31beca1 parent 128b43230 files · +838 −44Browse files

Showing the changes to apps/server/src/git/GitWorkflowService.ts (1 of 30 files). Show all

Unified · Split

apps/server/src/git/GitWorkflowService.ts+67−3
@@ -3,6 +3,7 @@ import { Context, Effect, Layer } from "effect"; · 2 unmodified lines
33 import {
44 GitManagerError,
55 GitCommandError,
6+ type VcsDriverKind,
67 type VcsSwitchRefInput,
78 type VcsSwitchRefResult,
89 type VcsCreateRefInput,
@@ -29,6 +30,7 @@ import { · 20 unmodified lines
2930 import { GitManager, type GitRunStackedActionOptions } from "./GitManager.ts";
3031 import { GitVcsDriver } from "../vcs/GitVcsDriver.ts";
3132 import { VcsDriverRegistry } from "../vcs/VcsDriverRegistry.ts";
33+import { mergeGitStatusParts } from "@t3tools/shared/git";
3234
3335 export interface GitWorkflowServiceShape {
3436 readonly status: (
@@ -92,6 +94,18 @@ const unsupportedGitCommand = (operation: string, cwd: string, detail: string) = · 57 unmodified lines
9294 detail,
9395 });
9496
97+const emptyWorkingTree = { files: [], insertions: 0, deletions: 0 } as const;
98+
99+const nonGitLocalStatus = (kind: VcsDriverKind, isRepo: boolean): VcsStatusLocalResult => ({
100+ kind,
101+ isRepo,
102+ hasPrimaryRemote: false,
103+ isDefaultRef: false,
104+ refName: null,
105+ hasWorkingTreeChanges: false,
106+ workingTree: emptyWorkingTree,
107+});
108+
95109 export const make = Effect.fn("makeGitWorkflowService")(function* () {
96110 const registry = yield* VcsDriverRegistry;
97111 const git = yield* GitVcsDriver;
@@ -153,10 +167,60 @@ export const make = Effect.fn("makeGitWorkflowService")(function* () { · 55 unmodified lines
153167 (input: Input) =>
154168 ensureGit(operation, input.cwd).pipe(Effect.andThen(run(input)));
155169
170+ const localStatus: GitWorkflowServiceShape["localStatus"] = Effect.fn(
171+ "GitWorkflowService.localStatus",
172+ )(function* (input) {
173+ const handle = yield* registry
174+ .detect({ cwd: input.cwd })
175+ .pipe(
176+ Effect.mapError((error) =>
177+ unsupportedGitWorkflow(
178+ "GitWorkflowService.localStatus",
179+ input.cwd,
180+ error instanceof Error ? error.message : String(error),
181+ ),
182+ ),
183+ );
184+ if (!handle) {
185+ return nonGitLocalStatus("unknown", false);
186+ }
187+ if (handle.kind === "git") {
188+ return yield* gitManager.localStatus(input);
189+ }
190+ return nonGitLocalStatus(handle.kind, true);
191+ });
192+
193+ const remoteStatus: GitWorkflowServiceShape["remoteStatus"] = Effect.fn(
194+ "GitWorkflowService.remoteStatus",
195+ )(function* (input) {
196+ const handle = yield* registry
197+ .detect({ cwd: input.cwd })
198+ .pipe(
199+ Effect.mapError((error) =>
200+ unsupportedGitWorkflow(
201+ "GitWorkflowService.remoteStatus",
202+ input.cwd,
203+ error instanceof Error ? error.message : String(error),
204+ ),
205+ ),
206+ );
207+ if (handle?.kind === "git") {
208+ return yield* gitManager.remoteStatus(input);
209+ }
210+ return null;
211+ });
212+
213+ const status: GitWorkflowServiceShape["status"] = Effect.fn("GitWorkflowService.status")(
214+ function* (input) {
215+ const [local, remote] = yield* Effect.all([localStatus(input), remoteStatus(input)]);
216+ return mergeGitStatusParts(local, remote);
217+ },
218+ );
219+
156220 return GitWorkflowService.of({
157− status: routeGitManager("GitWorkflowService.status", gitManager.status),
158− localStatus: routeGitManager("GitWorkflowService.localStatus", gitManager.localStatus),
159− remoteStatus: routeGitManager("GitWorkflowService.remoteStatus", gitManager.remoteStatus),
221+ status,
222+ localStatus,
223+ remoteStatus,
160224 invalidateLocalStatus: gitManager.invalidateLocalStatus,
161225 invalidateRemoteStatus: gitManager.invalidateRemoteStatus,
162226 invalidateStatus: gitManager.invalidateStatus,