feat(git): support nested git repositories in the Git tab
When the project root is not itself a git repository, discover nested repositories (depth- and visit-capped readdir walk via a new /api/fs/git-dirs route), auto-select the first one, and show a repository picker next to the branch dropdown to switch. Selections persist per runtime and root; discovery failure is a distinct marker with a retry action, never an empty success.
This commit is contained in:
@@ -12,6 +12,12 @@ import type { IconName } from "@/components/icon/icons";
|
||||
import { BranchSelector } from './BranchSelector';
|
||||
import { WorktreeBranchDisplay } from './WorktreeBranchDisplay';
|
||||
import { SyncActions } from './SyncActions';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from '@/components/ui/select';
|
||||
import type {
|
||||
GitStatus,
|
||||
GitIdentityProfile,
|
||||
@@ -51,6 +57,13 @@ interface GitHeaderProps {
|
||||
pullRequest?: GitHubPullRequest | null;
|
||||
prChecks?: GitHubChecksSummary | null;
|
||||
onOpenPullRequest?: () => void;
|
||||
// Nested repository picker: shown when the Git tab operates on a repository
|
||||
// nested inside a non-repository root. Options are absolute repository
|
||||
// paths; `repositoryRoot` is the root those paths are relative to.
|
||||
repositoryOptions?: string[];
|
||||
selectedRepository?: string | null;
|
||||
onSelectRepository?: (repository: string) => void;
|
||||
repositoryRoot?: string;
|
||||
}
|
||||
|
||||
const IDENTITY_ICON_MAP: Record<string, IconName> = {
|
||||
@@ -258,12 +271,23 @@ export const GitHeader: React.FC<GitHeaderProps> = ({
|
||||
pullRequest,
|
||||
prChecks,
|
||||
onOpenPullRequest,
|
||||
repositoryOptions,
|
||||
selectedRepository,
|
||||
onSelectRepository,
|
||||
repositoryRoot,
|
||||
}) => {
|
||||
const { t } = useI18n();
|
||||
if (!status) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const repositoryOptionsForPicker = (repositoryOptions ?? []).filter(Boolean);
|
||||
const repositoryRelativePath = (repository: string): string => {
|
||||
const rootPrefix = `${repositoryRoot ?? ''}/`;
|
||||
return repository.startsWith(rootPrefix) ? repository.slice(rootPrefix.length) : repository;
|
||||
};
|
||||
const repositoryLabel = selectedRepository ? repositoryRelativePath(selectedRepository) : '';
|
||||
|
||||
const managementButtons = (
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
{onOpenHistory || onOpenGraph || onOpenStashes || onOpenUpdateBranch ? (
|
||||
@@ -410,7 +434,7 @@ export const GitHeader: React.FC<GitHeaderProps> = ({
|
||||
return (
|
||||
<header className="@container/git-header px-3 py-2 bg-transparent">
|
||||
<div className="flex items-center justify-between gap-2 min-w-0">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-1">
|
||||
{isWorktreeMode ? (
|
||||
<WorktreeBranchDisplay
|
||||
currentBranch={status.current}
|
||||
@@ -427,6 +451,34 @@ export const GitHeader: React.FC<GitHeaderProps> = ({
|
||||
remotes={remotes}
|
||||
/>
|
||||
)}
|
||||
{repositoryOptionsForPicker.length > 0 ? (
|
||||
<Select
|
||||
value={selectedRepository ?? undefined}
|
||||
onValueChange={(value) => {
|
||||
if (value && onSelectRepository) {
|
||||
onSelectRepository(value);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SelectTrigger
|
||||
size="sm"
|
||||
className="max-w-[13rem] gap-1.5 px-2 py-1"
|
||||
aria-label={t('gitView.empty.selectRepositoryPlaceholder')}
|
||||
>
|
||||
<Icon name="folder-3" className="size-4 text-muted-foreground" />
|
||||
<span className="min-w-0 truncate font-medium text-left">
|
||||
{repositoryLabel}
|
||||
</span>
|
||||
</SelectTrigger>
|
||||
<SelectContent align="start">
|
||||
{repositoryOptionsForPicker.map((repository) => (
|
||||
<SelectItem key={repository} value={repository}>
|
||||
<span className="truncate">{repositoryRelativePath(repository)}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
{identityControl}
|
||||
|
||||
Reference in New Issue
Block a user