Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,17 @@ charset = utf-8
indent_style = space
indent_size = 4
tab_width = 4
end_of_line = crlf
# LF on every platform, matching the `* text=auto eol=lf` default in .gitattributes.
# Overrides below must stay in step with the eol pins in that file.
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

# .NET code files
[*.{cs,csx,cake,fs,fsx,vb,vbx}]
indent_style = tab

file_header_template = Copyright (c) ktsu.dev\nAll rights reserved.\nLicensed under the MIT license.
file_header_template = Copyright (c) 2023-2026 ktsu-dev contributors

# Default severity for all .NET Code Style rules
dotnet_analyzer_diagnostic.severity = error
Expand Down Expand Up @@ -503,4 +505,9 @@ indent_style = tab
# Shell scripts
[*.sh]
end_of_line = lf
indent_size = 2
indent_size = 2

# Windows batch scripts and Visual Studio solution files keep CRLF on every platform.
# These match the eol=crlf pins in .gitattributes.
[*.{cmd,bat,sln}]
end_of_line = crlf
22 changes: 15 additions & 7 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@
# Git Line Endings #
###############################

# Set default behaviour to automatically normalize line endings.
* text=auto
# Normalize all text files to LF in the repository, and check them out as LF on every
# platform. The explicit eol overrides each machine's core.autocrlf, so the working tree
# is byte-identical on Windows, Linux and macOS. This must stay in step with the
# end_of_line settings in .editorconfig.
* text=auto eol=lf

# Force bash scripts to always use LF line endings so that if a repo is accessed
# in Unix via a file share from Windows, the scripts will work. Redundant with the
# default above, kept explicit because these files break outright with CRLF.
*.sh text eol=lf

# Force batch scripts to always use CRLF line endings so that if a repo is accessed
# in Windows via a file share from Linux, the scripts will work.
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf
*.cmd text eol=crlf
*.bat text eol=crlf

# Force bash scripts to always use LF line endings so that if a repo is accessed
# in Unix via a file share from Windows, the scripts will work.
*.sh text eol=lf
# Visual Studio rewrites solution files with CRLF regardless of the checkout, so pin
# them to avoid a spurious whole-file diff every time the solution is opened.
*.sln text eol=crlf

###############################
# Git Large File System (LFS) #
Expand Down
21 changes: 1 addition & 20 deletions .runsettings
Original file line number Diff line number Diff line change
@@ -1,25 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<!-- Specify a deterministic output directory -->
<RunConfiguration>
<ResultsDirectory>.\coverage</ResultsDirectory>
<ResultsDirectory>TestResults</ResultsDirectory>
</RunConfiguration>
<!-- Specify a deterministic output directory -->
<RunConfiguration>
<ResultsDirectory>.\coverage</ResultsDirectory>
</RunConfiguration>

<!-- Configuration for coverlet data collector -->
<DataCollectionRunSettings>
<DataCollectors>
<DataCollector friendlyName="XPlat Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="coverlet.collector.DataCollectors.CoverletInProcDataCollector, coverlet.collector, Version=6.0.4.0, Culture=neutral, PublicKeyToken=null">
<Configuration>
<Format>opencover</Format>
<OutputPath>coverage.opencover.xml</OutputPath>
<Exclude>[*Test*]*,[*Tests*]*</Exclude>
<ExcludeByFile>**/obj/**/*</ExcludeByFile>
</Configuration>
</DataCollector>
</DataCollectors>
</DataCollectionRunSettings>
</RunSettings>
20 changes: 18 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ dotnet run
dotnet publish --configuration Release --output ./staging
```

This project has no test suite.
Tests live in `ProjectDirector.Test` (MSTest, via `MSTest.Sdk` + `ktsu.Sdk`). The app exposes its internals to the test project through `InternalsVisibleTo` in `ProjectDirector/AssemblyInfo.cs`. `GitCliTests` drives `GitCli` against throwaway repositories under the temp directory; the ImGui layer is not unit-tested.

```powershell
dotnet test --configuration Release
```

## Architecture

Expand All @@ -43,9 +47,21 @@ This project has no test suite.
- Concrete implementations: `GitHubRepository`, `AzureDevOpsRepository`
- Tracks: remote/local paths, fetch timing, diff results against other repos

**[GitCli.cs](ProjectDirector/GitCli.cs)** - Git access
- `GitResult` (exit code plus both streams) and the runner that produces it, built on `ktsu.RunCommand`
- Arguments are passed as a list rather than as a command string, so paths containing spaces need no quoting
- `RunIn` uses `git -C <repo>`, which never touches the process working directory and so stays safe while repositories are fetched concurrently
- Queries answer from git's exit code rather than by searching its output for "fatal"

### Why the git command line rather than a library

Git LFS is a pair of filters plus a set of hooks, and all of them belong to the git command. A library that reads and writes the object database directly bypasses them: a commit stores raw bytes where a pointer belongs, and a clone or checkout lands the pointer text on disk where the file belongs. This application clones, fetches and pulls, so it is the checkout side that matters here. `ProjectDirector.Test` pins both halves down.

Authentication follows from the same decision. There are no credentials in this code, because git uses the platform credential helper, which is also what makes SSH remotes work.

### Key Dependencies

- **LibGit2Sharp** - Git operations (clone, fetch, pull, status)
- **ktsu.RunCommand** - Starts the git command line, which is how all git work is done (see below)
- **Octokit** - GitHub API (list repos, user info)
- **DiffPlex** - Line-by-line file diffing
- **Hexa.NET.ImGui** - Immediate mode GUI framework
Expand Down
53 changes: 5 additions & 48 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,65 +3,22 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.15.1" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.4" />
<PackageVersion Include="DiffPlex" Version="1.9.0" />
<PackageVersion Include="FluentAssertions" Version="6.12.1" />
<PackageVersion Include="FluentValidation" Version="12.0.0" />
<PackageVersion Include="ImGui.NET" Version="1.91.6.1" />
<PackageVersion Include="ktsu.AppDataStorage" Version="1.16.33" />
<PackageVersion Include="ktsu.CaseConverter" Version="1.3.2" />
<PackageVersion Include="ktsu.Extensions" Version="1.5.35" />
<PackageVersion Include="ktsu.FuzzySearch" Version="1.2.1" />
<PackageVersion Include="ktsu.ImGui.App" Version="3.3.9" />
<PackageVersion Include="ktsu.ImGui.Popups" Version="3.3.9" />
<PackageVersion Include="ktsu.ImGui.Styler" Version="3.3.9" />
<PackageVersion Include="ktsu.ImGui.Widgets" Version="3.3.9" />
<PackageVersion Include="ktsu.ScopedAction" Version="1.1.2" />
<PackageVersion Include="ktsu.Semantics" Version="1.0.18" />
<PackageVersion Include="ktsu.SemanticString" Version="1.2.18" />
<PackageVersion Include="ktsu.StrongPaths" Version="1.3.2" />
<PackageVersion Include="ktsu.StrongStrings" Version="1.4.2" />
<PackageVersion Include="ktsu.TextFilter" Version="1.5.3" />
<PackageVersion Include="ktsu.ToStringJsonConverter" Version="1.2.4" />
<PackageVersion Include="ktsu.RoundTripStringJsonConverter" Version="1.0.1" />
<PackageVersion Include="LibGit2Sharp" Version="0.32.0" />
<PackageVersion Include="MessagePack" Version="3.1.4" />
<PackageVersion Include="Microsoft.Build" Version="17.14.8" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.14.8" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.6" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="Hexa.NET.ImGui" Version="2.2.9" />
<PackageVersion Include="ktsu.RunCommand" Version="1.4.26" />
<PackageVersion Include="ktsu.Semantics.Paths" Version="2.9.3" />
<PackageVersion Include="ktsu.Semantics.Strings" Version="2.9.3" />
<PackageVersion Include="Microsoft.SourceLink.AzureRepos.Git" Version="8.0.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Microsoft.SourceLink.Git" Version="1.0.0" />
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="17.14.2" />
<PackageVersion Include="Microsoft.Testing.Extensions.CrashDump" Version="1.7.2" />
<PackageVersion Include="Microsoft.Testing.Extensions.Fakes" Version="17.14.1" />
<PackageVersion Include="Microsoft.Testing.Extensions.HangDump" Version="1.7.2" />
<PackageVersion Include="Microsoft.Testing.Extensions.HotReload" Version="1.7.2" />
<PackageVersion Include="Microsoft.Testing.Extensions.Retry" Version="1.7.2" />
<PackageVersion Include="Microsoft.Testing.Extensions.TrxReport" Version="1.7.2" />
<PackageVersion Include="MSTest" Version="3.9.2" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="MSTest.TestAdapter" Version="3.9.2" />
<PackageVersion Include="MSTest.TestFramework" Version="3.9.2" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="NuGet.Packaging" Version="6.14.0" />
<PackageVersion Include="Octokit" Version="14.0.0" />
<PackageVersion Include="OpenAI" Version="2.13.0" />
<PackageVersion Include="Polyfill" Version="11.0.2" />
<PackageVersion Include="Spectre.Console" Version="0.50.0" />
<PackageVersion Include="Spectre.Console.Analyzer" Version="1.0.0" />
<PackageVersion Include="Spectre.Console.Cli" Version="0.50.0" />
<PackageVersion Include="Spectre.Console.Json" Version="0.50.0" />
<PackageVersion Include="Spectre.Console.Testing" Version="0.50.0" />
<PackageVersion Include="TestableIO.System.IO.Abstractions" Version="22.0.14" />
<PackageVersion Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="22.0.14" />
<PackageVersion Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.0.14" />
<PackageVersion Include="Tomlyn" Version="0.19.0" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
</ItemGroup>
</Project>
</Project>
Loading
Loading