diff --git a/apps/api/src/utils/git_control.py b/apps/api/src/utils/git_control.py
index becebc1..92e57ba 100644
--- a/apps/api/src/utils/git_control.py
+++ b/apps/api/src/utils/git_control.py
@@ -124,7 +124,15 @@ def create_branch(repo_path: str, name: str, base_branch: str = "HEAD") -> None:
try:
_run_git_command(repo_path, "rev-parse", "--verify", "HEAD^{commit}")
except RuntimeError:
- _run_git_command(repo_path, "checkout", "--orphan", name)
+ # No commits yet - empty repository
+ try:
+ _run_git_command(repo_path, "checkout", "--orphan", name)
+ except RuntimeError as e:
+ if "work tree" in str(e).lower():
+ # Bare repository - use symbolic-ref instead
+ _run_git_command(repo_path, "symbolic-ref", "HEAD", f"refs/heads/{name}")
+ return
+ raise
return
_run_git_command(repo_path, "branch", name, base_branch)
@@ -155,7 +163,14 @@ def checkout_branch(repo_path: str, name: str) -> None:
Raises:
RuntimeError: If checkout fails
"""
- _run_git_command(repo_path, "checkout", name)
+ try:
+ _run_git_command(repo_path, "checkout", name)
+ except RuntimeError as e:
+ if "work tree" in str(e).lower():
+ # Bare repository - use symbolic-ref instead
+ _run_git_command(repo_path, "symbolic-ref", "HEAD", f"refs/heads/{name}")
+ return
+ raise
def commit_changes(
@@ -290,6 +305,10 @@ def get_current_branch(repo_path: str) -> str:
Current branch name
"""
try:
- return _run_git_command(repo_path, "rev-parse", "--abbrev-ref", "HEAD").strip()
+ branch = _run_git_command(repo_path, "rev-parse", "--abbrev-ref", "HEAD").strip()
+ if branch != "HEAD":
+ return branch
except RuntimeError:
- return _run_git_command(repo_path, "symbolic-ref", "--short", "HEAD").strip()
+ pass
+
+ return _run_git_command(repo_path, "symbolic-ref", "--short", "HEAD").strip()
diff --git a/apps/api/tests/integration/test_git_control.py b/apps/api/tests/integration/test_git_control.py
index a7ab139..37d5425 100644
--- a/apps/api/tests/integration/test_git_control.py
+++ b/apps/api/tests/integration/test_git_control.py
@@ -70,6 +70,20 @@ def test_get_current_branch_handles_unborn_main() -> None:
assert get_current_branch(tmpdir) == "main"
+def test_create_branch_on_bare_repo_with_no_commits() -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ os.system(f"git init --bare {tmpdir}/bare.git >/dev/null 2>&1")
+ create_branch(f"{tmpdir}/bare.git", "main")
+ assert get_current_branch(f"{tmpdir}/bare.git") == "main"
+
+
+def test_checkout_branch_on_bare_repo_with_no_commits() -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ os.system(f"git init --bare {tmpdir}/bare.git >/dev/null 2>&1")
+ checkout_branch(f"{tmpdir}/bare.git", "main")
+ assert get_current_branch(f"{tmpdir}/bare.git") == "main"
+
+
class TestBranchOperations:
"""Tests for branch management functions."""
diff --git a/apps/web/src/components/app-shell.tsx b/apps/web/src/components/app-shell.tsx
index fd2a02f..8636e49 100644
--- a/apps/web/src/components/app-shell.tsx
+++ b/apps/web/src/components/app-shell.tsx
@@ -9,8 +9,9 @@ import { useSessions } from "../state/sessions";
import { Icon } from "./icon";
import type { IconName } from "../utils/icons";
-const NAV_ITEMS: { to: string; label: string; icon: IconName }[] = [
+const NAV_ITEMS: { to: string; label: string; icon: IconName; badge?: "sessions" }[] = [
{ to: "/", label: "Home", icon: "dashboard" },
+ { to: "/sessions", label: "Sessions", icon: "terminal", badge: "sessions" },
{ to: "/projects", label: "Projects", icon: "projects" },
{ to: "/tool-workshop", label: "Tool Workshop", icon: "settings" },
{ to: "/settings", label: "Settings", icon: "settings" }
@@ -83,7 +84,6 @@ export const AppShell = () => {