Skip to content

scm: show added and removed line counts in the Source Control view - #329140

Open
thexin7 wants to merge 2 commits into
microsoft:mainfrom
thexin7:feature/scm-diff-line-stats
Open

scm: show added and removed line counts in the Source Control view#329140
thexin7 wants to merge 2 commits into
microsoft:mainfrom
thexin7:feature/scm-diff-line-stats

Conversation

@thexin7

@thexin7 thexin7 commented Aug 5, 2026

Copy link
Copy Markdown

Fixes #306507

Shows how many lines each change adds and removes in the Source Control view — next to every resource, and aggregated on the resource group header.

Source Control view with line counts

Full window

Full window

How it works

Layer Change
API Proposed SourceControlResourceState.diffStatistics (scmDiffStatistics)
Git extension One git diff --raw --numstat per group (index + working tree), off switch via git.diffStatistics
Source Control view Renders +N / -M per resource and per group, with dedicated theme colors

The counts reuse the existing scmGraph.historyItemHover*Foreground ramp through two new colors, scm.diffStatisticsInsertionsForeground and scm.diffStatisticsDeletionsForeground, so the graph and the resource list stay on one palette.

Scope

Deliberately not covered, to keep the change small:

  • Untracked files — git reports no statistics for them, and counting their lines from disk means reading arbitrary working-tree content on every status update. Left for a follow-up.
  • Unmerged paths — git reports no line statistics for them either.
  • Copies and type changes — excluded by --diff-filter=ADMR; the shared --raw parser does not model them today.

Cost control: the two diff commands only run when git.diffStatistics is enabled, and are skipped entirely for repositories that hit git.statusLimit.

Verifying

  1. Run Code - OSS from this branch and open a repository with staged and unstaged changes.
  2. Confirm each file shows green +N / red -M, and that each group header shows the sum.
  3. Compare against git diff --numstat and git diff --cached --numstat.
  4. Set "git.diffStatistics": false and confirm the counts disappear without a reload.

Screen reader labels read as README.md, Index Modified, 9 insertions, src — the visual counts are aria-hidden so they are not announced twice.

Copilot AI balanced review requested due to automatic review settings August 5, 2026 09:41
@thexin7
thexin7 force-pushed the feature/scm-diff-line-stats branch from b4165fc to 2da20e0 Compare August 5, 2026 09:41
@thexin7

thexin7 commented Aug 5, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@vs-code-engineering

vs-code-engineering Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📬 CODENOTIFY

The following users are being notified based on files changed in this PR:

@lszomoru

Matched files:

  • extensions/git/package.json
  • extensions/git/package.nls.json
  • extensions/git/src/git.ts
  • extensions/git/src/repository.ts
  • extensions/git/src/test/diffStatistics.test.ts
  • extensions/git/tsconfig.json
  • src/vs/workbench/contrib/scm/browser/media/scm.css
  • src/vs/workbench/contrib/scm/browser/scmViewPane.ts
  • src/vs/workbench/contrib/scm/common/scm.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds per-resource and per-group line-change statistics to Source Control, backed by a proposed SCM API and Git integration.

Changes:

  • Adds proposed diff-statistics API plumbing.
  • Renders configurable, themed, accessible +/- counts.
  • Computes Git statistics with tests for untracked-file line counting.

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/vscode-dts/vscode.proposed.scmDiffStatistics.d.ts Defines the proposed API.
src/vs/workbench/contrib/scm/common/scm.ts Adds internal statistics types.
src/vs/workbench/contrib/scm/browser/scmViewPane.ts Renders and announces statistics.
src/vs/workbench/contrib/scm/browser/scm.contribution.ts Registers the visibility setting.
src/vs/workbench/contrib/scm/browser/media/scm.css Styles statistics.
src/vs/workbench/api/common/extHostSCM.ts Serializes extension-provided statistics.
src/vs/workbench/api/common/extHost.protocol.ts Extends the SCM protocol tuple.
src/vs/workbench/api/browser/mainThreadSCM.ts Deserializes statistics.
src/vs/platform/extensions/common/extensionsApiProposals.ts Registers the API proposal.
extensions/git/tsconfig.json Includes the proposal declaration.
extensions/git/src/test/diffStatistics.test.ts Tests file-line counting.
extensions/git/src/repository.ts Computes and attaches statistics.
extensions/git/src/git.ts Runs and parses Git diff statistics.
extensions/git/package.json Enables the proposal for Git.
build/lib/stylelint/vscode-known-variables.json Allows the new theme variables.

Comment thread extensions/git/src/repository.ts Outdated
Comment on lines +58 to +63
/**
* Untracked files are not reported by `git diff`, so their line count is read from
* disk. Files larger than this are skipped to bound the amount of I/O per status
* update.
*/
const DIFF_STATISTICS_MAX_FILE_SIZE = 1024 * 1024;
Comment thread extensions/git/src/repository.ts Outdated
return undefined;
});

await this.applyDiffStatistics(indexGroup, workingTreeGroup, untrackedGroup, similarityThreshold, cancellationToken);
* resources, while the group header is re-rendered on every scroll. Cache the result
* keyed on the resources array, which is replaced whenever the group changes.
*/
const resourceGroupDiffStatistics = new WeakMap<readonly ISCMResource[], ISCMResourceDiffStatistics | null>();
Comment thread extensions/git/src/git.ts Outdated
}

private async diffFilesWithStats(ref: string | undefined, options: { cached: boolean; similarityThreshold?: number }): Promise<DiffChange[]> {
const args = ['diff', '--raw', '--numstat', '--diff-filter=ADMR', '-z'];
await fs.rm(tempDir, { recursive: true, force: true });
});

test('countFileLines', async () => {
Adds a proposed `SourceControlResourceState.diffStatistics` API and renders
the reported insertions/deletions next to each resource, as well as the
aggregate of a resource group on its header.

The git extension fills the statistics in from `git diff --raw --numstat` for
the index and the working tree. It skips repositories that hit the status
limit, and can be turned off entirely with `git.diffStatistics`.
@thexin7
thexin7 force-pushed the feature/scm-diff-line-stats branch from 2da20e0 to 4586fe0 Compare August 5, 2026 10:35
@thexin7 thexin7 changed the title feat(scm): show added/removed line counts in Source Control scm: show added and removed line counts in the Source Control view Aug 5, 2026
@thexin7

thexin7 commented Aug 5, 2026

Copy link
Copy Markdown
Author

Force-pushed a rewrite that squashes this down to a single commit and cuts the diff from +490 to +327. The inline review comments above are now outdated, so here is how each one was handled:

Review point Resolution
The 1 MiB per-file cap does not bound I/O per status update Dropped the untracked-file fallback entirely. Nothing is read from disk anymore, so there is no I/O budget to get wrong. Untracked files simply show no counts.
Statistics are computed even when the feature is off The setting moved from scm.diffStatistics to git.diffStatistics, i.e. next to the code that spends the git commands. When it is off, no diff runs at all. It is wired into the existing config listener, so toggling it refreshes the status.
The group aggregate cache never invalidates Correct, and thanks — MainThreadSCMResourceGroup.resources is allocated once and mutated via splice, so keying a WeakMap on it was wrong. Removed the cache; the aggregate is now computed during render.
--diff-filter=ADMR drops C and T Kept the filter and documented it as out of scope. parseGitChangesRaw aborts on statuses it does not model, so widening the filter without extending that shared parser would break the three existing callers.
Tests only cover the untracked fallback That code is gone. The test now covers toDiffStatisticsMap, including rename mapping and the case where a rename's original path collides with another change.

Two things I did not take:

  • Type ramp for the counts. label1 does not exist in this repo. font-size: 0.9em is what iconlabel.css already uses for the description text in the same row, so the counts sit on the ramp that is actually there.
  • Extra sort key on compareResourceStates. Removed instead. Ordering files by change size would make rows move around as you type, and it was unreachable anyway.

Also fixed while verifying: the accessible label said 1 deletions and padded zeros (9 insertions, 0 deletions). It now reads README.md, Index Modified, 9 insertions, src.

Verified against a repository with staged, unstaged, deleted, added and untracked files — the rendered counts match git diff --numstat and git diff --cached --numstat exactly. Screenshot in the description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show +/- line diff stats in the Source Control panel

3 participants