Improve branch switch safety and recent branch status (#3302)

* feat(ui): block branch switches on dirty trees

* feat(ui): show unpushed commits in git branch selector

* feat(ui): show recent branches in git selector

* fix(ui): persist recent branch status

* feat(ui): add mobile branch picker

* fix(ui): guard mobile branch checkout

* fix(i18n): restore Turkish git empty state labels

* feat(ui): flag dirty draft directories on the branch selector

Replaces the draft dirty-directory banner with an indicator on the branch
selector: a warning icon plus a hover tooltip that opens by itself for five
seconds when the dirty state first appears, then stays hover-only. The copy
states the situation and the options (commit or worktree) without prescribing
either.

* feat(ui): optional push in the dirty branch switch dialog

Commit-and-switch gains an opt-in "Push after commit" checkbox. When the
push fails the commit stands but the switch is cancelled with an explicit
toast, so the user is never moved off a branch without knowing its push did
not happen. Without the checkbox the toast states the commit is local only.

* fix(i18n): align dirty-directory copy across locales

* fix(a11y): name the unpushed-commit badge in the branch picker

The badge showed a bare arrow and number with no accessible name or tooltip.
Both the desktop recents list and the mobile picker now carry a localized
"N commits not pushed" title and aria-label.

* fix(mobile): push before switching dirty branches

Honor the dirty-switch dialog's push option on the mobile Changes surface.
A failed push leaves the new commit on its source branch, refreshes state, and
cancels checkout. Mobile branch selection now also shows the existing dirty
switch notice.
This commit is contained in:
𝖎𝖚𝖑𝖎𝖎𝖆
2026-09-03 01:42:50 +03:00
committed by GitHub
parent 40e4b6f857
commit e885afbe89
35 changed files with 1105 additions and 28 deletions
+6
View File
@@ -147,6 +147,11 @@ export interface GitStatus {
attentionReason?: 'merge' | 'rebase' | 'cherry-pick' | 'revert' | 'bisect' | null;
}
export interface GitUnpushedBranchCounts {
/** Local commits not present in each branch's configured upstream. */
counts: Record<string, number>;
}
export interface GitDiffResponse {
diff: string;
}
@@ -505,6 +510,7 @@ export interface GitAPI {
revertGitHunk?(directory: string, filePath: string, patch: string): Promise<void>;
isLinkedWorktree(directory: string): Promise<boolean>;
getGitBranches(directory: string): Promise<GitBranch>;
getGitUnpushedBranchCounts(directory: string, branches: string[]): Promise<GitUnpushedBranchCounts>;
deleteGitBranch(directory: string, payload: GitDeleteBranchPayload): Promise<{ success: boolean }>;
deleteRemoteBranch(directory: string, payload: GitDeleteRemoteBranchPayload): Promise<{ success: boolean }>;
removeRemote(directory: string, payload: GitRemoveRemotePayload): Promise<{ success: boolean }>;
+6
View File
@@ -214,6 +214,12 @@ export async function getGitBranches(directory: string): Promise<import('./api/t
return gitHttp.getGitBranches(directory);
}
export async function getGitUnpushedBranchCounts(directory: string, branches: string[]): Promise<import('./api/types').GitUnpushedBranchCounts> {
const runtime = getRuntimeGit();
if (runtime) return runtime.getGitUnpushedBranchCounts(directory, branches);
return gitHttp.getGitUnpushedBranchCounts(directory, branches);
}
export async function deleteGitBranch(directory: string, payload: import('./api/types').GitDeleteBranchPayload): Promise<{ success: boolean }> {
const runtime = getRuntimeGit();
if (runtime) return runtimeStatusMutation(directory, runtime.deleteGitBranch(directory, payload));
+11
View File
@@ -7,6 +7,7 @@ import type {
GitFileDiffResponse,
GetGitFileDiffOptions,
GitBranch,
GitUnpushedBranchCounts,
GitDeleteBranchPayload,
GitDeleteRemoteBranchPayload,
GitRemoveRemotePayload,
@@ -493,6 +494,16 @@ export async function getGitBranches(directory: string): Promise<GitBranch> {
return response.json();
}
export async function getGitUnpushedBranchCounts(directory: string, branches: string[]): Promise<GitUnpushedBranchCounts> {
const response = await runtimeFetch(buildUrl(`${API_BASE}/branch-push-status`, directory), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ branches }),
});
if (!response.ok) throw new Error(`Failed to get branch push status: ${response.statusText}`);
return response.json();
}
export async function deleteGitBranch(directory: string, payload: GitDeleteBranchPayload): Promise<{ success: boolean }> {
if (!payload?.branch) {
throw new Error('branch is required to delete a branch');
+16
View File
@@ -699,6 +699,20 @@ export const dict = {
'gitView.commit.stageFilesHint': 'Stagen Sie Dateien, um Commit zu aktivieren.',
'gitView.commit.title': 'Commit',
'gitView.common.cancel': 'Abbrechen',
'gitView.branch.switchBlockedNotice': 'Nicht committete Änderungen — vor dem Wechsel folgt ein Commit-oder-Verwerfen-Schritt.',
'gitView.branch.unpushedSingle': '1 Commit nicht gepusht',
'gitView.branch.unpushedPlural': '{count} Commits nicht gepusht',
'gitView.branch.recentBranches': 'Kürzliche Branches',
'gitView.dirtySwitch.title': 'Nicht committete Änderungen',
'gitView.dirtySwitch.descriptionSingle': 'Der Wechsel zu {branch} ist angehalten, damit die geänderte Datei nicht verloren geht. Zuerst committen oder verwerfen.',
'gitView.dirtySwitch.descriptionPlural': 'Der Wechsel zu {branch} ist angehalten, damit die {count} geänderten Dateien nicht verloren gehen. Zuerst committen oder verwerfen.',
'gitView.dirtySwitch.commitAndSwitch': 'Committen und wechseln',
'gitView.dirtySwitch.committedNotPushed': 'Auf {branch} committet. Der Commit ist nur lokal — er wurde nicht gepusht.',
'gitView.dirtySwitch.pushAfterCommit': 'Nach dem Commit pushen',
'gitView.dirtySwitch.pushFailed': 'Committet, aber der Push ist fehlgeschlagen — der Branch wurde nicht gewechselt.',
'gitView.dirtySwitch.actionFailed': 'Die Aktion ist fehlgeschlagen; der Branch wurde nicht gewechselt.',
'gitView.dirtySwitch.revertAndSwitch': 'Verwerfen und wechseln',
'gitView.dirtySwitch.revertIncomplete': 'Einige Änderungen konnten nicht verworfen werden, der Branch wurde nicht gewechselt.',
'gitView.common.close': 'Schließen',
'gitView.common.done': 'Fertig',
'gitView.common.processing': 'Verarbeitung läuft...',
@@ -1429,6 +1443,8 @@ export const dict = {
'chat.autoReview.reviewSessionLabel': 'Überprüfungssitzung',
'chat.autoReview.actions.open': 'Öffnen',
'chat.autoReview.actions.stop': 'Stoppen',
'chat.draftDirtyNotice.tooltip': 'Dieser Branch hat nicht committete Dateien.\nDie neue Session sieht sie. Ein Commit oder ein Worktree hält sie getrennt.',
'chat.draftDirtyNotice.indicatorAria': 'Nicht committete Änderungen in diesem Verzeichnis',
'diffView.hunk.label': 'Stücke',
'diffView.hunk.stage': 'Zu Staging hinzufügen',
'diffView.hunk.unstage': 'Aus Staging entfernen',
+16
View File
@@ -795,6 +795,20 @@ export const dict = {
'gitView.commit.stageFilesHint': 'Stage files to enable commit.',
'gitView.commit.title': 'Commit',
'gitView.common.cancel': 'Cancel',
'gitView.branch.switchBlockedNotice': 'Uncommitted changes — switching opens a commit-or-revert step first.',
'gitView.branch.unpushedSingle': '1 commit not pushed',
'gitView.branch.unpushedPlural': '{count} commits not pushed',
'gitView.branch.recentBranches': 'Recent branches',
'gitView.dirtySwitch.title': 'Uncommitted changes',
'gitView.dirtySwitch.descriptionSingle': 'Switching to {branch} is paused so your changed file is not lost. Commit it, or revert it first.',
'gitView.dirtySwitch.descriptionPlural': 'Switching to {branch} is paused so your {count} changed files are not lost. Commit them, or revert them first.',
'gitView.dirtySwitch.commitAndSwitch': 'Commit and switch',
'gitView.dirtySwitch.committedNotPushed': 'Committed to {branch}. The commit is local only — it has not been pushed.',
'gitView.dirtySwitch.pushAfterCommit': 'Push after commit',
'gitView.dirtySwitch.pushFailed': 'Committed, but the push failed — the branch was not switched.',
'gitView.dirtySwitch.actionFailed': 'The action failed; the branch was not switched.',
'gitView.dirtySwitch.revertAndSwitch': 'Revert and switch',
'gitView.dirtySwitch.revertIncomplete': 'Some changes could not be reverted, so the branch was not switched.',
'gitView.common.close': 'Close',
'gitView.common.done': 'Done',
'gitView.common.processing': 'Processing...',
@@ -1626,6 +1640,8 @@ export const dict = {
'chat.autoReview.reviewSessionLabel': 'Review session',
'chat.autoReview.actions.open': 'Open',
'chat.autoReview.actions.stop': 'Stop',
'chat.draftDirtyNotice.tooltip': 'This branch has uncommitted files.\nThe new session will see them. A commit or a worktree keeps them separate.',
'chat.draftDirtyNotice.indicatorAria': 'Uncommitted changes in this directory',
'diffView.hunk.label': 'Hunks',
'diffView.hunk.stage': 'Stage',
'diffView.hunk.unstage': 'Unstage',
+16
View File
@@ -796,6 +796,20 @@ export const dict: Record<I18nKey, string> = {
"gitView.commit.stageFilesHint": "Prepara archivos para habilitar el commit.",
"gitView.commit.title": "Commit",
"gitView.common.cancel": "Cancelar",
'gitView.branch.switchBlockedNotice': 'Cambios sin confirmar: antes de cambiar de rama se ofrece confirmar o revertir.',
'gitView.branch.unpushedSingle': '1 commit sin push',
'gitView.branch.unpushedPlural': '{count} commits sin push',
'gitView.branch.recentBranches': 'Ramas recientes',
'gitView.dirtySwitch.title': 'Cambios sin confirmar',
'gitView.dirtySwitch.descriptionSingle': 'El cambio a {branch} está en pausa para no perder tu archivo modificado. Confírmalo o reviértelo primero.',
'gitView.dirtySwitch.descriptionPlural': 'El cambio a {branch} está en pausa para no perder tus {count} archivos modificados. Confírmalos o reviértelos primero.',
'gitView.dirtySwitch.commitAndSwitch': 'Confirmar y cambiar',
'gitView.dirtySwitch.committedNotPushed': 'Confirmado en {branch}. El commit es solo local: no se ha hecho push.',
'gitView.dirtySwitch.pushAfterCommit': 'Hacer push después del commit',
'gitView.dirtySwitch.pushFailed': 'Se confirmó, pero el push falló: no se cambió de rama.',
'gitView.dirtySwitch.actionFailed': 'La acción falló; no se cambió de rama.',
'gitView.dirtySwitch.revertAndSwitch': 'Revertir y cambiar',
'gitView.dirtySwitch.revertIncomplete': 'Algunos cambios no se pudieron revertir, así que no se cambió de rama.',
"gitView.common.close": "Cerrar",
"gitView.common.done": "Hecho",
"gitView.common.processing": "Procesando...",
@@ -1604,6 +1618,8 @@ export const dict: Record<I18nKey, string> = {
'chat.autoReview.reviewSessionLabel': 'Sesión de revisión',
'chat.autoReview.actions.open': 'Abrir',
'chat.autoReview.actions.stop': 'Detener',
'chat.draftDirtyNotice.tooltip': 'Esta rama tiene archivos sin confirmar.\nLa nueva sesión los verá. Un commit o un worktree los mantiene separados.',
'chat.draftDirtyNotice.indicatorAria': 'Cambios sin confirmar en este directorio',
"diffView.hunk.label": "Fragmentos",
"diffView.hunk.stage": "Preparar",
"diffView.hunk.unstage": "Quitar",
+16
View File
@@ -618,6 +618,20 @@ export const dict = {
'gitView.commit.stageFilesHint': 'Ajoutez des fichiers à lindex pour activer le commit.',
'gitView.commit.title': 'Commettre',
'gitView.common.cancel': 'Annuler',
'gitView.branch.switchBlockedNotice': 'Modifications non commitées — le changement passe dabord par un commit ou une annulation.',
'gitView.branch.unpushedSingle': '1 commit non poussé',
'gitView.branch.unpushedPlural': '{count} commits non poussés',
'gitView.branch.recentBranches': 'Branches récentes',
'gitView.dirtySwitch.title': 'Modifications non commitées',
'gitView.dirtySwitch.descriptionSingle': 'Le passage à {branch} est suspendu pour ne pas perdre votre fichier modifié. Commitez-le ou annulez-le dabord.',
'gitView.dirtySwitch.descriptionPlural': 'Le passage à {branch} est suspendu pour ne pas perdre vos {count} fichiers modifiés. Commitez-les ou annulez-les dabord.',
'gitView.dirtySwitch.commitAndSwitch': 'Commiter et changer',
'gitView.dirtySwitch.committedNotPushed': 'Commité sur {branch}. Le commit est local uniquement — il na pas été poussé.',
'gitView.dirtySwitch.pushAfterCommit': 'Pousser après le commit',
'gitView.dirtySwitch.pushFailed': 'Commité, mais le push a échoué — la branche na pas été changée.',
'gitView.dirtySwitch.actionFailed': 'Laction a échoué ; la branche na pas été changée.',
'gitView.dirtySwitch.revertAndSwitch': 'Annuler et changer',
'gitView.dirtySwitch.revertIncomplete': 'Certaines modifications nont pas pu être annulées, la branche na donc pas été changée.',
'gitView.common.close': 'Fermer',
'gitView.common.done': 'Fait',
'gitView.common.processing': 'Traitement...',
@@ -1390,6 +1404,8 @@ export const dict = {
'chat.autoReview.reviewSessionLabel': 'Session de revue',
'chat.autoReview.actions.open': 'Ouvrir',
'chat.autoReview.actions.stop': 'Arrêter',
'chat.draftDirtyNotice.tooltip': 'Cette branche a des fichiers non commités.\nLa nouvelle session les verra. Un commit ou un worktree les garde séparés.',
'chat.draftDirtyNotice.indicatorAria': 'Modifications non commitées dans ce répertoire',
'diffView.hunk.label': 'Sections',
'diffView.hunk.stage': 'Préparer',
'diffView.hunk.unstage': 'Retirer',
+16
View File
@@ -793,6 +793,20 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.stageFilesHint': 'ファイルをステージするとコミットが有効になります。',
'gitView.commit.title': 'コミット',
'gitView.common.cancel': 'キャンセル',
'gitView.branch.switchBlockedNotice': '未コミットの変更があります — 切り替え前にコミットまたは破棄の手順が入ります。',
'gitView.branch.unpushedSingle': '未プッシュのコミットが1件',
'gitView.branch.unpushedPlural': '未プッシュのコミットが{count}件',
'gitView.branch.recentBranches': '最近のブランチ',
'gitView.dirtySwitch.title': '未コミットの変更',
'gitView.dirtySwitch.descriptionSingle': '変更したファイルを失わないよう、{branch}への切り替えを一時停止しました。先にコミットするか破棄してください。',
'gitView.dirtySwitch.descriptionPlural': '変更した{count}件のファイルを失わないよう、{branch}への切り替えを一時停止しました。先にコミットするか破棄してください。',
'gitView.dirtySwitch.commitAndSwitch': 'コミットして切り替え',
'gitView.dirtySwitch.committedNotPushed': '{branch}にコミットしました。このコミットはローカルのみで、プッシュされていません。',
'gitView.dirtySwitch.pushAfterCommit': 'コミット後にプッシュ',
'gitView.dirtySwitch.pushFailed': 'コミットしましたが、プッシュに失敗したためブランチは切り替えませんでした。',
'gitView.dirtySwitch.actionFailed': '操作に失敗したため、ブランチは切り替えませんでした。',
'gitView.dirtySwitch.revertAndSwitch': '破棄して切り替え',
'gitView.dirtySwitch.revertIncomplete': '一部の変更を破棄できなかったため、ブランチは切り替えませんでした。',
'gitView.common.close': '閉じる',
'gitView.common.done': '完了',
'gitView.common.processing': '処理中...',
@@ -1631,6 +1645,8 @@ export const dict: Record<I18nKey, string> = {
'chat.autoReview.reviewSessionLabel': 'レビューセッション',
'chat.autoReview.actions.open': '開く',
'chat.autoReview.actions.stop': '停止',
'chat.draftDirtyNotice.tooltip': 'このブランチには未コミットのファイルがあります。\n新しいセッションからも見えます。コミットまたはワークツリーで分けられます。',
'chat.draftDirtyNotice.indicatorAria': 'このディレクトリに未コミットの変更があります',
'rightSidebar.contextNotesTodo.plan.defaultTitle': '計画',
'rightSidebar.contextNotesTodo.empty.selectProject': 'プロジェクトを選択してメモとTODOを追加します。',
'rightSidebar.contextNotesTodo.notes.placeholder': 'コンテキスト、リマインダー、リンクを記録',
+16
View File
@@ -796,6 +796,20 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.stageFilesHint': '커밋하려면 파일을 스테이징하세요.',
'gitView.commit.title': '커밋',
'gitView.common.cancel': '취소',
'gitView.branch.switchBlockedNotice': '커밋되지 않은 변경 사항이 있습니다 — 전환 전에 커밋 또는 되돌리기 단계가 먼저 열립니다.',
'gitView.branch.unpushedSingle': '푸시되지 않은 커밋 1개',
'gitView.branch.unpushedPlural': '푸시되지 않은 커밋 {count}개',
'gitView.branch.recentBranches': '최근 브랜치',
'gitView.dirtySwitch.title': '커밋되지 않은 변경 사항',
'gitView.dirtySwitch.descriptionSingle': '변경된 파일을 잃지 않도록 {branch}(으)로의 전환을 잠시 멈췄습니다. 먼저 커밋하거나 되돌리세요.',
'gitView.dirtySwitch.descriptionPlural': '변경된 파일 {count}개를 잃지 않도록 {branch}(으)로의 전환을 잠시 멈췄습니다. 먼저 커밋하거나 되돌리세요.',
'gitView.dirtySwitch.commitAndSwitch': '커밋하고 전환',
'gitView.dirtySwitch.committedNotPushed': '{branch}에 커밋했습니다. 이 커밋은 로컬 전용이며 푸시되지 않았습니다.',
'gitView.dirtySwitch.pushAfterCommit': '커밋 후 푸시',
'gitView.dirtySwitch.pushFailed': '커밋했지만 푸시에 실패하여 브랜치를 전환하지 않았습니다.',
'gitView.dirtySwitch.actionFailed': '작업이 실패하여 브랜치를 전환하지 않았습니다.',
'gitView.dirtySwitch.revertAndSwitch': '되돌리고 전환',
'gitView.dirtySwitch.revertIncomplete': '일부 변경 사항을 되돌리지 못해 브랜치를 전환하지 않았습니다.',
'gitView.common.close': '닫기',
'gitView.common.done': '완료',
'gitView.common.processing': '처리 중…',
@@ -1628,6 +1642,8 @@ export const dict: Record<I18nKey, string> = {
'chat.autoReview.reviewSessionLabel': '리뷰 세션',
'chat.autoReview.actions.open': '열기',
'chat.autoReview.actions.stop': '중지',
'chat.draftDirtyNotice.tooltip': '이 브랜치에는 커밋되지 않은 파일이 있습니다.\n새 세션에서도 보입니다. 커밋 또는 워크트리로 분리할 수 있습니다.',
'chat.draftDirtyNotice.indicatorAria': '이 디렉터리에 커밋되지 않은 변경 사항이 있습니다',
'diffView.hunk.label': '허크',
'diffView.hunk.stage': '스테이지',
'diffView.hunk.unstage': '스테이지 해제',
+16
View File
@@ -1844,6 +1844,8 @@ export const dict: Record<I18nKey, string> = {
'chat.autoReview.reviewSessionLabel': 'Sesja review',
'chat.autoReview.actions.open': 'Otwórz',
'chat.autoReview.actions.stop': 'Zatrzymaj',
'chat.draftDirtyNotice.tooltip': 'Ta gałąź ma niezacommitowane pliki.\nNowa sesja będzie je widzieć. Commit albo worktree trzyma je osobno.',
'chat.draftDirtyNotice.indicatorAria': 'Niezacommitowane zmiany w tym katalogu',
'diffView.hunk.label': 'Fragmenty',
'diffView.hunk.stage': 'Przygotuj',
'diffView.hunk.unstage': 'Cofnij',
@@ -2106,6 +2108,20 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.stageFilesHint': 'Dodaj pliki do indeksu, aby włączyć commit.',
'gitView.commit.title': 'Commit',
'gitView.common.cancel': 'Anuluj',
'gitView.branch.switchBlockedNotice': 'Niezacommitowane zmiany — przed przełączeniem pojawi się krok commit lub cofnięcie.',
'gitView.branch.unpushedSingle': '1 niewypchnięty commit',
'gitView.branch.unpushedPlural': 'Niewypchnięte commity: {count}',
'gitView.branch.recentBranches': 'Ostatnie gałęzie',
'gitView.dirtySwitch.title': 'Niezacommitowane zmiany',
'gitView.dirtySwitch.descriptionSingle': 'Przełączenie na {branch} wstrzymano, aby nie stracić zmienionego pliku. Najpierw go zacommituj lub cofnij.',
'gitView.dirtySwitch.descriptionPlural': 'Przełączenie na {branch} wstrzymano, aby nie stracić {count} zmienionych plików. Najpierw je zacommituj lub cofnij.',
'gitView.dirtySwitch.commitAndSwitch': 'Zacommituj i przełącz',
'gitView.dirtySwitch.committedNotPushed': 'Zacommitowano na {branch}. Commit jest tylko lokalny — nie został wypchnięty.',
'gitView.dirtySwitch.pushAfterCommit': 'Wypchnij po commicie',
'gitView.dirtySwitch.pushFailed': 'Zacommitowano, ale push się nie powiódł — gałąź nie została przełączona.',
'gitView.dirtySwitch.actionFailed': 'Akcja nie powiodła się; gałąź nie została przełączona.',
'gitView.dirtySwitch.revertAndSwitch': 'Cofnij i przełącz',
'gitView.dirtySwitch.revertIncomplete': 'Nie udało się cofnąć części zmian, więc gałąź nie została przełączona.',
'gitView.common.close': 'Zamknij',
'gitView.common.done': 'Gotowe',
'gitView.common.processing': 'Przetwarzanie...',
@@ -796,6 +796,20 @@ export const dict: Record<I18nKey, string> = {
"gitView.commit.stageFilesHint": "Adicione arquivos ao stage para habilitar o commit.",
"gitView.commit.title": "Commit",
"gitView.common.cancel": "Cancelar",
'gitView.branch.switchBlockedNotice': 'Alterações sem commit — antes de trocar, será oferecido commit ou reversão.',
'gitView.branch.unpushedSingle': '1 commit sem push',
'gitView.branch.unpushedPlural': '{count} commits sem push',
'gitView.branch.recentBranches': 'Branches recentes',
'gitView.dirtySwitch.title': 'Alterações sem commit',
'gitView.dirtySwitch.descriptionSingle': 'A troca para {branch} foi pausada para não perder seu arquivo alterado. Faça commit ou reverta primeiro.',
'gitView.dirtySwitch.descriptionPlural': 'A troca para {branch} foi pausada para não perder seus {count} arquivos alterados. Faça commit ou reverta primeiro.',
'gitView.dirtySwitch.commitAndSwitch': 'Fazer commit e trocar',
'gitView.dirtySwitch.committedNotPushed': 'Commit feito em {branch}. O commit é apenas local — não foi enviado com push.',
'gitView.dirtySwitch.pushAfterCommit': 'Fazer push após o commit',
'gitView.dirtySwitch.pushFailed': 'Commit feito, mas o push falhou — a branch não foi trocada.',
'gitView.dirtySwitch.actionFailed': 'A ação falhou; a branch não foi trocada.',
'gitView.dirtySwitch.revertAndSwitch': 'Reverter e trocar',
'gitView.dirtySwitch.revertIncomplete': 'Algumas alterações não puderam ser revertidas, então a branch não foi trocada.',
"gitView.common.close": "Fechar",
"gitView.common.done": "Concluído",
"gitView.common.processing": "Procesando...",
@@ -1604,6 +1618,8 @@ export const dict: Record<I18nKey, string> = {
'chat.autoReview.reviewSessionLabel': 'Sessão de revisão',
'chat.autoReview.actions.open': 'Abrir',
'chat.autoReview.actions.stop': 'Parar',
'chat.draftDirtyNotice.tooltip': 'Esta branch tem arquivos sem commit.\nA nova sessão os verá. Um commit ou um worktree os mantém separados.',
'chat.draftDirtyNotice.indicatorAria': 'Alterações sem commit neste diretório',
"diffView.hunk.label": "Trechos",
"diffView.hunk.stage": "Preparar",
"diffView.hunk.unstage": "Remover",
+20
View File
@@ -777,6 +777,20 @@ export const dict = {
'gitView.commit.stageFilesHint': 'Commit\'i etkinleştirmek için dosyaları stage edin.',
'gitView.commit.title': 'Commit',
'gitView.common.cancel': 'İptal',
'gitView.branch.switchBlockedNotice': 'Commit edilmemiş değişiklikler var — geçişten önce commit veya geri alma adımı açılır.',
'gitView.branch.unpushedSingle': '1 commit push edilmedi',
'gitView.branch.unpushedPlural': '{count} commit push edilmedi',
'gitView.branch.recentBranches': 'Son kullanılan dallar',
'gitView.dirtySwitch.title': 'Commit edilmemiş değişiklikler',
'gitView.dirtySwitch.descriptionSingle': 'Değiştirilen dosyanız kaybolmasın diye {branch} dalına geçiş duraklatıldı. Önce commit edin veya geri alın.',
'gitView.dirtySwitch.descriptionPlural': 'Değiştirilen {count} dosyanız kaybolmasın diye {branch} dalına geçiş duraklatıldı. Önce commit edin veya geri alın.',
'gitView.dirtySwitch.commitAndSwitch': 'Commit et ve geç',
'gitView.dirtySwitch.committedNotPushed': '{branch} dalına commit edildi. Commit yalnızca yerel — push edilmedi.',
'gitView.dirtySwitch.pushAfterCommit': 'Commit sonrası push et',
'gitView.dirtySwitch.pushFailed': 'Commit edildi ancak push başarısız oldu — dal değiştirilmedi.',
'gitView.dirtySwitch.actionFailed': 'İşlem başarısız oldu; dal değiştirilmedi.',
'gitView.dirtySwitch.revertAndSwitch': 'Geri al ve geç',
'gitView.dirtySwitch.revertIncomplete': 'Bazı değişiklikler geri alınamadığı için dal değiştirilmedi.',
'gitView.common.close': 'Kapat',
'gitView.common.done': 'Tamam',
'gitView.common.processing': 'İşleniyor...',
@@ -796,6 +810,8 @@ export const dict = {
'gitView.conflict.resolveNewSession': 'Yeni session\'da çöz',
'gitView.empty.cleanDescription': 'Tüm değişiklikler commit edildi',
'gitView.empty.cleanTitle': 'Working tree temiz',
'gitView.empty.discoveringRepositories': 'Git depoları aranıyor...',
'gitView.empty.discoverFailed': 'Git depoları taranamadı',
'gitView.empty.pullBehindPlural': '{count} commit pull et',
'gitView.empty.pullBehindSingle': '{count} commit pull et',
'gitView.header.identityTooltip': 'Git kimliği',
@@ -959,6 +975,8 @@ export const dict = {
'gitView.conflict.noDetailsAvailable': 'Çakışma detayları mevcut değil',
'gitView.empty.notGitRepository': 'Bu dizin bir Git repository\'si değil',
'gitView.empty.notGitRepositoryDescription': 'Bu dizinde Git\'i başlatın veya bir repository açın.',
'gitView.empty.retryDiscovery': 'Yeniden dene',
'gitView.empty.selectRepositoryPlaceholder': 'Bir repository seç...',
'gitView.empty.selectSessionOrDirectory': 'Git durumunu görüntülemek için bir session veya dizin seçin',
'gitView.empty.worktreeFeaturesUnavailable': 'Bu çalışma alanı modunda worktree özellikleri kullanılamıyor.',
'gitView.empty.worktreeSetupDescription': 'Worktree kurulumu tamamlanıyor ve repository durumu hazırlanıyor.',
@@ -1584,6 +1602,8 @@ export const dict = {
'chat.autoReview.reviewSessionLabel': 'İnceleme session\'ı',
'chat.autoReview.actions.open': 'Aç',
'chat.autoReview.actions.stop': 'Durdur',
'chat.draftDirtyNotice.tooltip': 'Bu dalda commit edilmemiş dosyalar var.\nYeni oturum onları görecek. Bir commit veya worktree onları ayrı tutar.',
'chat.draftDirtyNotice.indicatorAria': 'Bu dizinde commit edilmemiş değişiklikler var',
'diffView.hunk.label': 'Hunk\'lar',
'diffView.hunk.stage': 'Stage',
'diffView.hunk.unstage': 'Unstage',
+16
View File
@@ -796,6 +796,20 @@ export const dict: Record<I18nKey, string> = {
"gitView.commit.stageFilesHint": "Додайте файли до індексу, щоб увімкнути коміт.",
"gitView.commit.title": "Коміт",
"gitView.common.cancel": "Скасувати",
'gitView.branch.switchBlockedNotice': 'Є незакомічені зміни — перед перемиканням спершу буде крок «закомітити або скасувати».',
'gitView.branch.unpushedSingle': '1 незапушений коміт',
'gitView.branch.unpushedPlural': 'Незапушені коміти: {count}',
'gitView.branch.recentBranches': 'Нещодавні гілки',
'gitView.dirtySwitch.title': 'Незакомічені зміни',
'gitView.dirtySwitch.descriptionSingle': 'Перемикання на {branch} призупинено, щоб не втратити змінений файл. Спершу закоміть його або скасуй зміни.',
'gitView.dirtySwitch.descriptionPlural': 'Перемикання на {branch} призупинено, щоб не втратити {count} змінених файлів. Спершу закоміть їх або скасуй зміни.',
'gitView.dirtySwitch.commitAndSwitch': 'Закомітити й перемкнути',
'gitView.dirtySwitch.committedNotPushed': 'Закомічено в {branch}. Коміт лише локальний — його не запушено.',
'gitView.dirtySwitch.pushAfterCommit': 'Запушити після коміту',
'gitView.dirtySwitch.pushFailed': 'Закомічено, але push не вдався — гілку не перемкнено.',
'gitView.dirtySwitch.actionFailed': 'Дія не вдалася; гілку не перемкнено.',
'gitView.dirtySwitch.revertAndSwitch': 'Скасувати зміни й перемкнути',
'gitView.dirtySwitch.revertIncomplete': 'Частину змін не вдалося скасувати, тому гілку не перемкнено.',
"gitView.common.close": "Закрити",
"gitView.common.done": "Готово",
"gitView.common.processing": "Обробка...",
@@ -1604,6 +1618,8 @@ export const dict: Record<I18nKey, string> = {
'chat.autoReview.reviewSessionLabel': 'Сесія ревʼю',
'chat.autoReview.actions.open': 'Відкрити',
'chat.autoReview.actions.stop': 'Зупинити',
'chat.draftDirtyNotice.tooltip': 'У цій гілці є незакомічені файли.\nНова сесія бачитиме їх. Коміт або worktree тримають їх окремо.',
'chat.draftDirtyNotice.indicatorAria': 'Незакомічені зміни в цьому каталозі',
"diffView.hunk.label": "Шматки",
"diffView.hunk.stage": "Додати",
"diffView.hunk.unstage": "Прибрати",
@@ -796,6 +796,20 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.stageFilesHint': '暂存文件以启用提交。',
'gitView.commit.title': '提交',
'gitView.common.cancel': '取消',
'gitView.branch.switchBlockedNotice': '有未提交的更改 — 切换前会先进入提交或还原步骤。',
'gitView.branch.unpushedSingle': '1 个未推送的提交',
'gitView.branch.unpushedPlural': '{count} 个未推送的提交',
'gitView.branch.recentBranches': '最近分支',
'gitView.dirtySwitch.title': '未提交的更改',
'gitView.dirtySwitch.descriptionSingle': '为避免丢失已更改的文件,切换到 {branch} 已暂停。请先提交或还原。',
'gitView.dirtySwitch.descriptionPlural': '为避免丢失 {count} 个已更改的文件,切换到 {branch} 已暂停。请先提交或还原。',
'gitView.dirtySwitch.commitAndSwitch': '提交并切换',
'gitView.dirtySwitch.committedNotPushed': '已提交到 {branch}。该提交仅在本地,尚未推送。',
'gitView.dirtySwitch.pushAfterCommit': '提交后推送',
'gitView.dirtySwitch.pushFailed': '已提交,但推送失败 — 未切换分支。',
'gitView.dirtySwitch.actionFailed': '操作失败,未切换分支。',
'gitView.dirtySwitch.revertAndSwitch': '还原并切换',
'gitView.dirtySwitch.revertIncomplete': '部分更改无法还原,因此未切换分支。',
'gitView.common.close': '关闭',
'gitView.common.done': '完成',
'gitView.common.processing': '处理中...',
@@ -1592,6 +1606,8 @@ export const dict: Record<I18nKey, string> = {
'chat.autoReview.reviewSessionLabel': '审查会话',
'chat.autoReview.actions.open': '打开',
'chat.autoReview.actions.stop': '停止',
'chat.draftDirtyNotice.tooltip': '此分支有未提交的文件。\n新会话会看到它们。提交或工作树可将它们分开。',
'chat.draftDirtyNotice.indicatorAria': '此目录有未提交的更改',
'diffView.hunk.label': '代码块',
'diffView.hunk.stage': '暂存',
'diffView.hunk.unstage': '取消暂存',
@@ -809,6 +809,20 @@ export const dict: Record<I18nKey, string> = {
'gitView.commit.stageFilesHint': '暫存文件以啟用提交。',
'gitView.commit.title': '提交',
'gitView.common.cancel': '取消',
'gitView.branch.switchBlockedNotice': '有未提交的變更 — 切換前會先進入提交或還原步驟。',
'gitView.branch.unpushedSingle': '1 個未推送的提交',
'gitView.branch.unpushedPlural': '{count} 個未推送的提交',
'gitView.branch.recentBranches': '最近分支',
'gitView.dirtySwitch.title': '未提交的變更',
'gitView.dirtySwitch.descriptionSingle': '為避免遺失已變更的檔案,切換到 {branch} 已暫停。請先提交或還原。',
'gitView.dirtySwitch.descriptionPlural': '為避免遺失 {count} 個已變更的檔案,切換到 {branch} 已暫停。請先提交或還原。',
'gitView.dirtySwitch.commitAndSwitch': '提交並切換',
'gitView.dirtySwitch.committedNotPushed': '已提交到 {branch}。該提交僅在本地,尚未推送。',
'gitView.dirtySwitch.pushAfterCommit': '提交後推送',
'gitView.dirtySwitch.pushFailed': '已提交,但推送失敗 — 未切換分支。',
'gitView.dirtySwitch.actionFailed': '操作失敗,未切換分支。',
'gitView.dirtySwitch.revertAndSwitch': '還原並切換',
'gitView.dirtySwitch.revertIncomplete': '部分變更無法還原,因此未切換分支。',
'gitView.common.close': '關閉',
'gitView.common.done': '完成',
'gitView.common.processing': '處理中...',
@@ -1602,6 +1616,8 @@ export const dict: Record<I18nKey, string> = {
'chat.autoReview.reviewSessionLabel': '審查工作階段',
'chat.autoReview.actions.open': '開啟',
'chat.autoReview.actions.stop': '停止',
'chat.draftDirtyNotice.tooltip': '此分支有未提交的檔案。\n新的工作階段會看到它們。提交或工作樹可將它們分開。',
'chat.draftDirtyNotice.indicatorAria': '此目錄有未提交的變更',
'diffView.hunk.label': '程式碼區塊',
'diffView.hunk.stage': '暫存',
'diffView.hunk.unstage': '取消暫存',