Repository navigation
Fix diff-loading bugs that prevented the git tree filter from activating - #64
Merged
Merged
Conversation
AssertValidReference rejected any repository whose HEAD friendly name was not in the branch list, so a detached HEAD (checked-out commit/tag, mid-rebase/bisect, or detached worktree) threw HeadNotFoundException. The UI caught it and rendered an empty, unchanging list. Validate HEAD by checking it resolves to a real commit (Head.Tip) instead, which accepts detached HEAD while still rejecting an unborn/head-less repo. Adds detached-HEAD changeset tests over the Basic and Merge fixtures. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09814c7d-b363-45cb-83eb-1832f7a8abcb
TryGetRootRepositoryPath relied solely on IGitExt.ActiveRepositories, which is empty for git worktrees and submodules (.git is a file). The plugin then deactivated with 'Could not identify GIT repository to use', so selecting the filter did nothing even though the solution was inside a Git repo. Add filesystem-based GitRepositoryDiscovery (LibGit2Sharp Repository.Discover) and use it as a fallback from the solution directory when IGitExt reports no repository. Covers worktrees, submodules, and nested-subdirectory solutions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09814c7d-b363-45cb-83eb-1832f7a8abcb
The worktree scenario reaches PluginState=RUNNING yet selecting the filter does nothing and the settings button never appears. The existing logs used Debug.WriteLine (invisible in the VS Output pane) and none covered the filter/changeset path, so the failing boundary was unobservable. - GitTreeFilterOutput: drop the UI-thread guard (OutputStringThreadSafe is thread-safe) and add Log() with timestamp + thread id so background-thread stages can trace. This makes filter creation and changeset building visible. - Instrument GitFilterProvider.CreateFilter (service null / plugin state / reference object), GitFileFilter ctor + OnInitialized (settings-button trigger), GetIncludedItemsAsync, InvalidateAsync and CreateChangesetAsync. - Add a catch-all in CreateChangesetAsync so a non-GitOperationException no longer bubbles out of GetIncludedItemsAsync (which would abort filter initialization and hide the settings button); it is logged and degrades to an empty list instead. - Promote repo-discovery and reference-resolution logs in the service to the Output pane, and log IsFilterApplied/lifecycle transitions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09814c7d-b363-45cb-83eb-1832f7a8abcb
Bug #3 root cause: LibGit2Sharp 0.27.0-preview (libgit2 ~1.0) cannot parse the index written by modern git when the untracked-cache (UNTR) extension is present at scale (repos with core.untrackedCache/feature.manyFiles). This threw "invalid data in index - calculated checksum does not match expected" from RetrieveStatus in SolutionRepository.Changeset, leaving the Solution Explorer list empty ("won't budge"). Fix: upgrade LibGit2Sharp 0.27.0-preview-0182 -> 0.31.0 (libgit2 ~1.8), which parses the modern index cleanly. No API breaks. Native git2 binaries flow into the VSIX via the Core ProjectReference (lib/win32/*). Adds a regression test that builds a temp repo with feature.manyFiles + 500 files (confirmed RED on 0.27, GREEN on 0.31). Full Core suite: 83 passing. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09814c7d-b363-45cb-83eb-1832f7a8abcb
libgit2 1.8 (bundled with LibGit2Sharp 0.31, adopted to fix the modern
index-checksum bug) enforces the "dubious ownership" check from
CVE-2022-24765. On enterprise machines a git worktree/clone is frequently
owned by a different account than the one running Visual Studio, so
`new Repository(path)` threw "repository path '...' is not owned by current
user" inside GitTreeFilterService.SetUpAsync. That exception escaped the
async service factory, leaving SGitFilterService null and making the filter
provider fail with "Cannot find an instance of the GitTreeFilter.
IGitFilterService service" ("cannot open").
Root cause fix: call GlobalSettings.SetOwnerValidation(false) in
GitRepositoryFactory.Create before opening the repository. Visual Studio has
already opened the repo for the user and the plugin only reads it, so the
ownership gate adds no protection here.
Defense in depth: wrap the fallible body of SetUpAsync in try/catch so any
future setup exception degrades the plugin to INACTIVE (with the reason
logged to the output pane) instead of nulling the whole service.
TDD: RepositoryOwnershipTest.OpeningRepositoryDisablesLibGit2OwnerValidation
opens a repo through the public SolutionRepository API and asserts owner
validation is disabled. RED before the fix, GREEN after. Full Core suite
84/84. End-to-end probe against the real worktree with the Release-output
0.31 DLLs: open now succeeds and RetrieveStatus returns 1658 entries.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 09814c7d-b363-45cb-83eb-1832f7a8abcb
kboom
enabled auto-merge (squash)
July 20, 2026 17:58
GitHub automatically fails runs that use the deprecated actions/cache v2. Bump to v4 (current major, input-compatible) to keep CI working. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 09814c7d-b363-45cb-83eb-1832f7a8abcb
kboom
disabled auto-merge
July 20, 2026 18:07
kboom
added a commit
that referenced
this pull request
Jul 20, 2026
Promote the diff-loading fixes from `main` to `release/v1` to cut a normal **v1.20.0** release. Merging this PR triggers `ci-release.yml`, which tags `v1.20.0`, builds the VSIX, and creates a **draft** GitHub Release with the VSIX attached. Publishing that draft release then pushes it to the VS Marketplace via `publish.yml`. ### Included - #64 Fix diff-loading bugs that prevented the git tree filter from activating (detached HEAD, worktree detection, modern-index checksum, repo-owned-by-another-user; + `actions/cache` v4 bump).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The git tree filter would silently fail to load the diff (the explorer list wouldn't budge, and the settings button never appeared) under several real-world conditions. This PR fixes four distinct root causes found via systematic debugging, each backed by a regression test.
All work follows TDD: every fix has a test that is RED before the change and GREEN after. Full
GitTreeFilter.Coresuite: 84/84 passing.Bugs fixed
Bug #1 — Detached HEAD (
735f689)AssertValidReferencerejected a detached HEAD and threwHeadNotFoundException, deactivating the plugin. Now tolerates detached HEAD via aHead?.Tip == nullguard.Bug #2 — Worktrees / submodules not detected (
b0d68ef)VS
IGitExt.ActiveRepositoriesis empty for worktrees, so the plugin reported "Could not identify GIT repository". AddedGitRepositoryDiscoverywhich falls back to LibGit2SharpRepository.Discover.Bug #3 — Modern git index checksum error (
3a6cd86)On large repos with a modern index (
UNTRextension) the diff failed with "invalid data in index — calculated checksum does not match expected". Root cause: the old LibGit2Sharp0.27.0-preview(libgit2 ~1.0) couldn't parse a modern index. Upgraded to LibGit2Sharp 0.31.0 (libgit2 1.8), which VS itself ships.Bug #4 — Repo owned by another user (
b35c7fd)The 0.31 upgrade pulled in libgit2 1.8, which enforces the CVE-2022-24765 "dubious ownership" check. On enterprise machines a worktree/clone owned by a different account made
new Repository(path)throw "repository path … is not owned by current user" insideSetUpAsync. That exception escaped the async service factory, leavingSGitFilterServicenull → "Cannot find an instance of the GitTreeFilter.IGitFilterService service" ("cannot open").GlobalSettings.SetOwnerValidation(false)inGitRepositoryFactory.Create— VS already opened the repo for the user and the plugin only reads it.SetUpAsyncin try/catch so any future setup exception degrades the plugin toINACTIVE(with the reason logged to the output pane) instead of nulling the whole service.Also added thread-safe Output-pane diagnostics across the activation flow (
a246704) to make future issues diagnosable from the log.Testing
ModernIndexChangesetTest,RepositoryOwnershipTest— each RED before its fix, GREEN after.GitTreeFilter.Coresuite: 84/84.