Skip to content
Closed
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
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/checkout-branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function createCheckoutBranchHandler() {
}

// Get current branch for reference
const { stdout: currentBranchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
const currentBranch = currentBranchOutput.trim();
Comment on lines +50 to 53
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

wc -l apps/server/src/routes/worktree/routes/checkout-branch.ts

Repository: AutoMaker-Org/automaker

Length of output: 190


🏁 Script executed:

cat -n apps/server/src/routes/worktree/routes/checkout-branch.ts

Repository: AutoMaker-Org/automaker

Length of output: 3121


🏁 Script executed:

rg -n 'detached|symbolic-ref|previousBranch' apps/server/src/routes/worktree/ --type=ts

Repository: AutoMaker-Org/automaker

Length of output: 1996


🏁 Script executed:

# Search for tests related to checkout-branch or detached HEAD
fd -t f '*.test.ts' -o '*.spec.ts' apps/server | xargs rg -l 'checkout.*branch|detached' 2>/dev/null || echo "No matching test files found"

Repository: AutoMaker-Org/automaker

Length of output: 700


🏁 Script executed:

# Check the middleware mentioned in the file (requireValidWorktree)
rg -n 'requireValidWorktree' apps/server/src/routes/worktree/ --type=ts -A 5

Repository: AutoMaker-Org/automaker

Length of output: 5583


🏁 Script executed:

# Check what happens with git symbolic-ref on detached HEAD
# Look at the actual error message and understand the implications
rg -n 'symbolic-ref' apps/server/src/ --type=ts | head -20

Repository: AutoMaker-Org/automaker

Length of output: 1503


🏁 Script executed:

# Check if there's any documentation or TODOs about this
rg -n 'TODO|FIXME|detached|HEAD' apps/server/src/routes/worktree/ --type=ts

Repository: AutoMaker-Org/automaker

Length of output: 3028


🏁 Script executed:

cat -n apps/server/src/routes/worktree/middleware.ts | head -100

Repository: AutoMaker-Org/automaker

Length of output: 2746


🏁 Script executed:

# Check what the middleware validation does
rg -n 'isGitRepo\|hasCommits' apps/server/src/routes/worktree/ --type=ts -B 2 -A 5

Repository: AutoMaker-Org/automaker

Length of output: 49


🏁 Script executed:

# Check if there are any existing tests for worktree operations
find apps/server -type f -name '*.test.*' -o -name '*.spec.*' | head -10

Repository: AutoMaker-Org/automaker

Length of output: 613


🏁 Script executed:

cat -n apps/server/tests/integration/routes/worktree/create.integration.test.ts | head -150

Repository: AutoMaker-Org/automaker

Length of output: 2489


🏁 Script executed:

# Check if there are any tests for checkout-branch, switch-branch, or detached HEAD scenarios
rg -n 'checkout.*branch|switch.*branch|detached' apps/server/tests/ --type=ts

Repository: AutoMaker-Org/automaker

Length of output: 49


🏁 Script executed:

# Understand what git symbolic-ref error looks like and what the implications are
# Let's check the common.ts to see how errors are handled
cat -n apps/server/src/routes/worktree/common.ts

Repository: AutoMaker-Org/automaker

Length of output: 7421


🏁 Script executed:

# Verify what error message git symbolic-ref produces in detached HEAD
# Check if there are any related issues or PRs mentioned in the codebase
rg -n 'detached.*HEAD|symbolic.*ref' docs/ CONTRIBUTING.md README.md 2>/dev/null || echo "No docs mention detached HEAD"

Repository: AutoMaker-Org/automaker

Length of output: 96


🏁 Script executed:

# Confirm the systematic nature by checking all files that use symbolic-ref without error handling
rg -l 'symbolic-ref.*HEAD' apps/server/src/routes/worktree/routes/ --type=ts | wc -l

Repository: AutoMaker-Org/automaker

Length of output: 69


Handle detached HEAD state across symbolic-ref calls.

The git symbolic-ref --short HEAD call will fail when the repository is in a detached HEAD state, even though the repository passes all middleware validation (which only checks that commits exist via git rev-parse --verify HEAD). This affects 10 files in the worktree routes and causes ungraceful 500 errors with generic messages instead of informative feedback.

For checkout-branch.ts specifically: creating a branch from detached HEAD is a valid Git operation (git checkout -b works fine), but retrieving the previous branch state fails. Consider wrapping the git symbolic-ref call in a try-catch to either:

  • Set previousBranch to the commit SHA (via git rev-parse HEAD) when detached
  • Return a clear error message explaining detached HEAD state

This pattern should be addressed consistently across all symbolic-ref calls in the worktree route handlers.

🤖 Prompt for AI Agents
In @apps/server/src/routes/worktree/routes/checkout-branch.ts around lines
50-53, The git symbolic-ref call can throw in detached HEAD state; wrap the
execAsync('git symbolic-ref --short HEAD', { cwd: worktreePath }) invocation
used to compute currentBranch in checkout-branch.ts in a try/catch and, on
failure, call execAsync('git rev-parse HEAD', { cwd: worktreePath }) to set
previousBranch/currentBranch to the commit SHA (or alternatively return a clear
4xx error that explains the detached HEAD) so branch creation via checkout
(e.g., git checkout -b) still works and you avoid unhandled 500s; update the
variables currentBranch/currentBranchOutput/previousBranch accordingly and apply
the same pattern to other handlers that use git symbolic-ref.

Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function createCommitHandler() {
const commitHash = hashOutput.trim().substring(0, 8);

// Get branch name
const { stdout: branchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
const branchName = branchOutput.trim();
Comment on lines +62 to 65
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing error handling for detached HEAD state.

In a detached HEAD state, git symbolic-ref --short HEAD will fail with an error, causing the entire handler to return a 500 error even though the commit operation itself (line 51) succeeded. This could confuse users and lose context about the successful commit.

🔎 Proposed fix to add error handling
-      // Get branch name
-      const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
-        cwd: worktreePath,
-      });
-      const branchName = branchOutput.trim();
+      // Get branch name (may be null in detached HEAD state)
+      let branchName: string | null = null;
+      try {
+        const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
+          cwd: worktreePath,
+        });
+        branchName = branchOutput.trim();
+      } catch {
+        // Detached HEAD state - branch name unavailable
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
const branchName = branchOutput.trim();
// Get branch name (may be null in detached HEAD state)
let branchName: string | null = null;
try {
const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
branchName = branchOutput.trim();
} catch {
// Detached HEAD state - branch name unavailable
}

Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/create-pr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function createCreatePRHandler() {
const effectiveProjectPath = projectPath || worktreePath;

// Get current branch name
const { stdout: branchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file and context around line 46
cat -n apps/server/src/routes/worktree/routes/create-pr.ts | sed -n '30,60p'

Repository: AutoMaker-Org/automaker

Length of output: 1204


🏁 Script executed:

# Run the provided search for detached HEAD handling
rg -n -C3 'detached.HEAD|HEAD detached' --type=ts

Repository: AutoMaker-Org/automaker

Length of output: 49


🏁 Script executed:

# Also search for git symbolic-ref usage in the codebase
rg -n 'git symbolic-ref' --type=ts

Repository: AutoMaker-Org/automaker

Length of output: 1503


🏁 Script executed:

# Search for error handling around the git command in the file
cat -n apps/server/src/routes/worktree/routes/create-pr.ts | head -100

Repository: AutoMaker-Org/automaker

Length of output: 4097


🏁 Script executed:

# View the rest of the create-pr.ts file to see error handling
cat -n apps/server/src/routes/worktree/routes/create-pr.ts | sed -n '100,150p'

Repository: AutoMaker-Org/automaker

Length of output: 2128


🏁 Script executed:

# Check the common.ts to understand execAsync error behavior
cat -n apps/server/src/routes/worktree/routes/common.ts | head -80

Repository: AutoMaker-Org/automaker

Length of output: 147


🏁 Script executed:

# Check if there's worktree initialization that ensures non-detached HEAD
rg -n -C5 'worktree create|worktree add' --type=ts

Repository: AutoMaker-Org/automaker

Length of output: 4737


🏁 Script executed:

# Find common.ts in the worktree routes
find apps/server/src/routes/worktree -name "common.ts"

Repository: AutoMaker-Org/automaker

Length of output: 108


🏁 Script executed:

# Check the error handling wrapper that execAsync would trigger
cat -n apps/server/src/routes/worktree/common.ts | head -100

Repository: AutoMaker-Org/automaker

Length of output: 4136


🏁 Script executed:

# Look at the try-catch block in create-pr.ts to see what happens when execAsync throws
cat -n apps/server/src/routes/worktree/routes/create-pr.ts | sed -n '150,200p'

Repository: AutoMaker-Org/automaker

Length of output: 2516


🏁 Script executed:

# Check the final catch block of the create-pr.ts handler to see generic error handling
cat -n apps/server/src/routes/worktree/routes/create-pr.ts | tail -50

Repository: AutoMaker-Org/automaker

Length of output: 2008


Add explicit detached HEAD error handling.

The git symbolic-ref --short HEAD command fails in detached HEAD state and lacks explicit error handling. While the outer try-catch (line 397) catches this failure, it returns a generic 500 error with raw git stderr. Since worktrees are created on branches and this pattern is used consistently across 10 worktree route files, either:

  1. Add explicit detached HEAD validation that returns a clear error message, or
  2. Document the assumption that worktrees are always on branches (and validate accordingly)

The current implicit error handling via the generic catch block is insufficient for user guidance.

🤖 Prompt for AI Agents
In @apps/server/src/routes/worktree/routes/create-pr.ts around line 46, The
current use of execAsync('git symbolic-ref --short HEAD', ...) assigns to
branchOutput but doesn't explicitly detect a detached HEAD; when that command
fails the outer catch returns a generic 500 with raw git stderr. Update the
handler to explicitly validate the current HEAD: run the branch lookup (or use
'git rev-parse --abbrev-ref HEAD') and if the result is 'HEAD' or empty (or the
command exits non-zero) return a clear 400/422 response like "repository is in a
detached HEAD; worktrees must be created from a branch", instead of letting the
outer try-catch return a generic 500; factor this logic into a shared helper
(e.g., getCurrentBranchOrThrow or validateBranchOnBranch) so the same explicit
detached-HEAD check can be reused across the other worktree route files that use
branchOutput.

cwd: worktreePath,
env: execEnv,
});
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function createDeleteHandler() {
// Get branch name before removing worktree
let branchName: string | null = null;
try {
const { stdout } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
branchName = stdout.trim();
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function createInfoHandler() {
const worktreePath = path.join(projectPath, '.worktrees', featureId);
try {
await secureFs.access(worktreePath);
const { stdout } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
res.json({
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/list-branches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function createListBranchesHandler() {
}

// Get current branch
const { stdout: currentBranchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
const currentBranch = currentBranchOutput.trim();
Comment on lines +37 to 40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and examine the relevant lines
if [ -f "apps/server/src/routes/worktree/routes/list-branches.ts" ]; then
  echo "=== File exists, showing context around lines 37-40 ==="
  wc -l apps/server/src/routes/worktree/routes/list-branches.ts
  echo ""
  echo "=== Lines 30-50 ==="
  sed -n '30,50p' apps/server/src/routes/worktree/routes/list-branches.ts
else
  echo "File not found at specified path"
fi

Repository: AutoMaker-Org/automaker

Length of output: 938


🏁 Script executed:

# Search for similar git symbolic-ref patterns in the codebase
echo "=== Searching for git symbolic-ref usage ==="
rg "git symbolic-ref" --type ts --type tsx -n

Repository: AutoMaker-Org/automaker

Length of output: 156


🏁 Script executed:

# Search for git rev-parse usage to understand common patterns
echo "=== Searching for git rev-parse usage ==="
rg "git rev-parse" --type ts --type tsx -n -A 2

Repository: AutoMaker-Org/automaker

Length of output: 153


🏁 Script executed:

# Search for git symbolic-ref and git rev-parse with correct syntax
echo "=== Searching for git symbolic-ref usage ==="
rg "git symbolic-ref" --type ts -n

echo ""
echo "=== Searching for git rev-parse usage ==="
rg "git rev-parse" --type ts -n -A 2

Repository: AutoMaker-Org/automaker

Length of output: 4748


🏁 Script executed:

# Examine the full function to understand its purpose and callers
echo "=== Full function context (lines 1-60) ==="
sed -n '1,60p' apps/server/src/routes/worktree/routes/list-branches.ts

Repository: AutoMaker-Org/automaker

Length of output: 1977


🏁 Script executed:

# Check if there are error handlers elsewhere in the codebase for execAsync
echo "=== Searching for execAsync error handling patterns ==="
rg "execAsync" --type ts -A 3 | head -50

Repository: AutoMaker-Org/automaker

Length of output: 2952


Add graceful handling for detached HEAD state.

The git symbolic-ref --short HEAD command fails when the worktree is in a detached HEAD state, causing the entire list-branches endpoint to fail. For a read-only list operation, this significantly degrades UX. Catch the error and fall back to git rev-parse --short HEAD to display the commit SHA, or use a sentinel value like "HEAD (detached)".

Proposed fix
-      const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
-        cwd: worktreePath,
-      });
-      const currentBranch = currentBranchOutput.trim();
+      let currentBranch: string;
+      try {
+        const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
+          cwd: worktreePath,
+        });
+        currentBranch = currentBranchOutput.trim();
+      } catch {
+        // Detached HEAD state - use commit SHA
+        const { stdout: commitSha } = await execAsync('git rev-parse --short HEAD', {
+          cwd: worktreePath,
+        });
+        currentBranch = `HEAD (detached at ${commitSha.trim()})`;
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
const currentBranch = currentBranchOutput.trim();
let currentBranch: string;
try {
const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
currentBranch = currentBranchOutput.trim();
} catch {
// Detached HEAD state - use commit SHA
const { stdout: commitSha } = await execAsync('git rev-parse --short HEAD', {
cwd: worktreePath,
});
currentBranch = `HEAD (detached at ${commitSha.trim()})`;
}
🤖 Prompt for AI Agents
In @apps/server/src/routes/worktree/routes/list-branches.ts around lines 37-40,
The current code calls execAsync('git symbolic-ref --short HEAD') and assumes
success; wrap that call (the section producing
currentBranchOutput/currentBranch) in a try/catch, and on failure call
execAsync('git rev-parse --short HEAD') to get the commit SHA (trimmed) or set
currentBranch to a sentinel like "HEAD (detached)"; ensure you reuse execAsync
and assign to currentBranch so downstream logic still works and keep errors
non-fatal for the list-branches endpoint.

Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function createMergeHandler() {
const worktreePath = path.join(projectPath, '.worktrees', featureId);

// Get current branch
const { stdout: currentBranch } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout: currentBranch } = await execAsync('git symbolic-ref --short HEAD', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The currentBranch variable is not used within this function. This execAsync call to get the branch name is unnecessary. Consider removing this block of code (lines 37-40) to improve performance and code clarity.

cwd: projectPath,
});
Comment on lines +38 to 40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n apps/server/src/routes/worktree/routes/merge.ts

Repository: AutoMaker-Org/automaker

Length of output: 2860


Remove unused currentBranch variable—the git symbolic-ref command will fail in detached HEAD state and is never used.

Lines 38-40 fetch the current branch name but never reference it in the merge logic. The command will throw an error if the repository is in a detached HEAD state, with no error handling. Delete these lines entirely since the merge operation does not depend on knowing the current branch.

🤖 Prompt for AI Agents
In @apps/server/src/routes/worktree/routes/merge.ts around lines 38-40, Remove
the unused currentBranch retrieval that calls execAsync('git symbolic-ref
--short HEAD', { cwd: projectPath }) — this can throw on detached HEAD and its
result is never used; delete the const { stdout: currentBranch } = await
execAsync(...) statement (and any immediate unused variable) from merge.ts so
the merge logic no longer executes the git symbolic-ref command or relies on
currentBranch.


Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function createPullHandler() {
}

// Get current branch name
const { stdout: branchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
const branchName = branchOutput.trim();
Comment on lines +31 to 34
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing error handling for detached HEAD state.

When a repo is in a detached HEAD state, git symbolic-ref --short HEAD will fail, causing the handler to return a generic 500 error rather than a meaningful message about the detached HEAD state.

🔎 Proposed fix to add error handling
-      // Get current branch name
-      const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
-        cwd: worktreePath,
-      });
-      const branchName = branchOutput.trim();
+      // Get current branch name
+      let branchName: string;
+      try {
+        const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
+          cwd: worktreePath,
+        });
+        branchName = branchOutput.trim();
+      } catch {
+        res.status(400).json({
+          success: false,
+          error: 'Cannot pull: worktree is in detached HEAD state',
+        });
+        return;
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
const branchName = branchOutput.trim();
// Get current branch name
let branchName: string;
try {
const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
branchName = branchOutput.trim();
} catch {
res.status(400).json({
success: false,
error: 'Cannot pull: worktree is in detached HEAD state',
});
return;
}

Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/push.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function createPushHandler() {
}

// Get branch name
const { stdout: branchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout: branchOutput } = await execAsync('git symbolic-ref --short HEAD', {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n apps/server/src/routes/worktree/routes/push.ts | head -80

Repository: AutoMaker-Org/automaker

Length of output: 2256


🏁 Script executed:

#!/bin/bash
# Create a temporary test repository
TEST_DIR=$(mktemp -d)
cd "$TEST_DIR"
git init

# Create initial commit
echo "test" > file.txt
git add file.txt
git commit -m "initial"

# Get the commit hash
COMMIT_HASH=$(git rev-parse HEAD)

# Test 1: Normal branch state
echo "=== Test 1: Normal branch state ==="
echo "git symbolic-ref --short HEAD:"
git symbolic-ref --short HEAD
echo "git rev-parse --abbrev-ref HEAD:"
git rev-parse --abbrev-ref HEAD

# Test 2: Detached HEAD state
echo ""
echo "=== Test 2: Detached HEAD state ==="
git checkout "$COMMIT_HASH"

echo "git symbolic-ref --short HEAD:"
git symbolic-ref --short HEAD 2>&1 || echo "FAILED with exit code: $?"

echo "git rev-parse --abbrev-ref HEAD:"
git rev-parse --abbrev-ref HEAD 2>&1

# Cleanup
cd /
rm -rf "$TEST_DIR"

Repository: AutoMaker-Org/automaker

Length of output: 1868


🏁 Script executed:

#!/bin/bash
# Create a temporary test repository
TEST_DIR=$(mktemp -d)
cd "$TEST_DIR"
git init

# Configure git
git config user.email "[email protected]"
git config user.name "Test User"

# Create initial commit
echo "test" > file.txt
git add file.txt
git commit -m "initial"

# Get the commit hash
COMMIT_HASH=$(git rev-parse HEAD)

# Test 1: Normal branch state
echo "=== Test 1: Normal branch state ==="
echo "git symbolic-ref --short HEAD:"
git symbolic-ref --short HEAD
echo "Exit code: $?"
echo ""
echo "git rev-parse --abbrev-ref HEAD:"
git rev-parse --abbrev-ref HEAD
echo "Exit code: $?"

# Test 2: Detached HEAD state
echo ""
echo "=== Test 2: Detached HEAD state ==="
echo "Checking out commit: $COMMIT_HASH"
git checkout "$COMMIT_HASH"
echo ""

echo "Verifying detached HEAD:"
git symbolic-ref -q HEAD && echo "Still on branch" || echo "In detached HEAD state"
echo ""

echo "git symbolic-ref --short HEAD:"
git symbolic-ref --short HEAD 2>&1
echo "Exit code: $?"
echo ""

echo "git rev-parse --abbrev-ref HEAD:"
git rev-parse --abbrev-ref HEAD 2>&1
echo "Exit code: $?"

# Cleanup
cd /
rm -rf "$TEST_DIR"

Repository: AutoMaker-Org/automaker

Length of output: 1829


Add error handling for detached HEAD state.

git symbolic-ref --short HEAD fails with exit code 128 in detached HEAD state (error: "ref HEAD is not a symbolic ref"). Since detached HEAD is a possible state in git worktrees, add specific error handling to gracefully inform the user instead of returning a generic 500 error.

Note: git rev-parse --abbrev-ref HEAD returns the string "HEAD" in detached state, not a commit SHA, so it's not a suitable alternative for this use case.

cwd: worktreePath,
});
const branchName = branchOutput.trim();
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/routes/worktree/routes/switch-branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export function createSwitchBranchHandler() {
}

// Get current branch
const { stdout: currentBranchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
const previousBranch = currentBranchOutput.trim();
Comment on lines +90 to 93
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing error handling for detached HEAD state.

When in a detached HEAD state, git symbolic-ref --short HEAD will fail, causing a generic 500 error. However, switching branches from a detached HEAD is a valid operation that should be supported with better error handling.

🔎 Proposed fix to add error handling
-      // Get current branch
-      const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
-        cwd: worktreePath,
-      });
-      const previousBranch = currentBranchOutput.trim();
+      // Get current branch (may be in detached HEAD state)
+      let previousBranch: string;
+      try {
+        const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
+          cwd: worktreePath,
+        });
+        previousBranch = currentBranchOutput.trim();
+      } catch {
+        previousBranch = 'detached HEAD';
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
const previousBranch = currentBranchOutput.trim();
// Get current branch (may be in detached HEAD state)
let previousBranch: string;
try {
const { stdout: currentBranchOutput } = await execAsync('git symbolic-ref --short HEAD', {
cwd: worktreePath,
});
previousBranch = currentBranchOutput.trim();
} catch {
previousBranch = 'detached HEAD';
}
🤖 Prompt for AI Agents
In @apps/server/src/routes/worktree/routes/switch-branch.ts around lines 90-93,
The code assumes `git symbolic-ref --short HEAD` always succeeds; wrap the
execAsync call in try/catch and handle detached HEADs by falling back to
obtaining the current commit (e.g., run `git rev-parse --short HEAD` via
execAsync) or set previousBranch to a stable sentinel like `HEAD@{detached}` so
downstream logic can still switch branches; update references to previousBranch
(the variable set from currentBranchOutput) so they tolerate the fallback value
and ensure errors are only thrown for unexpected failures (rethrow other exec
errors).

Expand Down