Persist base branch for Paseo worktrees and gate merge action

This commit is contained in:
Mohamed Boudra
2026-01-23 15:27:42 +07:00
parent 23cd462336
commit eab8713c24
15 changed files with 565 additions and 79 deletions
@@ -12,6 +12,7 @@ import {
NotGitRepoError,
} from "./checkout-git.js";
import { createWorktree } from "./worktree.js";
import { getPaseoWorktreeMetadataPath } from "./worktree-metadata.js";
function initRepo(): { tempDir: string; repoDir: string } {
const tempDir = realpathSync(mkdtempSync(join(tmpdir(), "checkout-git-test-")));
@@ -87,6 +88,7 @@ describe("checkout git utilities", () => {
const result = await createWorktree({
branchName: "main",
cwd: repoDir,
baseBranch: "main",
worktreeSlug: "alpha",
});
@@ -117,6 +119,7 @@ describe("checkout git utilities", () => {
const worktree = await createWorktree({
branchName: "main",
cwd: repoDir,
baseBranch: "main",
worktreeSlug: "merge",
});
@@ -138,6 +141,12 @@ describe("checkout git utilities", () => {
});
expect(baseContainsFeature).toBeDefined();
const statusAfterMerge = await getCheckoutStatus(worktree.worktreePath);
expect(statusAfterMerge.isGit).toBe(true);
if (statusAfterMerge.isGit) {
expect(statusAfterMerge.aheadBehind?.ahead ?? 0).toBe(0);
}
const currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
cwd: worktree.worktreePath,
})
@@ -174,4 +183,96 @@ describe("checkout git utilities", () => {
mergeToBase(repoDir, { baseRef: "main" })
).rejects.toBeInstanceOf(MergeConflictError);
});
it("uses stored baseRefName for Paseo worktrees (no heuristics)", async () => {
// Create a non-default base branch with a unique commit.
execSync("git checkout -b develop", { cwd: repoDir });
writeFileSync(join(repoDir, "file.txt"), "develop\n");
execSync("git add file.txt", { cwd: repoDir });
execSync("git -c commit.gpgsign=false commit -m 'develop change'", { cwd: repoDir });
execSync("git checkout main", { cwd: repoDir });
// Create a worktree/branch based on develop, but keep main as the repo default.
const worktree = await createWorktree({
branchName: "feature",
cwd: repoDir,
baseBranch: "develop",
worktreeSlug: "feature",
});
writeFileSync(join(worktree.worktreePath, "feature.txt"), "feature\n");
execSync("git add feature.txt", { cwd: worktree.worktreePath });
execSync("git -c commit.gpgsign=false commit -m 'feature commit'", {
cwd: worktree.worktreePath,
});
const status = await getCheckoutStatus(worktree.worktreePath);
expect(status.isGit).toBe(true);
expect(status.baseRef).toBe("develop");
expect(status.aheadBehind?.ahead).toBe(1);
const baseDiff = await getCheckoutDiff(worktree.worktreePath, { mode: "base" });
expect(baseDiff.diff).toContain("feature.txt");
expect(baseDiff.diff).not.toContain("file.txt");
});
it("merges to stored baseRefName when baseRef is not provided", async () => {
// Create a non-default base branch with a unique commit.
execSync("git checkout -b develop", { cwd: repoDir });
writeFileSync(join(repoDir, "file.txt"), "develop\n");
execSync("git add file.txt", { cwd: repoDir });
execSync("git -c commit.gpgsign=false commit -m 'develop change'", { cwd: repoDir });
execSync("git checkout main", { cwd: repoDir });
// Create a Paseo worktree configured to use develop as base.
const worktree = await createWorktree({
branchName: "feature",
cwd: repoDir,
baseBranch: "develop",
worktreeSlug: "merge-to-develop",
});
writeFileSync(join(worktree.worktreePath, "feature.txt"), "feature\n");
execSync("git add feature.txt", { cwd: worktree.worktreePath });
execSync("git -c commit.gpgsign=false commit -m 'feature commit'", {
cwd: worktree.worktreePath,
});
const featureCommit = execSync("git rev-parse HEAD", { cwd: worktree.worktreePath })
.toString()
.trim();
// No baseRef passed: should merge into the configured base (develop), not default/main.
await mergeToBase(worktree.worktreePath);
execSync(`git merge-base --is-ancestor ${featureCommit} develop`, {
cwd: repoDir,
stdio: "pipe",
});
expect(() =>
execSync(`git merge-base --is-ancestor ${featureCommit} main`, {
cwd: repoDir,
stdio: "pipe",
})
).toThrow();
});
it("throws if Paseo worktree base metadata is missing", async () => {
const worktree = await createWorktree({
branchName: "main",
cwd: repoDir,
baseBranch: "main",
worktreeSlug: "missing-metadata",
});
const metadataPath = getPaseoWorktreeMetadataPath(worktree.worktreePath);
rmSync(metadataPath, { force: true });
await expect(getCheckoutStatus(worktree.worktreePath)).rejects.toThrow(
/base/i
);
await expect(getCheckoutDiff(worktree.worktreePath, { mode: "base" })).rejects.toThrow(
/base/i
);
await expect(mergeToBase(worktree.worktreePath)).rejects.toThrow(/base/i);
});
});