From 9e53dfef3378412036fe886b34fc06e5d4cafc1d Mon Sep 17 00:00:00 2001 From: Steven Wexler Date: Thu, 5 Jun 2025 09:56:07 -0400 Subject: [PATCH] Support staged vs. unstaged changs. Increase diff size to 8000. --- .../void/electron-main/voidSCMMainService.ts | 117 +++++------------- 1 file changed, 34 insertions(+), 83 deletions(-) diff --git a/src/vs/workbench/contrib/void/electron-main/voidSCMMainService.ts b/src/vs/workbench/contrib/void/electron-main/voidSCMMainService.ts index 56f0a26e..c32fcb08 100644 --- a/src/vs/workbench/contrib/void/electron-main/voidSCMMainService.ts +++ b/src/vs/workbench/contrib/void/electron-main/voidSCMMainService.ts @@ -1,7 +1,7 @@ import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js' -import { IVoidSCMService } from '../common/voidSCMTypes.js' import { promisify } from 'util' import { exec as _exec } from 'child_process' +import { IVoidSCMService } from '../common/voidSCMTypes.js' interface NumStat { file: string @@ -11,6 +11,10 @@ interface NumStat { const exec = promisify(_exec) +//8000 and 10 were chosen after some experimentation on small-to-moderately sized changes +const MAX_DIFF_LENGTH = 8000 +const MAX_DIFF_FILES = 10 + const git = async (command: string, path: string): Promise => { const { stdout, stderr } = await exec(`${command}`, { cwd: path }) if (stderr) { @@ -19,101 +23,48 @@ const git = async (command: string, path: string): Promise => { return stdout.trim() } -const getNumStat = async (path: string): Promise => { - // Get both staged and unstaged changes - const [stagedOutput, unstagedOutput] = await Promise.all([ - git('git diff --cached --numstat', path).catch(() => ''), // staged changes - git('git diff --numstat', path).catch(() => '') // unstaged changes - ]) - - const parseOutput = (output: string) => { - if (!output.trim()) return [] - return output - .split('\n') - .filter(line => line.trim()) - .map((line) => { - const [added, removed, file] = line.split('\t') - return { - file, - added: parseInt(added, 10) || 0, - removed: parseInt(removed, 10) || 0, - } - }) - } - - const stagedStats = parseOutput(stagedOutput) - const unstagedStats = parseOutput(unstagedOutput) - - // Combine and deduplicate by file, summing the changes - const fileMap = new Map() - - for (const stat of [...stagedStats, ...unstagedStats]) { - const existing = fileMap.get(stat.file) - if (existing) { - existing.added += stat.added - existing.removed += stat.removed - } else { - fileMap.set(stat.file, { ...stat }) - } - } - - return Array.from(fileMap.values()) +const getNumStat = async (path: string, useStagedChanges: boolean): Promise => { + const staged = useStagedChanges ? '--staged' : '' + const output = await git(`git diff --numstat ${staged}`, path) + return output + .split('\n') + .map((line) => { + const [added, removed, file] = line.split('\t') + return { + file, + added: parseInt(added, 10) || 0, + removed: parseInt(removed, 10) || 0, + } + }) } -const getSampledDiff = async (file: string, path: string): Promise => { - // Get both staged and unstaged diffs - const [stagedDiff, unstagedDiff] = await Promise.all([ - git(`git diff --cached --unified=0 --no-color -- "${file}"`, path).catch(() => ''), // staged changes - git(`git diff --unified=0 --no-color -- "${file}"`, path).catch(() => '') // unstaged changes - ]) - - let combinedDiff = '' - if (stagedDiff.trim()) { - combinedDiff += `=== STAGED CHANGES ===\n${stagedDiff}\n\n` - } - if (unstagedDiff.trim()) { - combinedDiff += `=== UNSTAGED CHANGES ===\n${unstagedDiff}\n\n` - } - - return combinedDiff.slice(0, 2000) +const getSampledDiff = async (file: string, path: string, useStagedChanges: boolean): Promise => { + const staged = useStagedChanges ? '--staged' : '' + const diff = await git(`git diff --unified=0 --no-color ${staged} -- "${file}"`, path) + return diff.slice(0, MAX_DIFF_LENGTH) +} + +const hasStagedChanges = async (path: string): Promise => { + const output = await git('git diff --staged --name-only', path) + return output.length > 0 } export class VoidSCMService implements IVoidSCMService { readonly _serviceBrand: undefined async gitStat(path: string): Promise { - // Get both staged and unstaged stats - const [stagedStat, unstagedStat] = await Promise.all([ - git('git diff --cached --stat', path).catch(() => ''), // staged changes - git('git diff --stat', path).catch(() => '') // unstaged changes - ]) - - let combinedStat = '' - if (stagedStat.trim()) { - combinedStat += `Staged changes:\n${stagedStat}\n\n` - } - if (unstagedStat.trim()) { - combinedStat += `Unstaged changes:\n${unstagedStat}\n\n` - } - - // If neither staged nor unstaged changes, check if there are any changes at all - if (!combinedStat.trim()) { - // This will show changes between HEAD and working directory (includes staged changes) - const allChanges = await git('git diff HEAD --stat', path).catch(() => '') - if (allChanges.trim()) { - combinedStat = `All changes:\n${allChanges}` - } - } - - return combinedStat.trim() + const useStagedChanges = await hasStagedChanges(path) + const staged = useStagedChanges ? '--staged' : '' + return git(`git diff --stat ${staged}`, path) } async gitSampledDiffs(path: string): Promise { - const numStatList = await getNumStat(path) + const useStagedChanges = await hasStagedChanges(path) + const numStatList = await getNumStat(path, useStagedChanges) const topFiles = numStatList .sort((a, b) => (b.added + b.removed) - (a.added + a.removed)) - .slice(0, 10) - const diffs = await Promise.all(topFiles.map(async ({ file }) => ({ file, diff: await getSampledDiff(file, path) }))) + .slice(0, MAX_DIFF_FILES) + const diffs = await Promise.all(topFiles.map(async ({ file }) => ({ file, diff: await getSampledDiff(file, path, useStagedChanges) }))) return diffs.map(({ file, diff }) => `==== ${file} ====\n${diff}`).join('\n\n') }