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:
@@ -115,6 +115,23 @@ export async function checkIsGitRepository(directory: string): Promise<boolean>
|
||||
}
|
||||
}
|
||||
|
||||
export async function listGitDirectories(root: string): Promise<string[]> {
|
||||
const response = await runtimeFetch('/api/fs/git-dirs', { query: { path: root } });
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to list git directories: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
if (!data || !Array.isArray(data.repositories)) {
|
||||
throw new Error('Unexpected git directories response');
|
||||
}
|
||||
return data.repositories
|
||||
.map((entry: unknown) => {
|
||||
const path = entry && typeof entry === 'object' && 'path' in entry ? (entry as { path?: unknown }).path : undefined;
|
||||
return typeof path === 'string' && path.trim() ? path.trim() : null;
|
||||
})
|
||||
.filter((path: string | null): path is string => path !== null);
|
||||
}
|
||||
|
||||
export async function getGitStatus(directory: string, options?: { mode?: 'light' }): Promise<GitStatus> {
|
||||
const mode = options?.mode;
|
||||
const runtimeKey = getRuntimeKey();
|
||||
|
||||
@@ -859,6 +859,10 @@ export const dict = {
|
||||
'gitView.empty.worktreeFeaturesUnavailable': 'Worktree-Funktionen sind in diesem Arbeitsbereichsmodus nicht verfügbar.',
|
||||
'gitView.empty.worktreeSetupDescription': 'Arbeitstruktur-Einrichtung wird abgeschlossen und Repository-Zustand wird vorbereitet.',
|
||||
'gitView.empty.worktreeSetupInProgress': 'Worktree-Einrichtung läuft',
|
||||
'gitView.empty.discoveringRepositories': 'Suche nach Git-Repositories...',
|
||||
'gitView.empty.discoverFailed': 'Git-Repositories konnten nicht durchsucht werden',
|
||||
'gitView.empty.retryDiscovery': 'Erneut versuchen',
|
||||
'gitView.empty.selectRepositoryPlaceholder': 'Repository auswählen...',
|
||||
'worktree.bootstrap.toast.failed': 'Worktree-Einrichtung fehlgeschlagen',
|
||||
'worktree.bootstrap.toast.failedDescription': 'Die Worktree wurde erstellt, aber die Hintergrund-Einrichtung wurde nicht abgeschlossen.',
|
||||
'worktree.bootstrap.toast.timeoutDescription': 'Die Worktree wurde erstellt, aber die Hintergrund-Einrichtung hat ein Timeout.',
|
||||
|
||||
@@ -923,6 +923,10 @@ export const dict = {
|
||||
'gitView.empty.worktreeFeaturesUnavailable': 'Worktree features are unavailable in this workspace mode.',
|
||||
'gitView.empty.worktreeSetupDescription': 'Finishing worktree setup and preparing repository state.',
|
||||
'gitView.empty.worktreeSetupInProgress': 'Worktree setup in progress',
|
||||
'gitView.empty.discoveringRepositories': 'Looking for Git repositories...',
|
||||
'gitView.empty.discoverFailed': 'Could not scan for Git repositories',
|
||||
'gitView.empty.retryDiscovery': 'Retry',
|
||||
'gitView.empty.selectRepositoryPlaceholder': 'Select a repository...',
|
||||
'worktree.bootstrap.toast.failed': 'Worktree setup failed',
|
||||
'worktree.bootstrap.toast.failedDescription': 'The worktree was created, but background setup did not finish.',
|
||||
'worktree.bootstrap.toast.timeoutDescription': 'The worktree was created, but background setup timed out.',
|
||||
|
||||
@@ -924,6 +924,10 @@ export const dict: Record<I18nKey, string> = {
|
||||
"gitView.empty.worktreeFeaturesUnavailable": "Las características de worktree no están disponibles en este modo de espacio de trabajo.",
|
||||
"gitView.empty.worktreeSetupDescription": "Finalizando la configuración de worktree y preparando el estado del repositorio.",
|
||||
"gitView.empty.worktreeSetupInProgress": "Configuración de worktree en progreso",
|
||||
"gitView.empty.discoveringRepositories": "Buscando repositorios de Git...",
|
||||
"gitView.empty.discoverFailed": "No se pudo escanear en busca de repositorios de Git",
|
||||
"gitView.empty.retryDiscovery": "Reintentar",
|
||||
"gitView.empty.selectRepositoryPlaceholder": "Selecciona un repositorio...",
|
||||
"worktree.bootstrap.toast.failed": "Error al configurar el worktree",
|
||||
"worktree.bootstrap.toast.failedDescription": "El worktree se creó, pero la configuración en segundo plano no terminó.",
|
||||
"worktree.bootstrap.toast.timeoutDescription": "El worktree se creó, pero la configuración en segundo plano agotó el tiempo de espera.",
|
||||
|
||||
@@ -751,6 +751,10 @@ export const dict = {
|
||||
'gitView.empty.worktreeFeaturesUnavailable': 'Les fonctionnalités Worktree ne sont pas disponibles dans ce mode d’espace de travail.',
|
||||
'gitView.empty.worktreeSetupDescription': 'Termine la configuration du worktree et prépare l\'état du dépôt.',
|
||||
'gitView.empty.worktreeSetupInProgress': 'Configuration de worktree en cours',
|
||||
'gitView.empty.discoveringRepositories': 'Recherche des dépôts Git...',
|
||||
'gitView.empty.discoverFailed': 'Impossible d’analyser les dépôts Git',
|
||||
'gitView.empty.retryDiscovery': 'Réessayer',
|
||||
'gitView.empty.selectRepositoryPlaceholder': 'Sélectionnez un dépôt...',
|
||||
'gitView.gitmoji.empty': 'Aucun gitmoji trouvé',
|
||||
'gitView.gitmoji.searchPlaceholder': 'Rechercher des gitmoji...',
|
||||
'gitView.gitmoji.title': 'Insérer un gitmoji',
|
||||
|
||||
@@ -920,6 +920,10 @@ export const dict: Record<I18nKey, string> = {
|
||||
'gitView.empty.worktreeFeaturesUnavailable': 'このワークスペースモードではワークツリー機能は利用できません。',
|
||||
'gitView.empty.worktreeSetupDescription': 'ワークツリーのセットアップを完了し、リポジトリ状態を準備中。',
|
||||
'gitView.empty.worktreeSetupInProgress': 'ワークツリーのセットアップ進行中',
|
||||
'gitView.empty.discoveringRepositories': 'Git リポジトリを検索しています...',
|
||||
'gitView.empty.discoverFailed': 'Git リポジトリを検索できませんでした',
|
||||
'gitView.empty.retryDiscovery': '再試行',
|
||||
'gitView.empty.selectRepositoryPlaceholder': 'リポジトリを選択...',
|
||||
'worktree.bootstrap.toast.failed': 'ワークツリーのセットアップに失敗しました',
|
||||
'worktree.bootstrap.toast.failedDescription': 'ワークツリーは作成されましたが、バックグラウンドセットアップが完了しませんでした。',
|
||||
'worktree.bootstrap.toast.timeoutDescription': 'ワークツリーは作成されましたが、バックグラウンドセットアップがタイムアウトしました。',
|
||||
|
||||
@@ -924,6 +924,10 @@ export const dict: Record<I18nKey, string> = {
|
||||
'gitView.empty.worktreeFeaturesUnavailable': '이 워크스페이스 모드에서는 워크트리 기능을 사용할 수 없습니다.',
|
||||
'gitView.empty.worktreeSetupDescription': '워크트리 설정을 마치고 레포지토리 상태를 준비하고 있습니다.',
|
||||
'gitView.empty.worktreeSetupInProgress': '워크트리 설정 중',
|
||||
'gitView.empty.discoveringRepositories': 'Git 저장소를 찾는 중...',
|
||||
'gitView.empty.discoverFailed': 'Git 저장소를 검색할 수 없습니다',
|
||||
'gitView.empty.retryDiscovery': '다시 시도',
|
||||
'gitView.empty.selectRepositoryPlaceholder': '저장소 선택...',
|
||||
'worktree.bootstrap.toast.failed': '워크트리 설정 실패',
|
||||
'worktree.bootstrap.toast.failedDescription': '워크트리는 생성되었지만 백그라운드 설정이 완료되지 않았습니다.',
|
||||
'worktree.bootstrap.toast.timeoutDescription': '워크트리는 생성되었지만 백그라운드 설정 시간이 초과되었습니다.',
|
||||
|
||||
@@ -1958,6 +1958,10 @@ export const dict: Record<I18nKey, string> = {
|
||||
'gitView.empty.worktreeFeaturesUnavailable': 'Worktree features are unavailable in this workspace mode.',
|
||||
'gitView.empty.worktreeSetupDescription': 'Finishing worktree setup and preparing repository state.',
|
||||
'gitView.empty.worktreeSetupInProgress': 'Worktree setup in progress',
|
||||
'gitView.empty.discoveringRepositories': 'Szukanie repozytoriów Git...',
|
||||
'gitView.empty.discoverFailed': 'Nie udało się przeskanować repozytoriów Git',
|
||||
'gitView.empty.retryDiscovery': 'Ponów',
|
||||
'gitView.empty.selectRepositoryPlaceholder': 'Wybierz repozytorium...',
|
||||
'worktree.bootstrap.toast.failed': 'Konfiguracja drzewa pracy nie powiodła się',
|
||||
'worktree.bootstrap.toast.failedDescription': 'Drzewo pracy zostało utworzone, ale konfiguracja w tle nie została ukończona.',
|
||||
'worktree.bootstrap.toast.timeoutDescription': 'Drzewo pracy zostało utworzone, ale konfiguracja w tle przekroczyła limit czasu.',
|
||||
|
||||
@@ -924,6 +924,10 @@ export const dict: Record<I18nKey, string> = {
|
||||
"gitView.empty.worktreeFeaturesUnavailable": "Os recursos de worktree não estão disponíveis neste modo de workspace.",
|
||||
"gitView.empty.worktreeSetupDescription": "Finalizando a configuração de worktree e preparando o status do repositório.",
|
||||
"gitView.empty.worktreeSetupInProgress": "Configuração de worktree em andamento",
|
||||
"gitView.empty.discoveringRepositories": "Procurando repositórios Git...",
|
||||
"gitView.empty.discoverFailed": "Não foi possível verificar os repositórios Git",
|
||||
"gitView.empty.retryDiscovery": "Tentar novamente",
|
||||
"gitView.empty.selectRepositoryPlaceholder": "Selecione um repositório...",
|
||||
"worktree.bootstrap.toast.failed": "Falha na configuração do worktree",
|
||||
"worktree.bootstrap.toast.failedDescription": "O worktree foi criado, mas a configuração em segundo plano não terminou.",
|
||||
"worktree.bootstrap.toast.timeoutDescription": "O worktree foi criado, mas a configuração em segundo plano atingiu o tempo limite.",
|
||||
|
||||
@@ -924,6 +924,10 @@ export const dict: Record<I18nKey, string> = {
|
||||
"gitView.empty.worktreeFeaturesUnavailable": "У цьому режимі робочої області функції worktree недоступні.",
|
||||
"gitView.empty.worktreeSetupDescription": "Завершення налаштування worktree та підготовка стану сховища.",
|
||||
"gitView.empty.worktreeSetupInProgress": "Виконується налаштування worktree",
|
||||
"gitView.empty.discoveringRepositories": "Пошук репозиторіїв Git...",
|
||||
"gitView.empty.discoverFailed": "Не вдалося просканувати репозиторії Git",
|
||||
"gitView.empty.retryDiscovery": "Повторити",
|
||||
"gitView.empty.selectRepositoryPlaceholder": "Виберіть репозиторій...",
|
||||
"worktree.bootstrap.toast.failed": "Не вдалося налаштувати worktree",
|
||||
"worktree.bootstrap.toast.failedDescription": "Worktree створено, але фонове налаштування не завершилося.",
|
||||
"worktree.bootstrap.toast.timeoutDescription": "Worktree створено, але час очікування фонового налаштування минув.",
|
||||
|
||||
@@ -924,6 +924,10 @@ export const dict: Record<I18nKey, string> = {
|
||||
'gitView.empty.worktreeFeaturesUnavailable': '当前工作区模式下,工作树功能不可用。',
|
||||
'gitView.empty.worktreeSetupDescription': '正在完成工作树设置并准备仓库状态。',
|
||||
'gitView.empty.worktreeSetupInProgress': '工作树设置进行中',
|
||||
'gitView.empty.discoveringRepositories': '正在查找 Git 仓库...',
|
||||
'gitView.empty.discoverFailed': '无法扫描 Git 仓库',
|
||||
'gitView.empty.retryDiscovery': '重试',
|
||||
'gitView.empty.selectRepositoryPlaceholder': '选择仓库...',
|
||||
'worktree.bootstrap.toast.failed': '工作树设置失败',
|
||||
'worktree.bootstrap.toast.failedDescription': '工作树已创建,但后台设置未完成。',
|
||||
'worktree.bootstrap.toast.timeoutDescription': '工作树已创建,但后台设置超时。',
|
||||
|
||||
@@ -936,6 +936,10 @@ export const dict: Record<I18nKey, string> = {
|
||||
'gitView.empty.worktreeFeaturesUnavailable': '目前工作區模式下,worktree 功能無法使用。',
|
||||
'gitView.empty.worktreeSetupDescription': '正在完成 worktree 設定並準備儲存庫狀態。',
|
||||
'gitView.empty.worktreeSetupInProgress': 'worktree 設定進行中',
|
||||
'gitView.empty.discoveringRepositories': '正在尋找 Git 儲存庫...',
|
||||
'gitView.empty.discoverFailed': '無法掃描 Git 儲存庫',
|
||||
'gitView.empty.retryDiscovery': '重試',
|
||||
'gitView.empty.selectRepositoryPlaceholder': '選擇儲存庫...',
|
||||
'worktree.bootstrap.toast.failed': 'worktree 設定失敗',
|
||||
'worktree.bootstrap.toast.failedDescription': 'worktree 已建立,但背景設定未完成。',
|
||||
'worktree.bootstrap.toast.timeoutDescription': 'worktree 已建立,但背景設定逾時。',
|
||||
|
||||
Reference in New Issue
Block a user