History

fetch in background

Julius Marminge committed 9 months ago

Commit 724ecbe1 parent 067c5c72 files · +75 −11Browse files

Unified · Split

2 files
  1. apps/server/src/git.test.ts +73 −6
  2. apps/server/src/git.ts +2 −5
apps/server/src/git.test.ts+73−6
@@ -272,11 +272,12 @@ describe("git integration", () => { · 271 unmodified lines
272272
273273 await checkoutGitBranch({ cwd: source.path, branch: featureBranch });
274274 const core = new GitCoreService();
275− const details = await core.statusDetails(source.path);
276−
277− expect(details.branch).toBe(featureBranch);
278− expect(details.aheadCount).toBe(0);
279− expect(details.behindCount).toBe(1);
275+ await vi.waitFor(async () => {
276+ const details = await core.statusDetails(source.path);
277+ expect(details.branch).toBe(featureBranch);
278+ expect(details.aheadCount).toBe(0);
279+ expect(details.behindCount).toBe(1);
280+ });
280281 });
281282
282283 it("keeps checkout successful when upstream refresh fails", async () => {
@@ -320,7 +321,9 @@ describe("git integration", () => { · 37 unmodified lines
320321 await expect(core.checkoutBranch({ cwd: source.path, branch: featureBranch })).resolves.toBe(
321322 undefined,
322323 );
323− expect(core.refreshFetchAttempts).toBe(1);
324+ await vi.waitFor(() => {
325+ expect(core.refreshFetchAttempts).toBe(1);
326+ });
324327 expect(await git(source.path, "branch --show-current")).toBe(featureBranch);
325328 });
326329
@@ -362,6 +365,9 @@ describe("git integration", () => { · 35 unmodified lines
362365
363366 const core = new FetchRecordingGitCoreService();
364367 await core.checkoutBranch({ cwd: source.path, branch: featureBranch });
368+ await vi.waitFor(() => {
369+ expect(core.fetchArgs).not.toBeNull();
370+ });
365371
366372 expect(await git(source.path, "branch --show-current")).toBe(featureBranch);
367373 expect(core.fetchArgs).toEqual([
@@ -373,6 +379,67 @@ describe("git integration", () => { · 5 unmodified lines
373379 ]);
374380 });
375381
382+ it("returns checkout result before background upstream refresh completes", async () => {
383+ await using remote = await makeTmpDir();
384+ await using source = await makeTmpDir();
385+ await git(remote.path, "init --bare");
386+
387+ await initRepoWithCommit(source.path);
388+ const defaultBranch = (await listGitBranches({ cwd: source.path })).branches.find(
389+ (branch) => branch.current,
390+ )!.name;
391+ await git(source.path, `remote add origin ${JSON.stringify(remote.path)}`);
392+ await git(source.path, `push -u origin ${defaultBranch}`);
393+
394+ const featureBranch = "feature/background-refresh";
395+ await git(source.path, `checkout -b ${featureBranch}`);
396+ await writeFile(path.join(source.path, "feature.txt"), "feature base\n");
397+ await git(source.path, "add feature.txt");
398+ await git(source.path, "commit -m 'feature base'");
399+ await git(source.path, `push -u origin ${featureBranch}`);
400+ await git(source.path, `checkout ${defaultBranch}`);
401+
402+ class SlowRefreshGitCoreService extends GitCoreService {
403+ fetchStarted = false;
404+ private readonly waitForReleasePromise: Promise<void>;
405+ private releaseWait: () => void = () => undefined;
406+
407+ constructor() {
408+ super();
409+ this.waitForReleasePromise = new Promise<void>((resolve) => {
410+ this.releaseWait = resolve;
411+ });
412+ }
413+
414+ releaseFetch(): void {
415+ this.releaseWait();
416+ }
417+
418+ override async git(
419+ cwd: string,
420+ args: readonly string[],
421+ allowNonZeroExit = false,
422+ ): Promise<void> {
423+ if (args[0] === "fetch") {
424+ this.fetchStarted = true;
425+ await this.waitForReleasePromise;
426+ return;
427+ }
428+ await super.git(cwd, args, allowNonZeroExit);
429+ }
430+ }
431+
432+ const core = new SlowRefreshGitCoreService();
433+ await expect(core.checkoutBranch({ cwd: source.path, branch: featureBranch })).resolves.toBe(
434+ undefined,
435+ );
436+ await vi.waitFor(() => {
437+ expect(core.fetchStarted).toBe(true);
438+ });
439+ expect(await git(source.path, "branch --show-current")).toBe(featureBranch);
440+ core.releaseFetch();
441+ });
442+
376443 it("throws when branch does not exist", async () => {
377444 await using tmp = await makeTmpDir();
378445 await initRepoWithCommit(tmp.path);
apps/server/src/git.ts+2−5
@@ -587,11 +587,8 @@ export class GitCoreService { · 586 unmodified lines
587587 timeoutMs: 10_000,
588588 fallbackErrorMessage: "git checkout failed",
589589 });
590− try {
591− await this.refreshCheckedOutBranchUpstream(input.cwd);
592− } catch {
593− // Best effort: checkout already succeeded, so avoid surfacing refresh failures.
594− }
590+ // Refresh upstream refs in the background so checkout remains responsive.
591+ void this.refreshCheckedOutBranchUpstream(input.cwd).catch(() => undefined);
595592 }
596593
597594 async initRepo(input: GitInitInput): Promise<void> {