From 7b66bca2c509c855e654dc6f8fcee9ec6b9e7b41 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Tue, 5 May 2026 23:53:21 +0300 Subject: [PATCH] fix: clarify sync button and result toasts Show sync label when no remote changes are known Report pulled file counts in sync success toasts Count pulled files correctly in VS Code sync --- packages/ui/src/components/views/GitView.tsx | 24 +++++++++++++++++-- .../src/components/views/git/SyncActions.tsx | 11 ++++++--- packages/ui/src/lib/i18n/messages/en.ts | 3 +++ packages/ui/src/lib/i18n/messages/es.ts | 3 +++ packages/ui/src/lib/i18n/messages/ko.ts | 3 +++ packages/ui/src/lib/i18n/messages/pt-BR.ts | 3 +++ packages/ui/src/lib/i18n/messages/uk.ts | 3 +++ packages/ui/src/lib/i18n/messages/zh-CN.ts | 3 +++ packages/vscode/src/gitService.ts | 16 ++++++++++--- 9 files changed, 61 insertions(+), 8 deletions(-) diff --git a/packages/ui/src/components/views/GitView.tsx b/packages/ui/src/components/views/GitView.tsx index 6b90930a..a5dcfc66 100644 --- a/packages/ui/src/components/views/GitView.tsx +++ b/packages/ui/src/components/views/GitView.tsx @@ -948,6 +948,8 @@ export const GitView: React.FC = () => { if (!remote) { throw new Error('No remote available for sync'); } + let pulledFileCount = 0; + let pushedChanges = false; await git.gitFetch(currentDirectory, { remote: remote.name }); const afterFetch = await git.getGitStatus(currentDirectory); @@ -956,14 +958,32 @@ export const GitView: React.FC = () => { toast.error(t('gitView.toast.commitOrStashBeforeSync')); return; } - await git.gitPull(currentDirectory, getPullOptions(remote)); + const pullResult = await git.gitPull(currentDirectory, getPullOptions(remote)); + pulledFileCount = pullResult.files.length; } const afterPull = await git.getGitStatus(currentDirectory); if ((afterPull.ahead ?? 0) > 0) { await git.gitPush(currentDirectory); + pushedChanges = true; + } + if (pulledFileCount > 0 && pushedChanges) { + toast.success( + pulledFileCount === 1 + ? t('gitView.toast.syncedPulledSingleAndPushed', { count: pulledFileCount, name: remote.name }) + : t('gitView.toast.syncedPulledPluralAndPushed', { count: pulledFileCount, name: remote.name }) + ); + } else if (pulledFileCount > 0) { + toast.success( + pulledFileCount === 1 + ? t('gitView.toast.pulledFilesSingle', { count: pulledFileCount, name: remote.name }) + : t('gitView.toast.pulledFilesPlural', { count: pulledFileCount, name: remote.name }) + ); + } else if (pushedChanges) { + toast.success(t('gitView.toast.pushedToUpstream')); + } else { + toast.success(t('gitView.toast.syncedChanges')); } - toast.success(t('gitView.toast.syncedChanges')); } await refreshStatusAndBranches(false); diff --git a/packages/ui/src/components/views/git/SyncActions.tsx b/packages/ui/src/components/views/git/SyncActions.tsx index f32de47a..790bef9d 100644 --- a/packages/ui/src/components/views/git/SyncActions.tsx +++ b/packages/ui/src/components/views/git/SyncActions.tsx @@ -54,11 +54,16 @@ export const SyncActions: React.FC = ({ const blocksRebaseSync = behindCount > 0 && hasUncommittedChanges; const isPrimaryDisabled = disabled || syncAction !== null || isRemovingRemote || !trackingRemote || blocksRebaseSync; const isDropdownDisabled = disabled || syncAction !== null || isRemovingRemote || remotes.length === 0; - const countsLabel = t('gitView.sync.syncCounts', { ahead: aheadCount, behind: behindCount }); + const hasKnownSyncWork = aheadCount > 0 || behindCount > 0; + const primaryLabel = hasKnownSyncWork + ? t('gitView.sync.syncCounts', { ahead: aheadCount, behind: behindCount }) + : t('gitView.sync.sync'); const tooltipLabel = blocksRebaseSync ? t('gitView.sync.commitOrStashTooltip') : trackingRemote - ? t('gitView.sync.syncChangesTooltip', { ahead: aheadCount, behind: behindCount }) + ? hasKnownSyncWork + ? t('gitView.sync.syncChangesTooltip', { ahead: aheadCount, behind: behindCount }) + : t('gitView.sync.syncChanges') : t('gitView.sync.noRemoteTooltip'); const handleSync = () => { @@ -87,7 +92,7 @@ export const SyncActions: React.FC = ({ ) : ( )} - {countsLabel} + {primaryLabel} {tooltipLabel} diff --git a/packages/ui/src/lib/i18n/messages/en.ts b/packages/ui/src/lib/i18n/messages/en.ts index cfc621df..6ff7c0ec 100644 --- a/packages/ui/src/lib/i18n/messages/en.ts +++ b/packages/ui/src/lib/i18n/messages/en.ts @@ -557,6 +557,7 @@ export const dict = { 'gitView.sync.push': 'Push', 'gitView.sync.pushTooltip': 'Push changes', 'gitView.sync.pushTooltipAhead': 'Push changes ({count} ahead)', + 'gitView.sync.sync': 'sync', 'gitView.sync.syncChanges': 'Sync Changes', 'gitView.sync.syncChangesTooltip': 'Sync Changes ({behind} down, {ahead} up)', 'gitView.sync.syncChangesWithCounts': 'Sync Changes {behind}↓ {ahead}↑', @@ -705,6 +706,8 @@ export const dict = { 'gitView.toast.pulledFilesSingle': 'Pulled {count} file from {name}', 'gitView.toast.pushedToUpstream': 'Pushed to upstream', 'gitView.toast.commitOrStashBeforeSync': 'Commit or stash your changes before syncing', + 'gitView.toast.syncedPulledPluralAndPushed': 'Pulled {count} files from {name} and pushed to upstream', + 'gitView.toast.syncedPulledSingleAndPushed': 'Pulled {count} file from {name} and pushed to upstream', 'gitView.toast.syncedChanges': 'Synced changes', 'gitView.toast.rebaseAborted': 'Rebase aborted', 'gitView.toast.rebaseConflictsDetected': 'Rebase conflicts detected', diff --git a/packages/ui/src/lib/i18n/messages/es.ts b/packages/ui/src/lib/i18n/messages/es.ts index 294d3a4b..147de2cd 100644 --- a/packages/ui/src/lib/i18n/messages/es.ts +++ b/packages/ui/src/lib/i18n/messages/es.ts @@ -558,6 +558,7 @@ export const dict: Record = { "gitView.sync.push": "Push", "gitView.sync.pushTooltip": "Hacer push", "gitView.sync.pushTooltipAhead": "Hacer push ({count} por delante)", + "gitView.sync.sync": "sync", "gitView.sync.syncChanges": "Sync Changes", "gitView.sync.syncChangesTooltip": "Sync Changes ({behind} abajo, {ahead} arriba)", "gitView.sync.syncChangesWithCounts": "Sync Changes {behind}↓ {ahead}↑", @@ -706,6 +707,8 @@ export const dict: Record = { "gitView.toast.pulledFilesSingle": "Se trajo {count} archivo de {name}", "gitView.toast.pushedToUpstream": "Enviado al upstream", "gitView.toast.commitOrStashBeforeSync": "Haz commit o stash de tus cambios antes de sincronizar", + "gitView.toast.syncedPulledPluralAndPushed": "Se trajeron {count} archivos de {name} y se envió al upstream", + "gitView.toast.syncedPulledSingleAndPushed": "Se trajo {count} archivo de {name} y se envió al upstream", "gitView.toast.syncedChanges": "Cambios sincronizados", "gitView.toast.rebaseAborted": "Rebase abortado", "gitView.toast.rebaseConflictsDetected": "Se detectaron conflictos al hacer rebase", diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts index 1d1bd0d3..5c3a087a 100644 --- a/packages/ui/src/lib/i18n/messages/ko.ts +++ b/packages/ui/src/lib/i18n/messages/ko.ts @@ -558,6 +558,7 @@ export const dict: Record = { 'gitView.sync.push': '푸시', 'gitView.sync.pushTooltip': '변경 사항 푸시', 'gitView.sync.pushTooltipAhead': '변경 사항 푸시({count}개 앞섬)', + 'gitView.sync.sync': 'sync', 'gitView.sync.syncChanges': 'Sync Changes', 'gitView.sync.syncChangesTooltip': 'Sync Changes({behind}개 내려받기, {ahead}개 올리기)', 'gitView.sync.syncChangesWithCounts': 'Sync Changes {behind}↓ {ahead}↑', @@ -706,6 +707,8 @@ export const dict: Record = { 'gitView.toast.pulledFilesSingle': '{name}에서 파일 {count}개를 풀했습니다', 'gitView.toast.pushedToUpstream': '업스트림에 푸시했습니다', 'gitView.toast.commitOrStashBeforeSync': '동기화하기 전에 변경 사항을 커밋하거나 stash하세요', + 'gitView.toast.syncedPulledPluralAndPushed': '{name}에서 파일 {count}개를 풀하고 업스트림에 푸시했습니다', + 'gitView.toast.syncedPulledSingleAndPushed': '{name}에서 파일 {count}개를 풀하고 업스트림에 푸시했습니다', 'gitView.toast.syncedChanges': '변경 사항을 동기화했습니다', 'gitView.toast.rebaseAborted': 'rebase 중단됨', 'gitView.toast.rebaseConflictsDetected': '리베이스 충돌이 감지되었습니다', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts index c7b717cc..283fcb07 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts @@ -558,6 +558,7 @@ export const dict: Record = { "gitView.sync.push": "Push", "gitView.sync.pushTooltip": "Fazer push", "gitView.sync.pushTooltipAhead": "Fazer push ({count} por delante)", + "gitView.sync.sync": "sync", "gitView.sync.syncChanges": "Sync Changes", "gitView.sync.syncChangesTooltip": "Sync Changes ({behind} abaixo, {ahead} acima)", "gitView.sync.syncChangesWithCounts": "Sync Changes {behind}↓ {ahead}↑", @@ -706,6 +707,8 @@ export const dict: Record = { "gitView.toast.pulledFilesSingle": "Se trajo {count} arquivo de {name}", "gitView.toast.pushedToUpstream": "Enviado ao upstream", "gitView.toast.commitOrStashBeforeSync": "Faça commit ou stash das alterações antes de sincronizar", + "gitView.toast.syncedPulledPluralAndPushed": "Foram trazidos {count} arquivos de {name} e enviados ao upstream", + "gitView.toast.syncedPulledSingleAndPushed": "Foi trazido {count} arquivo de {name} e enviado ao upstream", "gitView.toast.syncedChanges": "Alterações sincronizadas", "gitView.toast.rebaseAborted": "Rebase abortado", "gitView.toast.rebaseConflictsDetected": "Se detectaron conflitos ao hacer rebase", diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts index 90456d70..3e1cc1b9 100644 --- a/packages/ui/src/lib/i18n/messages/uk.ts +++ b/packages/ui/src/lib/i18n/messages/uk.ts @@ -558,6 +558,7 @@ export const dict: Record = { "gitView.sync.push": "Push", "gitView.sync.pushTooltip": "Push змін", "gitView.sync.pushTooltipAhead": "Push змін ({count} попереду)", + "gitView.sync.sync": "sync", "gitView.sync.syncChanges": "Sync Changes", "gitView.sync.syncChangesTooltip": "Sync Changes ({behind} вниз, {ahead} вгору)", "gitView.sync.syncChangesWithCounts": "Sync Changes {behind}↓ {ahead}↑", @@ -706,6 +707,8 @@ export const dict: Record = { "gitView.toast.pulledFilesSingle": "Отримано файл: {count} з {name}", "gitView.toast.pushedToUpstream": "Надіслано в upstream", "gitView.toast.commitOrStashBeforeSync": "Закомітьте або сховайте зміни перед синхронізацією", + "gitView.toast.syncedPulledPluralAndPushed": "Отримано файлів: {count} з {name} і надіслано в upstream", + "gitView.toast.syncedPulledSingleAndPushed": "Отримано файл: {count} з {name} і надіслано в upstream", "gitView.toast.syncedChanges": "Зміни синхронізовано", "gitView.toast.rebaseAborted": "Перебазування перервано", "gitView.toast.rebaseConflictsDetected": "Виявлено конфлікти перебазування", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts index 4d7021cf..4dc0ac0e 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts @@ -558,6 +558,7 @@ export const dict: Record = { 'gitView.sync.push': '推送', 'gitView.sync.pushTooltip': '推送更改', 'gitView.sync.pushTooltipAhead': '推送更改(领先 {count})', + 'gitView.sync.sync': 'sync', 'gitView.sync.syncChanges': 'Sync Changes', 'gitView.sync.syncChangesTooltip': 'Sync Changes({behind} 下,{ahead} 上)', 'gitView.sync.syncChangesWithCounts': 'Sync Changes {behind}↓ {ahead}↑', @@ -706,6 +707,8 @@ export const dict: Record = { 'gitView.toast.pulledFilesSingle': '已从 {name} 拉取 {count} 个文件', 'gitView.toast.pushedToUpstream': '已推送到上游', 'gitView.toast.commitOrStashBeforeSync': '同步前请先提交或储藏你的更改', + 'gitView.toast.syncedPulledPluralAndPushed': '已从 {name} 拉取 {count} 个文件并推送到上游', + 'gitView.toast.syncedPulledSingleAndPushed': '已从 {name} 拉取 {count} 个文件并推送到上游', 'gitView.toast.syncedChanges': '已同步更改', 'gitView.toast.rebaseAborted': '变基已中止', 'gitView.toast.rebaseConflictsDetected': '检测到变基冲突', diff --git a/packages/vscode/src/gitService.ts b/packages/vscode/src/gitService.ts index 57db3610..ed23ad3a 100644 --- a/packages/vscode/src/gitService.ts +++ b/packages/vscode/src/gitService.ts @@ -2380,6 +2380,7 @@ export async function gitPull( } // Fallback to raw git + const beforeHead = await execGit(['rev-parse', 'HEAD'], directory); const args = ['pull']; if (options?.rebase === true) args.push('--rebase'); if (options?.remote) args.push(options.remote); @@ -2389,11 +2390,20 @@ export async function gitPull( if (result.exitCode !== 0) { throw new Error(result.stderr.trim() || result.stdout.trim() || 'Failed to pull from remote'); } - + const afterHead = await execGit(['rev-parse', 'HEAD'], directory); + const before = beforeHead.exitCode === 0 ? beforeHead.stdout.trim() : ''; + const after = afterHead.exitCode === 0 ? afterHead.stdout.trim() : ''; + const changedFiles = before && after && before !== after + ? await execGit(['diff', '--name-only', before, after], directory) + : { stdout: '', stderr: '', exitCode: 0 }; + const files = changedFiles.exitCode === 0 + ? changedFiles.stdout.split('\n').map((line) => line.trim()).filter(Boolean) + : []; + return { success: result.exitCode === 0, - summary: { changes: 0, insertions: 0, deletions: 0 }, - files: [], + summary: { changes: files.length, insertions: 0, deletions: 0 }, + files, insertions: 0, deletions: 0, };