Skip to content

[minor] Replace LibGit2Sharp with the git command line - #377

Merged
matt-edmondson merged 1 commit into
mainfrom
chore/sdk-2.21.1-lf-migration
Aug 20, 2026
Merged

[minor] Replace LibGit2Sharp with the git command line#377
matt-edmondson merged 1 commit into
mainfrom
chore/sdk-2.21.1-lf-migration

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

No description provided.

@matt-edmondson

Copy link
Copy Markdown
Contributor Author

Keeping this open — do not close as part of the SDK-sweep cleanup.

Audited during the org-wide cleanup of the chore/sdk-2.21.1-lf-migration branches (2026-08-20). The other ten PRs in that sweep were closed as fully superseded. This one is not, despite the misleading chore title.

It contains genuinely unmerged feature work:

  • [minor] Replace LibGit2Sharp with the git command line
  • New ProjectDirector/GitCli.cs (179 lines)
  • New ProjectDirector.Test/ project with GitCliTests.cs (258 lines)
  • Removal of the LibGit2Sharp package reference

main still references LibGit2Sharp in Directory.Packages.props, ProjectDirector.csproj, GitRepository.cs and ProjectDirector.cs, so none of this has landed by another route.

State

The branch is 3 ahead / 36 behind main and conflicting. The chore portion it carries (SDK 2.21.1, header migration, .gitattributes LF) is all obsolete — main is on SDK 2.27.2 and already has the rest.

Suggested path

Rebase onto main and drop the chore commit, keeping only the LibGit2Sharp → git CLI migration. That should shrink the diff to the GitCli work alone and clear the conflicts, since the conflicts are almost entirely in the chore-touched files.

libgit2 implements neither the clean and smudge filters nor the hooks, and
Git LFS is built entirely out of those. For this application the half that
bites is the smudge filter: cloning through LibGit2Sharp writes each LFS
pointer to disk as its literal text, so a repository that tracks binaries
comes down looking like a set of three-line files rather than its content.

All git work now goes through GitCli, which starts the git command through
ktsu.RunCommand. Arguments are passed as a list rather than as one command
string, so paths containing spaces need no quoting, and RunIn uses git -C so
the process working directory is never mutated -- which is what keeps the
background fetches safe to run concurrently.

Notable call site changes:

- Clone, fetch and status move across directly. Pull becomes --ff-only. The
  old code performed a real merge, committed it unattended, and swallowed
  CheckoutConflictException, which left the working tree mid-conflict with
  nothing said about it.
- Repository discovery takes the parent of each .git directory rather than
  asking libgit2 for the working directory, and reads the remote with
  git remote get-url.
- The index enumeration behind the repository diff becomes git ls-files -z.
  NUL separation turns off the quoting git otherwise applies to unusual
  paths, so names arrive exactly as recorded.
- RepositoryNotFoundException was driving control flow in four places. Those
  become an explicit GitCli.IsRepository check.
- The log panel carried libgit2's debug trace. It now carries what git
  reported for each clone, fetch and pull, which is what someone watching
  that panel actually wants to see.

There are no credentials in this code any more: git uses the platform
credential helper, which also makes SSH remotes work.

Add ProjectDirector.Test, the repository's first test project. GitCliTests
pins both halves of the LFS guarantee -- a tracked binary is committed as a
pointer and comes back out of a clone as its content -- plus the repository
detection, tracked-file listing and remote lookup that replaced the exception
handling.

Also correct the solution configuration, which mapped Release to Debug, so
building Release produced a Debug binary.

Rebased onto main (2026-08-20), dropping the obsolete "[patch] Sync to
ktsu.Sdk 2.21.1" commit this branch was originally stacked on -- main is on
2.27.2 and already carries the header, .gitattributes and .editorconfig
migrations that commit was making.

Resolved against current main:

- Directory.Packages.props / ProjectDirector.csproj kept main's package
  versions, added ktsu.RunCommand and dropped LibGit2Sharp.
- Removed the Microsoft.Testing.Extensions.CodeCoverage and .TrxReport
  pins. MSTest.Sdk injects both itself, so they became duplicate
  PackageVersion items (NU1506) the moment this branch added the
  repository's first test project.
- README.md, DESCRIPTION.md and TAGS.md still described the application as
  using LibGit2Sharp; updated to match.
@matt-edmondson
matt-edmondson force-pushed the chore/sdk-2.21.1-lf-migration branch from b4a275a to ffbe2c6 Compare August 20, 2026 07:07
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
42.2% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@matt-edmondson

Copy link
Copy Markdown
Contributor Author

Conflicts resolved — rebased onto main (7d50bcb), now mergeable.

Rather than rebasing all three commits, I dropped the obsolete [patch] Sync to ktsu.Sdk 2.21.1 commit and replayed only the feature work. main is on SDK 2.27.2 and already carries the header, .gitattributes and .editorconfig migrations that commit was making, so it had nothing left to contribute — and it was the source of nearly all the conflicts. The branch is now a single commit, 3 files conflicted → 0.

Resolutions

File Resolution
Directory.Packages.props Kept main's newer package versions, added ktsu.RunCommand 1.5.1, dropped LibGit2Sharp
ProjectDirector/ProjectDirector.csproj Added the ktsu.RunCommand reference in alphabetical position

ProjectDirector.cs (216 lines changed) auto-merged cleanly against the 36 intervening commits.

Two things that needed fixing beyond the conflicts

  1. NU1506 restore failure. MSTest.Sdk injects Microsoft.Testing.Extensions.CodeCoverage and .TrxReport itself, so this repo's stale pins for both became duplicate PackageVersion items — surfacing only now, because this branch adds the repository's first test project. Removed both pins.
  2. Docs contradicted the change. README.md, DESCRIPTION.md and TAGS.md still listed LibGit2Sharp as the git layer. Updated to match CLAUDE.md.

Verification

Clean rebuild from a scratch worktree: 0 errors, 0 warnings, and 7/7 tests pass. CI agrees — Build, Test & Release ✅, CodeQL ✅.

One check still red: SonarCloud

new_coverage is 42.2% against an 80% gate. Every other condition passes (reliability/security/maintainability all A, duplication 0.0%, hotspots 100%).

This is structural rather than something the rebase introduced — the repo had no test project at all until this PR, so there is no coverage baseline to inherit. Of 64 new lines to cover, 37 are uncovered:

  • ProjectDirector.cs — 27 uncovered. Mostly the ImGui layer, but the repo-discovery and file-diff helpers in there are testable in principle.
  • GitCli.cs — 9 uncovered, all inside the FailureText formatter. Straightforwardly unit-testable.
  • GitRepository.cs — 1 uncovered (IsDirty).

Clearing the gate needs 25 of those 37 lines covered, so GitCli tests alone would not do it (that reaches ~56%). main is unprotected, so this does not block merging.

@matt-edmondson matt-edmondson changed the title Chore/sdk 2.21.1 lf migration [minor] Replace LibGit2Sharp with the git command line Aug 20, 2026
@matt-edmondson
matt-edmondson merged commit db4b03c into main Aug 20, 2026
7 of 8 checks passed
@matt-edmondson
matt-edmondson deleted the chore/sdk-2.21.1-lf-migration branch August 20, 2026 07:19
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.

1 participant