From e26b55e0677f552e2188f77a23863b21c12a21b9 Mon Sep 17 00:00:00 2001 From: jaygupta17 Date: Tue, 25 Aug 2026 19:55:00 +0530 Subject: [PATCH] fix(git): exit nested-repo states once resolution succeeds NestedRepoResolutionStates had no success exit: on a non-repo root rootIsGitRepo stays false forever, so once repositories were found the pull-request and walkthrough tabs kept showing 'Checking repository' even after the selected repository probed as a repository. GitView never hit this because its call site sits inside its own isGitRepo === false branch. The component now takes the operating directory's probe and returns null when it resolves true. DiffView also still keyed its not-a-repository gate and every diff fetch off the raw project root, so opening a change from a nested repository showed 'This directory is not a Git repository'. It now resolves the nested repository for git data and diff operations while session-scoped lookups (session messages, review-flow directory) stay on the root. --- packages/ui/src/apps/MobileChangesSurface.tsx | 1 + packages/ui/src/components/views/DiffView.tsx | 11 ++++++++--- packages/ui/src/components/views/GitView.tsx | 1 + .../ui/src/components/views/PullRequestView.tsx | 1 + .../views/git/NestedRepoResolutionStates.tsx | 13 +++++++++++-- .../views/walkthrough/WalkthroughView.tsx | 1 + 6 files changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/ui/src/apps/MobileChangesSurface.tsx b/packages/ui/src/apps/MobileChangesSurface.tsx index 9d12dd69..7fa06cbb 100644 --- a/packages/ui/src/apps/MobileChangesSurface.tsx +++ b/packages/ui/src/apps/MobileChangesSurface.tsx @@ -487,6 +487,7 @@ export const MobileChangesSurface: React.FC = ({ onCl return renderListState( { if (rootDirectory) void ensureNestedRepos(rootDirectory, { force: true }); diff --git a/packages/ui/src/components/views/DiffView.tsx b/packages/ui/src/components/views/DiffView.tsx index 28a15721..823adf72 100644 --- a/packages/ui/src/components/views/DiffView.tsx +++ b/packages/ui/src/components/views/DiffView.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { useUIStore } from '@/stores/useUIStore'; import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory'; +import { useNestedGitDirectory } from '@/hooks/useNestedGitDirectory'; import { useGitStore, useGitStatus, useIsGitRepo, useGitLoadingStatus } from '@/stores/useGitStore'; import { useGitBaseBranchStore, gitBaseBranchEntryKey } from '@/stores/useGitBaseBranchStore'; import { coerceDiffScope, branchRangeKey, isBranchScopeAvailable, isBranchScopeDefinitelyUnavailable, useRangeKeyedCache, useBoundedDirectoryRetry } from './branchDiffScope'; @@ -997,7 +998,11 @@ export const DiffView: React.FC = ({ }) => { const { t } = useI18n(); const { git, files } = useRuntimeAPIs(); - const effectiveDirectory = useEffectiveDirectory(); + const rootDirectory = useEffectiveDirectory(); + // Diffs belong to the repository being diffed: when the root is not + // itself a repository, operate on the resolved nested repository instead. + const { gitDirectory: nestedGitDirectory } = useNestedGitDirectory(rootDirectory ?? null); + const effectiveDirectory = nestedGitDirectory ?? rootDirectory; const openContextSurface = useUIStore((state) => state.openContextSurface); const requestWalkthroughSource = useWalkthroughStore((state) => state.requestSource); const { screenWidth, isMobile } = useDeviceInfo(); @@ -1038,7 +1043,7 @@ export const DiffView: React.FC = ({ const setDiffWrapLines = useUIStore((state) => state.setDiffWrapLines); const openContextFileAtLine = useUIStore((state) => state.openContextFileAtLine); const currentSessionId = useSessionUIStore((state) => state.currentSessionId); - const sessionMessages = useSessionMessages(currentSessionId ?? '', effectiveDirectory ?? undefined); + const sessionMessages = useSessionMessages(currentSessionId ?? '', rootDirectory ?? undefined); const diffWrapLines = diffWrapLinesStore; const forcedStaged = activeDiffScope === 'staged' ? true : activeDiffScope === 'working' ? false : null; const activeDiffStaged = forcedStaged ?? displayFileStaged; @@ -1645,7 +1650,7 @@ export const DiffView: React.FC = ({ const handleStartReviewFlow = React.useCallback(async (execution: ReviewFlowExecution) => { if (!currentSessionId) return; - const directory = useSessionUIStore.getState().getDirectoryForSession(currentSessionId) || effectiveDirectory || ''; + const directory = useSessionUIStore.getState().getDirectoryForSession(currentSessionId) || rootDirectory || ''; if (!directory) { toast.error(t('diffView.reviewDialog.toast.noSessionDirectory')); return; diff --git a/packages/ui/src/components/views/GitView.tsx b/packages/ui/src/components/views/GitView.tsx index 5317c041..1d599aef 100644 --- a/packages/ui/src/components/views/GitView.tsx +++ b/packages/ui/src/components/views/GitView.tsx @@ -2343,6 +2343,7 @@ export const GitView: React.FC = ({ isActive }) => { return ( { if (currentDirectory) { diff --git a/packages/ui/src/components/views/PullRequestView.tsx b/packages/ui/src/components/views/PullRequestView.tsx index a597b11e..43d6ccbb 100644 --- a/packages/ui/src/components/views/PullRequestView.tsx +++ b/packages/ui/src/components/views/PullRequestView.tsx @@ -265,6 +265,7 @@ export const PullRequestView: React.FC = () => { return ( { void ensureNestedRepos(currentDirectory, { force: true }); diff --git a/packages/ui/src/components/views/git/NestedRepoResolutionStates.tsx b/packages/ui/src/components/views/git/NestedRepoResolutionStates.tsx index 1798c34e..3e9e1e6d 100644 --- a/packages/ui/src/components/views/git/NestedRepoResolutionStates.tsx +++ b/packages/ui/src/components/views/git/NestedRepoResolutionStates.tsx @@ -8,6 +8,12 @@ import type { NestedRepoDiscovery } from '@/stores/useGitStore'; type NestedRepoResolutionStatesProps = { /** Probe of the project root: `false` means nested resolution applies. */ rootIsGitRepo: boolean | null; + /** + * Probe of the directory the consumer operates on (root or selected nested + * repository). `true` means resolution succeeded and the consumer should + * render its own content. + */ + resolvedIsGitRepo: boolean | null; /** Discovery outcome for the root (`undefined` = not run yet). */ nestedRepos: NestedRepoDiscovery | undefined; onRetryDiscovery: () => void; @@ -17,8 +23,9 @@ type NestedRepoResolutionStatesProps = { /** * Shared empty/loading states for git surfaces while nested-repository - * resolution is pending, failed, or impossible. Renders null once - * repositories are resolved so the consumer can proceed into its own content. + * resolution is pending, failed, or impossible. Renders null once resolution + * has finished — either the root is a repository or the operating directory + * probed as one — so the consumer can proceed into its own content. * * A runtime without the discovery route (VS Code) reports "unsupported": the * honest state there is the plain not-a-repository empty state, without a @@ -26,6 +33,7 @@ type NestedRepoResolutionStatesProps = { */ export const NestedRepoResolutionStates: React.FC = ({ rootIsGitRepo, + resolvedIsGitRepo, nestedRepos, onRetryDiscovery, emptyStateFooter, @@ -33,6 +41,7 @@ export const NestedRepoResolutionStates: React.FC { if (rootDirectory) void ensureNestedRepos(rootDirectory, { force: true });