CodeLogic.GitHelper 5.0.117-preview

This is a prerelease version of CodeLogic.GitHelper.
This package has a SemVer 2.0.0 package version: 5.0.117-preview+0b2490b.
dotnet add package CodeLogic.GitHelper --version 5.0.117-preview
                    
NuGet\Install-Package CodeLogic.GitHelper -Version 5.0.117-preview
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CodeLogic.GitHelper" Version="5.0.117-preview" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CodeLogic.GitHelper" Version="5.0.117-preview" />
                    
Directory.Packages.props
<PackageReference Include="CodeLogic.GitHelper" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CodeLogic.GitHelper --version 5.0.117-preview
                    
#r "nuget: CodeLogic.GitHelper, 5.0.117-preview"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CodeLogic.GitHelper@5.0.117-preview
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CodeLogic.GitHelper&version=5.0.117-preview&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=CodeLogic.GitHelper&version=5.0.117-preview&prerelease
                    
Install as a Cake Tool

CodeLogic.GitHelper

NuGet License: MIT

Programmatic Git repository management for CodeLogic 5 — clone, fetch, pull, push, commit, branch, and reset, all from configured repositories.

A thin, async wrapper over LibGit2Sharp that manages a pool of named repositories. Every operation returns a CodeLogic Result<T> / Result instead of throwing, with a typed Error (NotFoundError, ValidationError, ...) and a stable git.* code on failure; fetch, pull and push return GitDiagnostics (timing and transfer counters) as their value; and the standout EnsureUpToDateAsync gives you one idempotent call that clones a repository if it's missing or hard-resets it to the remote tip if it already exists.

Install

dotnet add package CodeLogic.GitHelper

Quick start

using CL.GitHelper;
using CL.GitHelper.Errors;
using CodeLogic.Core.Results;

await Libraries.LoadAsync<GitHelperLibrary>();   // register before ConfigureAsync()
await CodeLogic.ConfigureAsync();
await CodeLogic.StartAsync();

var git  = Libraries.Get<GitHelperLibrary>();
var repo = (await git.GetRepositoryAsync("Default")).ValueOrThrow();   // unknown id → git.repository_not_found

// One idempotent call: clones if missing, else fetches + hard-resets to the remote tip.
Result<RepositoryInfo> sync = await repo.EnsureUpToDateAsync();
if (sync.IsSuccess)
    Console.WriteLine($"On {sync.Value!.CurrentBranch} @ {sync.Value.HeadCommitSha[..7]}");
else if (sync.Error is ConflictError { Code: GitErrors.UncommittedChangesCode })
    Console.WriteLine("Local changes present; not resetting.");
else
    Console.WriteLine($"Sync failed: {sync.Error!.Code}: {sync.Error.Message}");

Features

  • Idempotent sync — EnsureUpToDateAsync clones-or-updates in a single call; the easiest way to keep a working copy at the remote tip.
  • Full remote workflow — CloneAsync, FetchAsync, PullAsync (merge, rebase or fast-forward only), PushAsync (optionally with tags), each with an options model, a progress callback and a CancellationToken that aborts a running transfer. Network operations are bounded by the repository's TimeoutSeconds.
  • Credential hygiene — the PAT is sent only to the configured repository's host, and credentials in URLs are redacted from logs and errors.
  • Background fetch — AutoFetchIntervalMinutes fetches each repository on a timer while the library runs.
  • Commit & status — stage and commit with author metadata (CommitAsync), and inspect the working tree (GetStatusAsync).
  • Branches — list local/remote branches and check them out (ListBranchesAsync, CheckoutBranchAsync).
  • Reset & history — ResetHardAsync and GetCommitLogAsync.
  • Batch operations — FetchAllAsync and GetAllStatusAsync run across every configured repository with bounded concurrency.
  • Repository caching — configurable in-memory pool with automatic eviction.
  • Safe under concurrency — every operation (reads included) runs under the repository's lock, cached instances are owned and disposed by the manager (Dispose() on one is a no-op), and a transfer that stalls inside libgit2 makes later calls fail fast with git.timeout instead of queuing forever.
  • No exceptions — every operation, including GetRepositoryAsync and RegisterRepository, returns a CodeLogic Result<T> (or Result for checkout/reset). Failures carry an Error of the matching kind (NotFoundError, ValidationError, UnauthorizedError, ConflictError, InternalError, ...) with a stable code from GitErrors (e.g. git.remote_not_found); caught exceptions are logged and their type name is in Error.Details.

Configuration

Auto-generated on first run as config.githelper.json:

{
  "enabled": true,
  "baseDirectory": "",
  "defaultTimeoutSeconds": 300,
  "maxConcurrentOperations": 3,
  "enableRepositoryCaching": true,
  "cacheTimeoutMinutes": 30,
  "repositories": [
    {
      "id": "Default",
      "name": "My Repository",
      "repositoryUrl": "https://github.com/username/repository.git",
      "localPath": "my-repo",
      "defaultBranch": "main",
      "username": null,
      "password": null,
      "autoFetch": false,
      "autoFetchIntervalMinutes": 0,
      "timeoutSeconds": 300
    }
  ]
}
Setting Default Description
Enabled true Master switch; when false the library is inactive and health reports disabled.
BaseDirectory "" Root for relative LocalPath values; empty means the library data directory.
DefaultTimeoutSeconds 300 Network operation timeout for repositories whose TimeoutSeconds is 0 (1–3600).
MaxConcurrentOperations 3 Concurrency cap for batch operations (1–20).
EnableRepositoryCaching true Keep opened repositories in an in-memory pool.
CacheTimeoutMinutes 30 Idle eviction window (0–1440; 0 = never expire).
Repositories one entry Per-repository definitions (URL, local path, branch, credentials).

Each repository entry: Id, Name, RepositoryUrl (required), LocalPath (required; absolute, or relative to BaseDirectory without climbing out of it), DefaultBranch, Username, Password (PAT), AutoFetch (fetch once at start), AutoFetchIntervalMinutes (background fetch timer; 0 = off), TimeoutSeconds (network timeout; 0 = DefaultTimeoutSeconds). Authentication is HTTPS only, and the token is only sent to the host of RepositoryUrl — see the full guide for details. SSH key fields exist in config but are not currently wired.

Changing config while running

With CodeLogic 4.9+, config.githelper.json applies live when changed through Libraries.UpdateConfigAsync, Libraries.ReloadConfigAsync, or the config file watcher. Only the repositories that changed are touched:

  • Changed or removed repository: dropped from the cache. Its old GitRepository is disposed once the git operation running on it finishes, and the next GetRepositoryAsync opens it with the new settings. Call GetRepositoryAsync again rather than keeping an old instance.
  • Added repository: available at once.
  • Untouched repositories: keep their open instances.
  • Library-wide settings (cache, timeouts, concurrency) apply at once. A changed baseDirectory re-resolves every relative localPath.
  • Added or changed repositories with autoFetch are fetched in the background.
  • Repositories added with RegisterRepository are kept across changes and never written to the file.
  • enabled can be switched either way.
  • An invalid configuration (Validate() fails) is rejected: the errors are logged and the current settings stay.

Documentation

Full guide: CL.GitHelper documentation

Requirements

License

MIT — see LICENSE.

Audit completion notes and current limits are in the guide.

Product Compatible and additional computed target framework versions.
.NET net11.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on CodeLogic.GitHelper:

Package Downloads
CodeLogic.WebLogic

Web application toolkit for CodeLogic 4 — routing, templates, forms, an ordered security pipeline, auth flows and realtime. No third-party dependencies beyond CodeLogic.*.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
5.0.117-preview 52 11/7/2026
5.0.116-preview 69 11/1/2026
5.0.115-preview 121 11/1/2026
5.0.114-preview 37 10/31/2026
5.0.113-preview 38 10/31/2026
4.9.98 610 10/25/2026
4.8.93 65 9/23/2026
4.8.87 105 9/13/2026
4.8.86 66 9/13/2026
4.8.85 63 9/12/2026
4.6.72 236 6/20/2026
4.6.69-preview 66 6/20/2026
4.5.2 142 5/24/2026
4.5.2-preview.68 91 6/20/2026
4.5.1 191 5/24/2026
4.5.1-preview.56 129 5/24/2026
4.4.2-preview.53 92 5/24/2026
4.4.1 131 5/24/2026
4.0.5 131 5/15/2026
4.0.4 136 5/9/2026
Loading failed

# CL.GitHelper — Changelog

All notable changes to **CodeLogic.GitHelper** are documented here. Versions follow
[Semantic Versioning](https://semver.org/).

## 5.0 (unreleased)

### Audit completion (2026-11-07)

- Apply the shared bounded single-line formatter after credential redaction; split repository operations and manager responsibilities into partial files.


### Breaking

- Targets **.NET 11** (`net11.0`) and requires **CodeLogic 5.0** (`[5.0.0, 6.0.0)`). On .NET 10, stay on
 4.9.x. CodeLogic 5 makes `Result<T>` / `Result` C# 15 unions and `Error` a closed record with
 kind subtypes (`NotFoundError`, `ValidationError`, ...). This library now returns those types
 directly (see below), so its results can be matched exhaustively with `switch`.
- **`GitResult<T>` is removed**; every operation returns CodeLogic's `Result<T>` / `Result`
 (`CodeLogic.Core.Results`). `ErrorMessage` → `Error.Message`; `Exception` is gone (the library
 logs it, and `Error.Details` holds the exception type name); `Diagnostics` is gone (see below).
- Signature changes on `GitRepository`:
 - `FetchAsync`, `PullAsync`, `PushAsync`: `Task<GitResult<bool>>` → `Task<Result<GitDiagnostics>>`.
   The diagnostics are now the success value. A pull's diagnostics include its fetch step's
   messages, and a failed fetch inside a pull returns the fetch's own error.
 - `CheckoutBranchAsync`, `ResetHardAsync`: `Task<GitResult<bool>>` → `Task<Result>`.
 - `CloneAsync`, `EnsureUpToDateAsync`, `GetRepositoryInfoAsync`: `Task<GitResult<RepositoryInfo>>`
   → `Task<Result<RepositoryInfo>>`. `CommitAsync` → `Task<Result<CommitInfo>>`, `GetStatusAsync`
   → `Task<Result<RepositoryStatus>>`, `ListBranchesAsync` → `Task<Result<List<BranchInfo>>>`,
   `GetCommitLogAsync` → `Task<Result<List<CommitInfo>>>`. Clone, commit and status no longer
   return diagnostics; clone logs its duration and messages instead.
- `GitHelperLibrary` / `GitManager`: `FetchAllAsync` → `Task<Dictionary<string, Result<GitDiagnostics>>>`,
 `GetAllStatusAsync` → `Task<Dictionary<string, Result<RepositoryStatus>>>`.
- **Expected failures are results, not exceptions** (audit 2026-11):
 - `GetRepositoryAsync` (library and manager) → `Task<Result<GitRepository>>`. An unknown id is a `NotFoundError`
   `git.repository_not_found` with `Details = "not_configured"` (was `ArgumentException`); on the library, calling it
   before initialization, while disabled or after stop is an `UnavailableError` `git.not_initialized` (was
   `InvalidOperationException`). Migrate with `(await git.GetRepositoryAsync(id)).ValueOrThrow()` or check `IsFailure`.
 - `RegisterRepository` (library and manager) → `Result`: a duplicate id is a `ConflictError` `git.repository_exists`,
   a missing URL/path or an escaping path a `ValidationError` `git.invalid_configuration`, and on the library
   `git.not_initialized` (all used to throw).
 - `GitHelperLibrary.FetchAllAsync` / `GetAllStatusAsync` → `Task<Result<Dictionary<…>>>` (`git.not_initialized`
   instead of `InvalidOperationException`). The `GitManager` versions are unchanged.
- **`Dispose()` on a `GitRepository` obtained from the manager is a no-op.** The manager owns cached instances and
 disposes them on eviction, config change or stop; a consumer `using` used to dispose the cached instance and
 break every later caller. Instances you construct yourself are not affected.
- `GitHelperConfig.Validate()` and `RepositoryConfiguration.IsValid()` reject a relative `LocalPath` that climbs out of
 `BaseDirectory` (`../../etc`, `a/../../b`); use a path inside it or an absolute path.
- `GitManager` implements `IAsyncDisposable`; `GitManager.Retire()` (internal) is gone.
- Failures have a kind and a stable code, all listed as constants on the new
 `CL.GitHelper.Errors.GitErrors`:
 - `NotFoundError`: `git.repository_not_found` (not cloned), `git.remote_not_found`,
   `git.branch_not_found` (checkout or reset target), `git.not_found`.
 - `ValidationError`: `git.empty_commit_message`, `git.nothing_to_commit`, `git.ssh_not_supported`.
 - `UnauthorizedError`: `git.authentication_failed`.
 - `ConflictError`: `git.directory_not_empty`, `git.uncommitted_changes`, `git.checkout_conflict`,
   `git.non_fast_forward`.
 - `UnavailableError`: `git.cancelled`.
 - `InternalError` (anything else): `git.clone_failed`, `git.fetch_failed`, `git.pull_failed`,
   `git.push_failed`, `git.commit_failed`, `git.status_failed`, `git.info_failed`,
   `git.list_branches_failed`, `git.checkout_failed`, `git.reset_failed`, `git.log_failed`.

### Changed

- Fetch now records `ObjectCount` and `BytesTransferred` in its diagnostics.
- Failures of read operations (status, info, branches, log) are logged at debug level with the
 full exception; before, the exception was only on the result.
- LibGit2Sharp is pinned to 0.32.0 instead of floating (`0.32.*`).

### Documentation

- Configuration examples in the README and the docs use the camelCase keys the CodeLogic loader reads.
 The PascalCase keys shown before are matched case-sensitively and were ignored, leaving defaults active.

### Fixed

- **Reads no longer race disposal** (audit 2026-11). `GetStatusAsync`, `GetRepositoryInfoAsync`, `ListBranchesAsync`,
 `GetCommitLogAsync`, the dirty check of `EnsureUpToDateAsync` and the lazy open used the native handle without the
 repository lock, so an eviction could dispose it mid-read (and two first calls could each open, leaking a handle).
 Every operation now runs under the lock; an operation on a disposed instance returns `git.repository_disposed`.
- **A disposed cached instance is never handed out again**: `GetRepositoryAsync` and the cleanup sweep check
 `IsDisposed` and create a fresh instance.
- **Runtime registration is thread-safe.** `RegisterRepository` / `UnregisterRepositoryAsync` mutated the repository
 list that batch calls, the health check and auto-fetch enumerate; they now replace the configuration as a whole
 under the cache lock, like live config changes. Repository ids in the cache are case-insensitive.
- **Stopping waits for running operations.** `OnStopAsync` disposed every repository at once, under running clones;
 it now awaits `GitManager.DisposeAsync()`, which disposes each repository once its operation finishes, bounded by
 `GitManager.DefaultDisposeTimeout` (30 s; a repository still busy then is disposed when it returns).
- **A stalled transfer no longer wedges the repository.** A connection that hangs before any progress callback kept
 libgit2 (and the repository lock) busy forever and queued every later call behind it. A watchdog now logs
 "transfer stalled" `GitRepository.DefaultStallGrace` (30 s) after the timeout; from then on, until the native call
 returns, waiting and new calls return `git.timeout` at once (`GitRepository.IsStalled`).
- `ConfigureAwait(false)` on every library await.
- **Cache eviction no longer disposes a repository under a running operation.** The idle-expiry timer,
 `GetRepositoryAsync` replacing a stale entry, `EvictFromCacheAsync` and `ClearCacheAsync` disposed the
 evicted `GitRepository` at once. Idle time only counts `GetRepositoryAsync` calls, so a clone or fetch that ran
 longer than `CacheTimeoutMinutes` had its LibGit2 handle and lock disposed underneath it (the operation's
 lock release then threw `ObjectDisposedException`). Evicted instances are now disposed once their current
 operation finishes, as config changes and disabling the library already did.
- **Push and pull use the configured credentials.** `PushAsync` passed no credentials provider, and
 `PullAsync` called `Commands.Pull` without fetch options, so every push, and every pull of a private
 repository, failed with `git.authentication_failed` even with a PAT configured.
- **Documented options are honoured.** `PullOptions.Strategy` (merge, rebase or fast-forward only),
 `PullOptions.RemoteName`, `PushOptions.PushTags`, the `OnProgress` callbacks of clone, fetch and push,
 `RepositoryConfiguration.TimeoutSeconds` / `GitHelperConfig.DefaultTimeoutSeconds` and
 `RepositoryConfiguration.AutoFetchIntervalMinutes` were never read. See the pull, push, timeout and
 auto-fetch notes below.
- **Cancellation aborts a running transfer.** The token only stopped an operation that had not started
 yet; libgit2's progress callbacks now return `false` once it is cancelled, so a clone, fetch or push
 stops mid-transfer with `git.cancelled`.
- **Bare repositories count as cloned.** `EnsureUpToDateAsync` and every operation looked for a `.git`
 directory, so a bare repository (or a worktree with a `.git` file) was reported as not cloned and
 `EnsureUpToDateAsync` tried to clone into it. `EnsureUpToDateAsync` on a bare repository now only
 fetches, and `RepositoryInfo` for it no longer throws.
- **The health check reports a disabled library as `Healthy`** ("disabled") instead of `Unhealthy`.
- **Live config changes are validated.** A change that fails `GitHelperConfig.Validate()` is logged and
 ignored, keeping the current settings, just as an invalid file stops initialization.
- A clone that fails or is cancelled removes the directory it created, so the next attempt does not fail
 with `git.directory_not_empty`.

### Security

- **Redaction gaps closed** (audit 2026-11). Commit, checkout and reset failures, and the catch blocks of the batch
 calls, auto-fetch, start-up probe and health check, logged or returned the raw exception message (and handed the
 exception, with its message, to the logger). All now go through redaction. `Redact` masks the configured token
 before the URL user-info, so a token containing `@` is no longer half-masked (`https://***@ss@host`), and also
 masks its percent-encoded form (`p%40ss`).
- **The PAT is sent only to the configured host.** The credentials callback answered every request,
 so a submodule, second remote or redirect on another host received the token. It now hands it out only
 when the request's scheme, host and port match `RepositoryUrl` (no `https` → `http` downgrade); other
 hosts get empty credentials and fail with `git.authentication_failed`, and a warning is logged.
- **Credentials are redacted.** URLs with embedded credentials (`https://user:token@host/…`) were logged
 verbatim ("Cloning …") and could appear in error messages. User info in URLs and the configured token
 are now replaced by `***` in log lines, `Error.Message` and `RepositoryInfo.RemoteUrl`.

### Added

- Error codes `git.repository_exists`, `git.invalid_configuration`, `git.not_initialized`, `git.repository_disposed`
 and `GitErrors.NotConfiguredDetails`; `GitRepository.IsDisposed` is public.
- Error codes `git.timeout` (`TimeoutError`), `git.merge_conflict` and `git.push_rejected` (`ConflictError`)
 on `GitErrors`.
- **Pull strategies.** `Merge` fast-forwards or creates a merge commit; on conflicts it returns
 `git.merge_conflict` and leaves the conflicted files in place, as `git pull` does. `Rebase` replays
 local commits on the fetched tip and aborts on conflicts. `FastForwardOnly` returns
 `git.non_fast_forward` when the branches diverged. The upstream is the branch's tracking branch on
 `RemoteName`, else the remote branch of the same name (`git.branch_not_found` when neither exists).
 `PullOptions.RemoteName` selects the remote; when left at `"origin"`, `FetchOptions.RemoteName` is
 used as before.
- **Push.** `PushTags` pushes every local tag (tags are never forced). References the remote refuses
 return `git.push_rejected` with its reason; before, such a push reported success.
- **Timeouts.** Clone, fetch, the fetch step of pull, and push stop after the repository's
 `TimeoutSeconds` and return `git.timeout`. `TimeoutSeconds` now accepts `0`, meaning
 `DefaultTimeoutSeconds`. The timeout is checked at progress callbacks, so it cannot interrupt a
 connection that hangs before any data arrives.
- **Background fetch.** `AutoFetchIntervalMinutes > 0` starts a fetch timer per repository when the
 library starts. Config changes and `RegisterRepository` add, retime or remove timers; a fetch still
 running at the next tick is skipped; stopping or disabling the library cancels running fetches.
- `GitHelperConfig.Validate()` also rejects duplicate repository ids (case-insensitive), a negative
 `AutoFetchIntervalMinutes` and a `TimeoutSeconds` outside 0–3600. A config file with duplicate ids
 used to load (the first entry won); it now fails validation.
- Push and pull diagnostics carry `ObjectCount` and `BytesTransferred`; pull adds a `MergeStatus` or
 `RebaseStatus` metric.

## 2026-10-24

### Added

- **Live config changes** (requires CodeLogic 4.9+). `GitHelperLibrary` implements `IConfigReloadable`:
 a change to `config.githelper.json` made through `Libraries.UpdateConfigAsync`,
 `Libraries.ReloadConfigAsync` or the config file watcher is applied without a restart, touching only
 the repositories that changed. Changed or removed repositories are evicted from the cache and their
 old `GitRepository` is disposed once its running git operation finishes; untouched repositories keep
 their instances; library-wide settings and `baseDirectory` apply at once; added or changed
 repositories with `autoFetch` are fetched in the background. `Enabled` can be switched either way.
 The `RequiresRestart` hints on `id`, `repositoryUrl`, `localPath` and `baseDirectory` were removed.

### Changed

- `GitManager` keeps its own copy of the configuration. Repositories added with `RegisterRepository`
 no longer appear in `Configuration.Get<GitHelperConfig>()` (and so can no longer be written to
 `config.githelper.json` by a later save); they are kept across config changes.

## 2026-09-12

### Changed

- Unified the version line with the CodeLogic framework on **4.8.x**. Every official
 library and the framework now share one `major.minor`, so a given `4.8.<patch>`
 means the same generation across all packages.
- `version.txt` moved from `4.6` to `4.8`. The patch component remains the CI run
 number, composed at pack time; `AssemblyVersion` stays pinned at `Major.Minor.0.0`
 (now `4.8.0.0`) so every patch in the line loads interchangeably.

## 2026-06-20

### Fixed

- SSH is not supported by the bundled LibGit2Sharp transport; configuring an SSH
 key now logs a clear warning and cloning an SSH URL (`git@…` / `ssh://…`)
 fails fast with an explanatory message instead of silently doing nothing.
- `EnsureUpToDateAsync` now refuses to hard-reset (and discard) a dirty working
 tree unless called with `discardLocalChanges: true`, preventing accidental
 data loss.
- The repository cache now expires entries by idle time rather than absolute
 age, so a repository in active use is no longer disposed out from under a
 caller; batch concurrency is clamped to at least 1; `CommitOptions` gained a
 `CancellationToken`.

### Documentation

- Full house-style rewrite of the README and the `docs/libs/githelper.md` deep guide.
- Documented the repository workflow around `GetRepositoryAsync` and highlighted
 `EnsureUpToDateAsync` as the idempotent clone-or-update easy path.
- Documented the per-operation options models (`GitCloneOptions`, `GitFetchOptions`,
 `GitPullOptions`, `GitPushOptions`, `GitCommitOptions`) and the `MergeStrategy` enum.
- Documented the `GitResult<T>` / `GitDiagnostics` contract and the result-bearing models
 (`RepositoryInfo`, `CommitInfo`, `BranchInfo`, `RepositoryStatus`, `FileStatusEntry`).
- Documented manager batch operations (`FetchAllAsync`, `GetAllStatusAsync`), runtime
 registration, repository caching (`CacheStats` / `CacheEntryStats`), and the health check.
- Clarified authentication: HTTPS only, PAT-only auth sends `x-access-token`, and the SSH
 key fields are present in config but not currently wired.

## [4.5.2] — 2026-06-20

### Documentation

- Documented the full `GitRepository` workflow in the README: `CloneAsync`,
 `FetchAsync`, `PullAsync`, `PushAsync`, `ListBranchesAsync`, `CheckoutBranchAsync`,
 `CommitAsync` (with `FilesToStage`), and `GetCommitLogAsync`.
- Documented the `ResetHardAsync` and `EnsureUpToDateAsync` sync helpers with examples.
- Documented the `GitResult<T>` return contract (`IsSuccess` / `Value` / `ErrorMessage`
 / `Diagnostics`).
- Documented `GitManager` access via `GetManager()`, including `ExecuteOnAllAsync`,
 `HealthCheckAsync`, runtime `RegisterRepository` / `UnregisterRepositoryAsync`, and
 cache control (`GetCacheStats`, `ClearCacheAsync`).
- Clarified authentication: PAT-only auth sends `x-access-token`, and the SSH key
 configuration fields are reserved but not currently wired (use HTTPS URLs).

## [4.5.0] — 2026-05-24

### Changed

- **Unified versioning.** All CodeLogic.Libs now share a single version line
 controlled by `version.txt` in the repo root. This is a version alignment
 release — no functional changes to this library.
## [4.0.4] — 2026-04-16

### Changed

- README + manifest refresh for the v4 baseline. No functional changes vs 4.0.3.
- `LibraryManifest.Version` now reads from assembly metadata.

## [4.0.3] — 2026-04-15

### Added

- Wired credentials through to `Clone` / `Fetch` / `Pull` and added
 `ResetHard` + `EnsureUpToDate` helpers.

## [4.0.2] — 2026-04-09

### Changed

- Annotated GitHelper configuration with `[ConfigField]` for the admin UI surface.
- Aligned with the v4 baseline across all libraries.

## [4.0.0] — 2026-04-09

Major rewrite. Republished as v4.0.0 to reset the version line under the
unified v4 baseline. Thin libgit2sharp wrapper for programmatic clone /
pull / fetch / reset.

### Notes

- Earlier history is retained in the
 [git log](https://github.com/zyntal-com/CodeLogic.Libs/commits/main/CL.GitHelper).