fix: improve worktree branch handling in list route

- Updated the logic in the createListHandler to ensure that the branch name is correctly assigned, especially for the main worktree when it may be missing.
- Added checks to handle cases where the worktree directory might not exist, ensuring that removed worktrees are accurately tracked.
- Enhanced the final worktree entry handling to account for scenarios where the output does not end with a blank line, improving robustness.
This commit is contained in:
webdevcody
2026-01-08 00:13:12 -05:00
parent 959467de90
commit fd5f7b873a
2 changed files with 67 additions and 5 deletions

View File

@@ -74,8 +74,23 @@ export function createListHandler() {
} else if (line.startsWith('branch ')) {
current.branch = line.slice(7).replace('refs/heads/', '');
} else if (line === '') {
if (current.path && current.branch) {
if (current.path) {
const isMainWorktree = isFirst;
// If branch is missing (can happen for main worktree in some git states),
// fall back to getCurrentBranch() for the main worktree
let branchName = current.branch;
if (!branchName && isMainWorktree) {
// For main worktree, use the current branch we already fetched
branchName = currentBranch || '';
}
// Skip if we still don't have a branch name (shouldn't happen, but be safe)
if (!branchName) {
current = {};
continue;
}
// Check if the worktree directory actually exists
// Skip checking/pruning the main worktree (projectPath itself)
let worktreeExists = false;
@@ -89,15 +104,15 @@ export function createListHandler() {
// Worktree directory doesn't exist - it was manually deleted
removedWorktrees.push({
path: current.path,
branch: current.branch,
branch: branchName,
});
} else {
// Worktree exists (or is main worktree), add it to the list
worktrees.push({
path: current.path,
branch: current.branch,
branch: branchName,
isMain: isMainWorktree,
isCurrent: current.branch === currentBranch,
isCurrent: branchName === currentBranch,
hasWorktree: true,
});
isFirst = false;
@@ -107,6 +122,48 @@ export function createListHandler() {
}
}
// Handle the last worktree entry if output doesn't end with blank line
if (current.path) {
const isMainWorktree = isFirst;
// If branch is missing (can happen for main worktree in some git states),
// fall back to getCurrentBranch() for the main worktree
let branchName = current.branch;
if (!branchName && isMainWorktree) {
// For main worktree, use the current branch we already fetched
branchName = currentBranch || '';
}
// Only add if we have a branch name
if (branchName) {
// Check if the worktree directory actually exists
// Skip checking/pruning the main worktree (projectPath itself)
let worktreeExists = false;
try {
await secureFs.access(current.path);
worktreeExists = true;
} catch {
worktreeExists = false;
}
if (!isMainWorktree && !worktreeExists) {
// Worktree directory doesn't exist - it was manually deleted
removedWorktrees.push({
path: current.path,
branch: branchName,
});
} else {
// Worktree exists (or is main worktree), add it to the list
worktrees.push({
path: current.path,
branch: branchName,
isMain: isMainWorktree,
isCurrent: branchName === currentBranch,
hasWorktree: true,
});
}
}
}
// Prune removed worktrees from git (only if any were detected)
if (removedWorktrees.length > 0) {
try {

View File

@@ -14,7 +14,6 @@ import {
setupProjectWithPath,
waitForBoardView,
authenticateForTests,
handleLoginScreenIfPresent,
} from '../utils';
const TEST_TEMP_DIR = createTempDirPath('worktree-tests');
@@ -55,10 +54,16 @@ test.describe('Worktree Integration', () => {
await waitForNetworkIdle(page);
await waitForBoardView(page);
// Wait for the worktree selector to appear (indicates API call completed)
const branchLabel = page.getByText('Branch:');
await expect(branchLabel).toBeVisible({ timeout: 10000 });
// Wait for the main branch button to appear
// This ensures the worktree API has returned data with the main branch
const mainBranchButton = page.locator('[data-testid="worktree-branch-main"]');
await expect(mainBranchButton).toBeVisible({ timeout: 15000 });
// Verify the branch name is displayed
await expect(mainBranchButton).toContainText('main');
});
});