Architecture
Component overview
The arrows summarize calls and data flow rather than every class dependency. All acquisition is coordinated by GitSnapshotAcquirer; the object pipeline validates downloaded data before delivery. Credentials apply in the HTTP transport, and GitClientOptions governs network policy and operation budgets. Temporary files are owned by GitObjectStore; final output belongs to the selected handler. Tests and documentation support the solution and are not runtime dependencies. Open the full-size SVG to inspect or edit the vector diagram.
Project map
| Location | Responsibility |
|---|---|
| CODE.Git.Client | Public library, targeting net10.0, nullable enabled, XML docs, warnings as errors, package generated on build |
| GitClientSample | Interactive console application demonstrating listing and custom stream handling |
| AzureDevOpsCloneSample | Authenticated full snapshot to disk using an Entra access token or PAT |
| CODE.Git.Client.Tests | Deterministic protocol, pack, tree, acquisition, limits, authentication, and safety tests |
| CODE.Git.Client.IntegrationTests | Opt-in real provider tests, optionally using Basic credentials |
| Docs | Developer and AI documentation included in the solution |
The test projects exist in the repository but are not project entries in CODE.Git.slnx.
Implementation map
All internal files below are in CODE.Git.Client/Internal.
| Component | Responsibility |
|---|---|
GitClient (public) |
Validates options/URL, owns transport, serializes operations with a semaphore, and exposes listing/cloning |
GitSnapshotAcquirer |
Coordinates discovery, HEAD resolution, metadata acquisition, selection, supplied-tree validation, and callbacks |
GitHttpTransport |
Builds GET/POST requests, applies credentials, enforces timeout/retry/redirect policy, and counts response bytes |
GitNetworkPolicy |
Canonicalizes repository URLs and checks DNS addresses against public-network policy |
GitProtocolV2Client |
Discovers capabilities; implements ls-refs, shallow fetch, tree filtering, selective OID requests, and sideband processing |
GitPktLineReaderWriter |
Reads Git packet framing and constructs protocol requests |
GitPackReader |
Validates PACK versions/checksums, inflates objects, reconstructs OFS/REF deltas, and registers objects |
GitObjectStore |
Stores objects in per-operation temporary files, verifies SHA-1 identities, and accounts for resource budgets |
GitTreeParser |
Resolves commit/tree objects into validated immutable directory and file models |
DiskFileHandler / CustomFileHandler (public) |
Writes files safely or delegates processing to application code |
Listing flow
GitClient.GetFileTreeAsyncacquires the operation semaphore.- The acquirer creates a disposable object store and protocol client.
- Discovery GETs
info/refs?service=git-upload-packwithGit-Protocol: version=2. ls-refsPOSTs togit-upload-packto resolve HEAD and its symbolic reference, when available.- A nonempty HEAD is fetched with
deepen 1andfilter blob:none. - Pack parsing and tree validation produce
GitFileTree. Temporary objects are removed when the store is disposed.
An empty/unborn repository returns an empty commit ID and empty root. Detached HEAD has a null reference name. A tree retains metadata, not downloaded blob streams or a reusable object cache.
Clone flow
The diagram below shows how commit selection and full or selective acquisition combine for a nonempty repository. Network calls pass through the protocol and HTTP layers; pack responses are parsed into the temporary object store before the acquirer builds a tree or delivers files.
Open the full-size clone-flow SVG to inspect or edit the vector diagram. The diagram shows successful delivery of supported files. Empty/unborn trees skip commit fetching. No selected files means no callbacks or selective blob fetch. Errors and cancellation stop the flow and clean temporary storage; previously delivered output remains. See Security and limits for unsupported content and Public API for stream ownership and errors.
Without a supplied tree, a full clone fetches the shallow snapshot including blobs; a filtered clone first fetches tree metadata. With a supplied tree, its canonical repository must match, and metadata is reacquired at its commit to validate paths, modes, names, and object IDs. HEAD is not resolved again in that path.
For a selective clone, the predicate runs exactly once per regular/executable file. Selected distinct blob IDs are fetched in batches controlled by BlobBatchSize. No selection means no blob fetch or callback. Unexpected unrelated blobs are rejected. There is no automatic full-download fallback if the server refuses selected blob IDs.
All selected objects are checked to be blobs before callbacks begin. LFS pointer detection occurs as files are processed. Each callback receives a file with known size and a temporary-file stream. Calls are sequential; streams close immediately after each callback. Handler failures stop the operation and are wrapped with the repository path, while cancellation propagates. Earlier output is retained.
Lifetime and protocol boundaries
Each operation has its own budgets and temporary store. Operations on a client serialize; callbacks are not parallel. Dispose a client only after its operation finishes. An internally created HttpClient is owned by the library; an injected one is owned by the caller.
Only HTTPS, protocol v2, and SHA-1 repositories are supported. The client requests no thin packs. Listing/selective metadata acquisition needs filter support; nonempty snapshots need shallow support. Provider capability advertisement and willingness to serve reachable unadvertised blob IDs are independent requirements.
The library has no Git executable dependency, native dependency, REST adapter, persistent repository, history API, staging, push, hook execution, or checkout filter execution. See Security and limits for the trust model and explicit omissions.