Skip to content

Commit c5baeb1

Browse files
built-by-asclaude
andauthored
Add branch name validation for worktree creation (#17)
Prevents creation of worktrees with branch names that already exist. Shows real-time validation with red error text when a duplicate branch name is entered. Validates against both local and remote branches. Co-authored-by: Claude <[email protected]>
1 parent 0097c71 commit c5baeb1

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ <h2 class="modal-title">New Session Configuration</h2>
8686
<div class="form-group" id="branch-name-group">
8787
<label class="form-label">Branch Name (optional)</label>
8888
<input type="text" id="branch-name" class="form-input" placeholder="e.g., feature/add-login" />
89-
<span class="text-xs text-gray-400 mt-1 block">Custom branch name for worktree (will also be used as session name)</span>
89+
<span id="branch-name-error" class="text-xs text-red-400 mt-1 hidden">Branch already exists</span>
90+
<span id="branch-name-help" class="text-xs text-gray-400 mt-1 block">Custom branch name for worktree (will also be used as session name)</span>
9091
</div>
9192

9293
<div class="form-group">

renderer.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ async function loadAndPopulateBranches(
221221
selectedBranch?: string
222222
): Promise<void> {
223223
const branches = await ipcRenderer.invoke("get-branches", directory);
224+
existingBranches = branches;
224225
parentBranchSelect.innerHTML = "";
225226

226227
if (branches.length === 0) {
@@ -758,6 +759,13 @@ ipcRenderer.on("session-created", (_event, sessionId: string, persistedSession:
758759
if (setupCommandsTextarea) {
759760
setupCommandsTextarea.value = "";
760761
}
762+
763+
// Reset validation state
764+
const branchNameError = document.getElementById("branch-name-error");
765+
const branchNameHelp = document.getElementById("branch-name-help");
766+
branchNameError?.classList.add("hidden");
767+
branchNameHelp?.classList.remove("hidden");
768+
existingBranches = [];
761769
});
762770

763771
// Handle session reopened
@@ -808,8 +816,44 @@ const localDescription = document.getElementById("local-description");
808816
const browseDirBtn = document.getElementById("browse-dir");
809817
const cancelBtn = document.getElementById("cancel-session");
810818
const createBtn = document.getElementById("create-session") as HTMLButtonElement;
819+
const branchNameInput = document.getElementById("branch-name") as HTMLInputElement;
820+
const branchNameError = document.getElementById("branch-name-error");
821+
const branchNameHelp = document.getElementById("branch-name-help");
811822

812823
let selectedDirectory = "";
824+
let existingBranches: string[] = [];
825+
826+
// Validate branch name
827+
function validateBranchName(): boolean {
828+
const branchName = branchNameInput?.value.trim();
829+
830+
if (!branchName) {
831+
// Empty branch name is allowed (it's optional)
832+
branchNameError?.classList.add("hidden");
833+
branchNameHelp?.classList.remove("hidden");
834+
return true;
835+
}
836+
837+
// Check if branch already exists
838+
const branchExists = existingBranches.some(branch =>
839+
branch === branchName || branch === `origin/${branchName}`
840+
);
841+
842+
if (branchExists) {
843+
branchNameError?.classList.remove("hidden");
844+
branchNameHelp?.classList.add("hidden");
845+
return false;
846+
} else {
847+
branchNameError?.classList.add("hidden");
848+
branchNameHelp?.classList.remove("hidden");
849+
return true;
850+
}
851+
}
852+
853+
// Add input event listener for branch name validation
854+
branchNameInput?.addEventListener("input", () => {
855+
validateBranchName();
856+
});
813857

814858
// Toggle skip permissions checkbox visibility based on coding agent
815859
codingAgentSelect?.addEventListener("change", () => {
@@ -914,6 +958,10 @@ cancelBtn?.addEventListener("click", () => {
914958
projectDirInput.value = "";
915959
selectedDirectory = "";
916960
parentBranchSelect.innerHTML = '<option value="">Loading branches...</option>';
961+
branchNameInput.value = "";
962+
branchNameError?.classList.add("hidden");
963+
branchNameHelp?.classList.remove("hidden");
964+
existingBranches = [];
917965
});
918966

919967
// Create session button
@@ -931,6 +979,12 @@ createBtn?.addEventListener("click", () => {
931979
return;
932980
}
933981

982+
// Validate branch name doesn't already exist for worktree sessions
983+
if (sessionType === SessionType.WORKTREE && !validateBranchName()) {
984+
alert("Cannot create worktree: branch already exists");
985+
return;
986+
}
987+
934988
const setupCommandsTextarea = document.getElementById("setup-commands") as HTMLTextAreaElement;
935989
const setupCommandsText = setupCommandsTextarea?.value.trim();
936990
const setupCommands = setupCommandsText

0 commit comments

Comments
 (0)