| ... | ... | @@ -826,7 +826,7 @@ it.layer(TestLayer)("git integration", (it) => { |
| 826 | 826 | }), |
| 827 | 827 | ); |
| 828 | 828 | |
| 829 | | it.effect("shares upstream refreshes across worktrees that use the same git common dir", () => |
| 829 | it.effect("coalesces upstream refreshes across sibling worktrees on the same remote", () => |
| 830 | 830 | Effect.gen(function* () { |
| 831 | 831 | const ok = (stdout = "") => |
| 832 | 832 | Effect.succeed({ |
| ... | ... | @@ -845,7 +845,9 @@ it.layer(TestLayer)("git integration", (it) => { |
| 845 | 845 | input.args[2] === "--symbolic-full-name" && |
| 846 | 846 | input.args[3] === "@{upstream}" |
| 847 | 847 | ) { |
| 848 | | return ok("origin/main\n"); |
| 848 | return ok( |
| 849 | input.cwd === "/repo/worktrees/pr-123" ? "origin/feature/pr-123\n" : "origin/main\n", |
| 850 | ); |
| 849 | 851 | } |
| 850 | 852 | if (input.args[0] === "remote") { |
| 851 | 853 | return ok("origin\n"); |
| ... | ... | @@ -856,10 +858,22 @@ it.layer(TestLayer)("git integration", (it) => { |
| 856 | 858 | if (input.args[0] === "--git-dir" && input.args[2] === "fetch") { |
| 857 | 859 | fetchCount += 1; |
| 858 | 860 | expect(input.cwd).toBe("/repo"); |
| 861 | expect(input.args).toEqual([ |
| 862 | "--git-dir", |
| 863 | "/repo/.git", |
| 864 | "fetch", |
| 865 | "--quiet", |
| 866 | "--no-tags", |
| 867 | "origin", |
| 868 | ]); |
| 859 | 869 | return ok(); |
| 860 | 870 | } |
| 861 | 871 | if (input.operation === "GitCore.statusDetails.status") { |
| 862 | | return ok("# branch.head main\n# branch.upstream origin/main\n# branch.ab +0 -0\n"); |
| 872 | return ok( |
| 873 | input.cwd === "/repo/worktrees/pr-123" |
| 874 | ? "# branch.head feature/pr-123\n# branch.upstream origin/feature/pr-123\n# branch.ab +0 -0\n" |
| 875 | : "# branch.head main\n# branch.upstream origin/main\n# branch.ab +0 -0\n", |
| 876 | ); |
| 863 | 877 | } |
| 864 | 878 | if ( |
| 865 | 879 | input.operation === "GitCore.statusDetails.unstagedNumstat" || |
| ... | ... | @@ -886,70 +900,80 @@ it.layer(TestLayer)("git integration", (it) => { |
| 886 | 900 | }), |
| 887 | 901 | ); |
| 888 | 902 | |
| 889 | | it.effect("briefly backs off failed upstream refreshes across sibling worktrees", () => |
| 890 | | Effect.gen(function* () { |
| 891 | | const ok = (stdout = "") => |
| 892 | | Effect.succeed({ |
| 893 | | code: 0, |
| 894 | | stdout, |
| 895 | | stderr: "", |
| 896 | | stdoutTruncated: false, |
| 897 | | stderrTruncated: false, |
| 898 | | }); |
| 903 | it.effect( |
| 904 | "briefly backs off failed upstream refreshes across sibling worktrees on one remote", |
| 905 | () => |
| 906 | Effect.gen(function* () { |
| 907 | const ok = (stdout = "") => |
| 908 | Effect.succeed({ |
| 909 | code: 0, |
| 910 | stdout, |
| 911 | stderr: "", |
| 912 | stdoutTruncated: false, |
| 913 | stderrTruncated: false, |
| 914 | }); |
| 899 | 915 | |
| 900 | | let fetchCount = 0; |
| 901 | | const core = yield* makeIsolatedGitCore((input) => { |
| 902 | | if ( |
| 903 | | input.args[0] === "rev-parse" && |
| 904 | | input.args[1] === "--abbrev-ref" && |
| 905 | | input.args[2] === "--symbolic-full-name" && |
| 906 | | input.args[3] === "@{upstream}" |
| 907 | | ) { |
| 908 | | return ok("origin/main\n"); |
| 909 | | } |
| 910 | | if (input.args[0] === "remote") { |
| 911 | | return ok("origin\n"); |
| 912 | | } |
| 913 | | if (input.args[0] === "rev-parse" && input.args[1] === "--git-common-dir") { |
| 914 | | return ok("/repo/.git\n"); |
| 915 | | } |
| 916 | | if (input.args[0] === "--git-dir" && input.args[2] === "fetch") { |
| 917 | | fetchCount += 1; |
| 916 | let fetchCount = 0; |
| 917 | const core = yield* makeIsolatedGitCore((input) => { |
| 918 | if ( |
| 919 | input.args[0] === "rev-parse" && |
| 920 | input.args[1] === "--abbrev-ref" && |
| 921 | input.args[2] === "--symbolic-full-name" && |
| 922 | input.args[3] === "@{upstream}" |
| 923 | ) { |
| 924 | return ok( |
| 925 | input.cwd === "/repo/worktrees/pr-123" |
| 926 | ? "origin/feature/pr-123\n" |
| 927 | : "origin/main\n", |
| 928 | ); |
| 929 | } |
| 930 | if (input.args[0] === "remote") { |
| 931 | return ok("origin\n"); |
| 932 | } |
| 933 | if (input.args[0] === "rev-parse" && input.args[1] === "--git-common-dir") { |
| 934 | return ok("/repo/.git\n"); |
| 935 | } |
| 936 | if (input.args[0] === "--git-dir" && input.args[2] === "fetch") { |
| 937 | fetchCount += 1; |
| 938 | return Effect.fail( |
| 939 | new GitCommandError({ |
| 940 | operation: input.operation, |
| 941 | command: `git ${input.args.join(" ")}`, |
| 942 | cwd: input.cwd, |
| 943 | detail: "simulated fetch timeout", |
| 944 | }), |
| 945 | ); |
| 946 | } |
| 947 | if (input.operation === "GitCore.statusDetails.status") { |
| 948 | return ok( |
| 949 | input.cwd === "/repo/worktrees/pr-123" |
| 950 | ? "# branch.head feature/pr-123\n# branch.upstream origin/feature/pr-123\n# branch.ab +0 -0\n" |
| 951 | : "# branch.head main\n# branch.upstream origin/main\n# branch.ab +0 -0\n", |
| 952 | ); |
| 953 | } |
| 954 | if ( |
| 955 | input.operation === "GitCore.statusDetails.unstagedNumstat" || |
| 956 | input.operation === "GitCore.statusDetails.stagedNumstat" |
| 957 | ) { |
| 958 | return ok(); |
| 959 | } |
| 960 | if (input.operation === "GitCore.statusDetails.defaultRef") { |
| 961 | return ok("refs/remotes/origin/main\n"); |
| 962 | } |
| 918 | 963 | return Effect.fail( |
| 919 | 964 | new GitCommandError({ |
| 920 | 965 | operation: input.operation, |
| 921 | 966 | command: `git ${input.args.join(" ")}`, |
| 922 | 967 | cwd: input.cwd, |
| 923 | | detail: "simulated fetch timeout", |
| 968 | detail: "Unexpected git command in refresh failure cooldown test.", |
| 924 | 969 | }), |
| 925 | 970 | ); |
| 926 | | } |
| 927 | | if (input.operation === "GitCore.statusDetails.status") { |
| 928 | | return ok("# branch.head main\n# branch.upstream origin/main\n# branch.ab +0 -0\n"); |
| 929 | | } |
| 930 | | if ( |
| 931 | | input.operation === "GitCore.statusDetails.unstagedNumstat" || |
| 932 | | input.operation === "GitCore.statusDetails.stagedNumstat" |
| 933 | | ) { |
| 934 | | return ok(); |
| 935 | | } |
| 936 | | if (input.operation === "GitCore.statusDetails.defaultRef") { |
| 937 | | return ok("refs/remotes/origin/main\n"); |
| 938 | | } |
| 939 | | return Effect.fail( |
| 940 | | new GitCommandError({ |
| 941 | | operation: input.operation, |
| 942 | | command: `git ${input.args.join(" ")}`, |
| 943 | | cwd: input.cwd, |
| 944 | | detail: "Unexpected git command in refresh failure cooldown test.", |
| 945 | | }), |
| 946 | | ); |
| 947 | | }); |
| 971 | }); |
| 948 | 972 | |
| 949 | | yield* core.statusDetails("/repo/worktrees/main"); |
| 950 | | yield* core.statusDetails("/repo/worktrees/pr-123"); |
| 951 | | expect(fetchCount).toBe(1); |
| 952 | | }), |
| 973 | yield* core.statusDetails("/repo/worktrees/main"); |
| 974 | yield* core.statusDetails("/repo/worktrees/pr-123"); |
| 975 | expect(fetchCount).toBe(1); |
| 976 | }), |
| 953 | 977 | ); |
| 954 | 978 | |
| 955 | 979 | it.effect("throws when branch does not exist", () => |
| ... | ... | @@ -1047,7 +1071,6 @@ it.layer(TestLayer)("git integration", (it) => { |
| 1047 | 1071 | "--quiet", |
| 1048 | 1072 | "--no-tags", |
| 1049 | 1073 | remoteName, |
| 1050 | | `+refs/heads/${featureBranch}:refs/remotes/${remoteName}/${featureBranch}`, |
| 1051 | 1074 | ]); |
| 1052 | 1075 | }), |
| 1053 | 1076 | ); |