| ... | @@ -3052,7 +3052,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3052,7 +3052,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3052 | }); | 3052 | }); |
| 3053 | | 3053 | |
| 3054 | describe("commit context", () => { | 3054 | describe("commit context", () => { |
| 3055 | it.effect("stages selected files and commits only those files", () => | 3055 | it.effect("prepares selected files without staging and commits only those files", () => |
| 3056 | Effect.gen(function* () { | 3056 | Effect.gen(function* () { |
| 3057 | const cwd = yield* makeTmpDir(); | 3057 | const cwd = yield* makeTmpDir(); |
| 3058 | yield* initRepoWithCommit(cwd); | 3058 | yield* initRepoWithCommit(cwd); |
| ... | @@ -3064,8 +3064,9 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3064,8 +3064,9 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3064 | const context = yield* driver.prepareCommitContext(cwd, ["a.txt"]); | 3064 | const context = yield* driver.prepareCommitContext(cwd, ["a.txt"]); |
| 3065 | assert.include(context?.stagedSummary ?? "", "a.txt"); | 3065 | assert.include(context?.stagedSummary ?? "", "a.txt"); |
| 3066 | assert.notInclude(context?.stagedSummary ?? "", "b.txt"); | 3066 | assert.notInclude(context?.stagedSummary ?? "", "b.txt"); |
| | 3067 | assert.equal(yield* git(cwd, ["diff", "--cached", "--name-only"]), ""); |
| 3067 | | 3068 | |
| 3068 | const commit = yield* driver.commit(cwd, "Add a", ""); | 3069 | const commit = yield* driver.commit(cwd, "Add a", "", { stage: { filePaths: ["a.txt"] } }); |
| 3069 | assert.match(commit.commitSha, /^[a-f0-9]{40}$/); | 3070 | assert.match(commit.commitSha, /^[a-f0-9]{40}$/); |
| 3070 | assert.equal(yield* git(cwd, ["log", "-1", "--pretty=%s"]), "Add a"); | 3071 | assert.equal(yield* git(cwd, ["log", "-1", "--pretty=%s"]), "Add a"); |
| 3071 | | 3072 | |
| ... | @@ -3084,14 +3085,96 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3084,14 +3085,96 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3084 | yield* writeTextFile(cwd, "selected[1].txt", "literal\n"); | 3085 | yield* writeTextFile(cwd, "selected[1].txt", "literal\n"); |
| 3085 | yield* writeTextFile(cwd, "selected1.txt", "pattern match\n"); | 3086 | yield* writeTextFile(cwd, "selected1.txt", "pattern match\n"); |
| 3086 | | 3087 | |
| 3087 | yield* driver.prepareCommitContext(cwd, ["selected[1].txt"]); | 3088 | const context = yield* driver.prepareCommitContext(cwd, ["selected[1].txt"]); |
| 3088 | | 3089 | |
| 3089 | assert.equal(yield* git(cwd, ["diff", "--cached", "--name-only"]), "selected[1].txt"); | 3090 | assert.include(context?.stagedSummary ?? "", "selected[1].txt"); |
| | 3091 | assert.notInclude(context?.stagedSummary ?? "", "selected1.txt"); |
| | 3092 | assert.equal(yield* git(cwd, ["diff", "--cached", "--name-only"]), ""); |
| | 3093 | yield* driver.commit(cwd, "Add literal path", "", { |
| | 3094 | stage: { filePaths: ["selected[1].txt"] }, |
| | 3095 | }); |
| | 3096 | assert.equal( |
| | 3097 | yield* git(cwd, ["show", "--pretty=", "--name-only", "HEAD"]), |
| | 3098 | "selected[1].txt", |
| | 3099 | ); |
| 3090 | | 3100 | |
| 3091 | const status = yield* git(cwd, ["status", "--porcelain"]); | 3101 | const status = yield* git(cwd, ["status", "--porcelain"]); |
| 3092 | assert.include(status, "?? selected1.txt"); | 3102 | assert.include(status, "?? selected1.txt"); |
| 3093 | }), | 3103 | }), |
| 3094 | ); | 3104 | ); |
| | 3105 | |
| | 3106 | it.effect.each([false, true])( |
| | 3107 | "preserves partial staging and split-index files (split: %s)", |
| | 3108 | (split) => |
| | 3109 | Effect.gen(function* () { |
| | 3110 | const cwd = yield* makeTmpDir(); |
| | 3111 | yield* initRepoWithCommit(cwd); |
| | 3112 | if (split) yield* git(cwd, ["config", "core.splitIndex", "true"]); |
| | 3113 | yield* writeTextFile(cwd, "README.md", "# test\nstaged\n"); |
| | 3114 | yield* git(cwd, ["add", "README.md"]); |
| | 3115 | yield* writeTextFile(cwd, "README.md", "# test\nstaged\nunstaged\n"); |
| | 3116 | yield* writeTextFile(cwd, "untracked.txt", "untracked\n"); |
| | 3117 | yield* writeTextFile(cwd, ".gitignore", "ignored.txt\n"); |
| | 3118 | yield* writeTextFile(cwd, "ignored.txt", "ignored\n"); |
| | 3119 | const before = NodeFS.readFileSync(`${cwd}/.git/index`); |
| | 3120 | const shared = NodeFS.readdirSync(`${cwd}/.git`).filter((name) => |
| | 3121 | name.startsWith("sharedindex."), |
| | 3122 | ); |
| | 3123 | const driver = yield* GitVcsDriver.GitVcsDriver; |
| | 3124 | for (const paths of [undefined, ["README.md"]]) { |
| | 3125 | const context = yield* driver.prepareCommitContext(cwd, paths); |
| | 3126 | assert.include(context?.stagedPatch ?? "", "unstaged"); |
| | 3127 | assert.notInclude(context?.stagedSummary ?? "", "ignored.txt"); |
| | 3128 | assert.deepEqual(NodeFS.readFileSync(`${cwd}/.git/index`), before); |
| | 3129 | assert.deepEqual( |
| | 3130 | NodeFS.readdirSync(`${cwd}/.git`).filter((name) => name.startsWith("sharedindex.")), |
| | 3131 | shared, |
| | 3132 | ); |
| | 3133 | } |
| | 3134 | }), |
| | 3135 | ); |
| | 3136 | |
| | 3137 | it.effect("prepares an unborn repository without creating a real index", () => |
| | 3138 | Effect.gen(function* () { |
| | 3139 | const cwd = yield* makeTmpDir(); |
| | 3140 | const driver = yield* GitVcsDriver.GitVcsDriver; |
| | 3141 | yield* driver.initRepo({ cwd }); |
| | 3142 | yield* git(cwd, ["config", "user.email", "test@test.com"]); |
| | 3143 | yield* git(cwd, ["config", "user.name", "Test"]); |
| | 3144 | yield* writeTextFile(cwd, "initial.txt", "initial\n"); |
| | 3145 | for (const paths of [undefined, ["initial.txt"]]) { |
| | 3146 | const context = yield* driver.prepareCommitContext(cwd, paths); |
| | 3147 | assert.include(context?.stagedSummary ?? "", "initial.txt"); |
| | 3148 | assert.isFalse(NodeFS.existsSync(`${cwd}/.git/index`)); |
| | 3149 | } |
| | 3150 | yield* driver.commit(cwd, "Initial", "", { stage: { filePaths: ["initial.txt"] } }); |
| | 3151 | assert.equal(yield* git(cwd, ["show", "HEAD:initial.txt"]), "initial"); |
| | 3152 | }), |
| | 3153 | ); |
| | 3154 | |
| | 3155 | it.effect("preserves merge metadata while preparing selected and all files", () => |
| | 3156 | Effect.gen(function* () { |
| | 3157 | const cwd = yield* makeTmpDir(); |
| | 3158 | const { initialBranch } = yield* initRepoWithCommit(cwd); |
| | 3159 | yield* git(cwd, ["checkout", "-b", "feature"]); |
| | 3160 | yield* writeTextFile(cwd, "feature.txt", "feature\n"); |
| | 3161 | yield* git(cwd, ["add", "."]); |
| | 3162 | yield* git(cwd, ["commit", "-m", "Feature"]); |
| | 3163 | yield* git(cwd, ["checkout", initialBranch]); |
| | 3164 | yield* git(cwd, ["merge", "--no-commit", "--no-ff", "feature"]); |
| | 3165 | yield* writeTextFile(cwd, "README.md", "# test\nmerge edit\n"); |
| | 3166 | const names = ["index", "MERGE_HEAD", "MERGE_MSG", "ORIG_HEAD"]; |
| | 3167 | const before = names.map((name) => NodeFS.readFileSync(`${cwd}/.git/${name}`)); |
| | 3168 | const driver = yield* GitVcsDriver.GitVcsDriver; |
| | 3169 | for (const paths of [undefined, ["README.md"]]) { |
| | 3170 | yield* driver.prepareCommitContext(cwd, paths); |
| | 3171 | assert.deepEqual( |
| | 3172 | names.map((name) => NodeFS.readFileSync(`${cwd}/.git/${name}`)), |
| | 3173 | before, |
| | 3174 | ); |
| | 3175 | } |
| | 3176 | }), |
| | 3177 | ); |
| 3095 | }); | 3178 | }); |
| 3096 | | 3179 | |
| 3097 | describe("remote operations", () => { | 3180 | describe("remote operations", () => { |
| ... | @@ -3271,7 +3354,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3271,7 +3354,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3271 | refName: "feature/push", | 3354 | refName: "feature/push", |
| 3272 | }); | 3355 | }); |
| 3273 | yield* writeTextFile(cwd, "feature.txt", "feature\n"); | 3356 | yield* writeTextFile(cwd, "feature.txt", "feature\n"); |
| 3274 | yield* (yield* GitVcsDriver.GitVcsDriver).prepareCommitContext(cwd); | 3357 | yield* git(cwd, ["add", "-A"]); |
| 3275 | yield* (yield* GitVcsDriver.GitVcsDriver).commit(cwd, "Add feature", ""); | 3358 | yield* (yield* GitVcsDriver.GitVcsDriver).commit(cwd, "Add feature", ""); |
| 3276 | | 3359 | |
| 3277 | const pushed = yield* (yield* GitVcsDriver.GitVcsDriver).pushCurrentBranch(cwd, null); | 3360 | const pushed = yield* (yield* GitVcsDriver.GitVcsDriver).pushCurrentBranch(cwd, null); |
| ... | @@ -3343,7 +3426,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3343,7 +3426,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3343 | yield* git(cwd, ["remote", "add", "origin", remote]); | 3426 | yield* git(cwd, ["remote", "add", "origin", remote]); |
| 3344 | yield* git(cwd, ["push", "-u", "origin", "main"]); | 3427 | yield* git(cwd, ["push", "-u", "origin", "main"]); |
| 3345 | yield* writeTextFile(cwd, "upstream.txt", "upstream\n"); | 3428 | yield* writeTextFile(cwd, "upstream.txt", "upstream\n"); |
| 3346 | yield* driver.prepareCommitContext(cwd); | 3429 | yield* git(cwd, ["add", "-A"]); |
| 3347 | yield* driver.commit(cwd, "Add upstream update", ""); | 3430 | yield* driver.commit(cwd, "Add upstream update", ""); |
| 3348 | | 3431 | |
| 3349 | const pushed = yield* driver.pushCurrentBranch(cwd, null); | 3432 | const pushed = yield* driver.pushCurrentBranch(cwd, null); |
| ... | @@ -3384,7 +3467,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3384,7 +3467,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3384 | const devSha = yield* git(cwd, ["rev-parse", "HEAD"]); | 3467 | const devSha = yield* git(cwd, ["rev-parse", "HEAD"]); |
| 3385 | yield* git(cwd, ["checkout", "-b", "feature/x", "origin/dev"]); | 3468 | yield* git(cwd, ["checkout", "-b", "feature/x", "origin/dev"]); |
| 3386 | yield* writeTextFile(cwd, "feature.txt", "feature\n"); | 3469 | yield* writeTextFile(cwd, "feature.txt", "feature\n"); |
| 3387 | yield* driver.prepareCommitContext(cwd); | 3470 | yield* git(cwd, ["add", "-A"]); |
| 3388 | yield* driver.commit(cwd, "Add feature", ""); | 3471 | yield* driver.commit(cwd, "Add feature", ""); |
| 3389 | | 3472 | |
| 3390 | const pushed = yield* driver.pushCurrentBranch(cwd, null); | 3473 | const pushed = yield* driver.pushCurrentBranch(cwd, null); |
| ... | @@ -3418,7 +3501,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3418,7 +3501,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3418 | yield* git(cwd, ["checkout", "-b", "feature/y", "origin/main"]); | 3501 | yield* git(cwd, ["checkout", "-b", "feature/y", "origin/main"]); |
| 3419 | yield* git(cwd, ["config", "branch.feature/y.gh-merge-base", "release/v2"]); | 3502 | yield* git(cwd, ["config", "branch.feature/y.gh-merge-base", "release/v2"]); |
| 3420 | yield* writeTextFile(cwd, "feature.txt", "feature\n"); | 3503 | yield* writeTextFile(cwd, "feature.txt", "feature\n"); |
| 3421 | yield* driver.prepareCommitContext(cwd); | 3504 | yield* git(cwd, ["add", "-A"]); |
| 3422 | yield* driver.commit(cwd, "Add feature", ""); | 3505 | yield* driver.commit(cwd, "Add feature", ""); |
| 3423 | | 3506 | |
| 3424 | const pushed = yield* driver.pushCurrentBranch(cwd, null); | 3507 | const pushed = yield* driver.pushCurrentBranch(cwd, null); |
| ... | @@ -3456,7 +3539,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { | ... | @@ -3456,7 +3539,7 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 3456 | "upstream/effect-atom", | 3539 | "upstream/effect-atom", |
| 3457 | ); | 3540 | ); |
| 3458 | yield* writeTextFile(cwd, "alias.txt", "alias\n"); | 3541 | yield* writeTextFile(cwd, "alias.txt", "alias\n"); |
| 3459 | yield* driver.prepareCommitContext(cwd); | 3542 | yield* git(cwd, ["add", "-A"]); |
| 3460 | yield* driver.commit(cwd, "Add alias update", ""); | 3543 | yield* driver.commit(cwd, "Add alias update", ""); |
| 3461 | | 3544 | |
| 3462 | const pushed = yield* driver.pushCurrentBranch(cwd, null); | 3545 | const pushed = yield* driver.pushCurrentBranch(cwd, null); |