Skip to content

Branch switching blocked by untracked files (should only block on modified tracked files) #753

@JasonBroderick

Description

@JasonBroderick

Summary

The worktree branch switching endpoint incorrectly blocks switching when there are untracked files (?? status). It should only block when there are modifications to tracked files that could be lost during checkout.

Steps to Reproduce

  1. Create a new project with ensureInitialCommit() (empty initial commit)
  2. Have untracked directories like .automaker/, .claude/, etc.
  3. Generate features with branchName fields (creates worktrees)
  4. Try to switch to a worktree branch via the UI

Expected Behavior

Branch switch succeeds. Untracked files are safe - git leaves them alone during checkout.

Actual Behavior

Error: Cannot switch branches: you have uncommitted changes (?? .automaker/, ?? .claude/, ?? .cursor/, ...). Please commit your changes first.

Root Cause

In apps/server/src/routes/worktree/routes/switch-branch.ts, the hasUncommittedChanges() function (lines 23-39) treats ALL git status --porcelain output as blocking changes:

async function hasUncommittedChanges(cwd: string): Promise<boolean> {
  const { stdout } = await execAsync('git status --porcelain', { cwd });
  const lines = stdout.trim().split('\n').filter((line) => {
    if (!line.trim()) return false;
    if (line.includes('.worktrees/')) return false;  // Only excludes .worktrees/
    return true;
  });
  return lines.length > 0;  // ANY remaining output = blocked
}

The code doesn't distinguish between:

  • ?? (untracked files) - safe to switch
  • M , A , D , etc. (modifications to tracked files) - should block

Suggested Fix

Filter out untracked files from the check:

const lines = stdout.trim().split('\n').filter((line) => {
  if (!line.trim()) return false;
  if (line.startsWith('??')) return false;  // Untracked files are safe
  if (line.includes('.worktrees/')) return false;
  return true;
});

Alternatively, use git status --porcelain --untracked-files=no to exclude untracked files from the output entirely.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions