diff --git a/apps/mobile/src/features/threads/git/GitBranchesSheet.occupancy.test.tsx b/apps/mobile/src/features/threads/git/GitBranchesSheet.occupancy.test.tsx
new file mode 100644
index 0000000000000..7667b0c793fc4
--- /dev/null
+++ b/apps/mobile/src/features/threads/git/GitBranchesSheet.occupancy.test.tsx
@@ -0,0 +1,100 @@
+import { act, type ReactNode } from "react";
+import { createRoot } from "react-dom/client";
+import { expect, it, vi } from "vite-plus/test";
+
+const checkout = vi.hoisted(() => ({
+ projectRoot: "",
+ worktreePath: null as string | null,
+ branches: [] as Array<{ name: string; worktreePath: string | null }>,
+}));
+
+vi.mock("react-native", () => ({
+ Platform: { OS: "android" },
+ View: ({ children }: { children: ReactNode }) =>
{children}
,
+ ScrollView: ({ children }: { children: ReactNode }) => {children}
,
+ Pressable: ({ children, disabled }: { children: ReactNode; disabled: boolean }) => (
+
+ ),
+ useWindowDimensions: () => ({ height: 800 }),
+}));
+vi.mock("@react-navigation/native", () => ({ useNavigation: () => ({}) }));
+vi.mock("react-native-safe-area-context", () => ({ useSafeAreaInsets: () => ({ bottom: 0 }) }));
+vi.mock("../../../components/ui/AndroidScreenHeader", () => ({ AndroidSheetHeader: () => null }));
+vi.mock("../../../components/ui/SheetHeader", () => ({ SheetHeader: () => null }));
+vi.mock("../../../components/ui/ScreenContent", () => ({
+ ScreenContent: ({ children }: { children: ReactNode }) => {children}
,
+}));
+vi.mock("../../../native/StackHeader", () => ({ NativeStackScreenOptions: () => null }));
+vi.mock("../../../components/ui/AppText", () => ({
+ AppText: ({ children }: { children: ReactNode }) => {children},
+ AppTextInput: () => null,
+}));
+vi.mock("./gitSheetComponents", () => ({ SheetActionButton: () => null }));
+vi.mock("../../../state/use-thread-selection", () => ({
+ useThreadSelection: () => ({
+ selectedThread: { environmentId: "environment-1", worktreePath: checkout.worktreePath },
+ selectedThreadProject: { workspaceRoot: checkout.projectRoot },
+ }),
+}));
+vi.mock("../../../state/use-thread-detail", () => ({
+ useSelectedThreadWorktreePath: () => checkout.worktreePath,
+}));
+vi.mock("../../../state/use-selected-thread-git-state", () => ({
+ useSelectedThreadGitState: () => ({
+ selectedThreadBranches: checkout.branches,
+ selectedThreadBranchesLoading: false,
+ gitOperationLabel: null,
+ }),
+}));
+vi.mock("../../../state/use-selected-thread-git-actions", () => ({
+ useSelectedThreadGitActions: () => ({ canChangeThreadBranch: true }),
+}));
+vi.mock("../../../state/query", () => ({ useEnvironmentQuery: () => ({ data: null }) }));
+vi.mock("../../../state/vcs", () => ({ vcsEnvironment: { status: () => null } }));
+
+import { GitBranchesSheet } from "./GitBranchesSheet";
+
+it("recognizes both the main checkout and a linked worktree as this thread's checkout", () => {
+ const directory = "/repo";
+ const linked = "/repo-linked";
+ checkout.projectRoot = directory;
+ checkout.branches = [
+ { name: "feature/available", worktreePath: null },
+ { name: "feature/linked", worktreePath: linked },
+ { name: "main", worktreePath: directory },
+ ];
+ (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
+ const container = document.createElement("div");
+ const root = createRoot(container);
+ const rows = () => {
+ act(() => {
+ root.render(
+ ,
+ );
+ });
+ return [...container.querySelectorAll("button")].map((button) => ({
+ name: button.querySelectorAll("span")[0]?.textContent,
+ subtitle: button.querySelectorAll("span")[1]?.textContent,
+ disabled: button.disabled,
+ }));
+ };
+ try {
+ checkout.worktreePath = null;
+ expect(rows()).toEqual([
+ { name: "feature/available", subtitle: "Local branch", disabled: false },
+ { name: "feature/linked", subtitle: "Checked out in another worktree", disabled: true },
+ { name: "main", subtitle: "Checked out in this thread", disabled: false },
+ ]);
+
+ checkout.worktreePath = linked;
+ expect(rows()).toEqual([
+ { name: "feature/available", subtitle: "Local branch", disabled: false },
+ { name: "feature/linked", subtitle: "Checked out in this thread", disabled: false },
+ { name: "main", subtitle: "Checked out in another worktree", disabled: true },
+ ]);
+ } finally {
+ act(() => root.unmount());
+ }
+});
diff --git a/apps/mobile/src/features/threads/git/GitBranchesSheet.tsx b/apps/mobile/src/features/threads/git/GitBranchesSheet.tsx
index 4075a49e9774a..dcaf96818bfe0 100644
--- a/apps/mobile/src/features/threads/git/GitBranchesSheet.tsx
+++ b/apps/mobile/src/features/threads/git/GitBranchesSheet.tsx
@@ -28,7 +28,7 @@ export function GitBranchesSheet(_props: GitBranchesSheetProps) {
const insets = useSafeAreaInsets();
const { height: windowHeight } = useWindowDimensions();
const { selectedThread } = useThreadSelection();
- const { selectedThreadCwd, selectedThreadWorktreePath } = useSelectedThreadWorktree();
+ const { selectedThreadCwd } = useSelectedThreadWorktree();
const gitState = useSelectedThreadGitState();
const gitActions = useSelectedThreadGitActions();
const { canChangeThreadBranch } = gitActions;
@@ -43,7 +43,7 @@ export function GitBranchesSheet(_props: GitBranchesSheetProps) {
);
const currentBranchLabel = gitStatus.data?.refName ?? selectedThread?.branch ?? "Detached HEAD";
- const currentWorktreePath = selectedThreadWorktreePath;
+ const currentWorktreePath = selectedThreadCwd;
const availableBranches = gitState.selectedThreadBranches;
const branchesLoading = gitState.selectedThreadBranchesLoading;
const busy = gitState.gitOperationLabel !== null;
diff --git a/apps/mobile/vite.config.ts b/apps/mobile/vite.config.ts
index e1509e98e1135..e7cd8f7a75afd 100644
--- a/apps/mobile/vite.config.ts
+++ b/apps/mobile/vite.config.ts
@@ -4,6 +4,7 @@ import { defineConfig, normalizePath } from "vite-plus";
import "vite-plus/test/config";
const reactTests = [
+ "src/features/threads/git/GitBranchesSheet.occupancy.test.tsx",
"src/features/files/useFileTreeEntries.test.tsx",
"src/features/projects/AddProjectScreen.test.ts",
"src/lib/useMediaVideoPlayback.test.tsx",