Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions apps/server/src/vcs/GitVcsDriverCore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3641,6 +3641,47 @@ it.layer(layerTest)("GitVcsDriver core integration", (it) => {
}),
);

it.effect("flattens a generated name when a branch at its parent path blocks it", () =>
Effect.gen(function* () {
const cwd = yield* makeTmpDir();
yield* initRepoWithCommit(cwd);
const driver = yield* GitVcsDriver.GitVcsDriver;
// A local `t3` branch is why launches start on the flat `t3-<hex>` temporary branch.
yield* git(cwd, ["branch", "t3"]);
yield* git(cwd, ["checkout", "-b", "t3-abcd1234"]);
yield* git(cwd, ["branch", "t3-fix-login"]);

const renamed = yield* driver.renameBranch({
cwd,
oldBranch: "t3-abcd1234",
newBranch: "t3/fix-login",
});

assert.equal(renamed.branch, "t3-fix-login-1");
assert.equal(yield* git(cwd, ["branch", "--show-current"]), "t3-fix-login-1");
}),
);

it.effect("skips a flattened name that a nested branch blocks", () =>
Effect.gen(function* () {
const cwd = yield* makeTmpDir();
yield* initRepoWithCommit(cwd);
const driver = yield* GitVcsDriver.GitVcsDriver;
yield* git(cwd, ["branch", "t3"]);
yield* git(cwd, ["checkout", "-b", "t3-abcd1234"]);
yield* git(cwd, ["branch", "t3-fix-login/child"]);

const renamed = yield* driver.renameBranch({
cwd,
oldBranch: "t3-abcd1234",
newBranch: "t3/fix-login",
});

assert.equal(renamed.branch, "t3-fix-login-1");
assert.equal(yield* git(cwd, ["branch", "--show-current"]), "t3-fix-login-1");
}),
);

it.effect("rejects a missing branch without restoring a matching dirty file", () =>
Effect.gen(function* () {
const cwd = yield* makeTmpDir();
Expand Down
27 changes: 24 additions & 3 deletions apps/server/src/vcs/GitVcsDriverCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1247,16 +1247,37 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*

const resolveAvailableBranchName = Effect.fn("resolveAvailableBranchName")(function* (
cwd: string,
desiredBranch: string,
requestedBranch: string,
) {
const isDesiredTaken = yield* branchExists(cwd, desiredBranch);
// A branch at a parent path (such as `t3`) blocks every ref nested under it, so no suffix
// would help; flatten to a sibling the way temporary worktree branches already do.
const segments = requestedBranch.split("/");
let blockedByParent = false;
for (let index = 1; index < segments.length && !blockedByParent; index += 1) {
blockedByParent = yield* branchExists(cwd, segments.slice(0, index).join("/"));
}
const desiredBranch = blockedByParent ? segments.join("-") : requestedBranch;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
// A branch nested under the name (`t3-fix/child`) blocks it as surely as one with the name.
const isBranchNameTaken = (name: string) =>
Effect.zipWith(
branchExists(cwd, name),
runGitStdout(
"GitVcsDriver.branchExists",
cwd,
["for-each-ref", "--count=1", "--format=%(refname)", `refs/heads/${name}/`],
true,
),
(exists, nested) => exists || nested.trim().length > 0,
);

const isDesiredTaken = yield* isBranchNameTaken(desiredBranch);
if (!isDesiredTaken) {
return desiredBranch;
}

for (let suffix = 1; suffix <= 100; suffix += 1) {
const candidate = `${desiredBranch}-${suffix}`;
const isCandidateTaken = yield* branchExists(cwd, candidate);
const isCandidateTaken = yield* isBranchNameTaken(candidate);
if (!isCandidateTaken) {
return candidate;
}
Expand Down
Loading