Exgit.Transport implementation for git smart-HTTP v2.
Speaks git protocol v2 over HTTPS using Req as the HTTP client.
Implements capabilities/1, ls_refs/2, fetch/3, and
push/4; supports want-ref, filter, sideband-all, and
symrefs extensions.
Credentials
The :auth field accepts either a bare auth tuple (auto-wrapped
in %Exgit.Credentials{} with host-binding) or an explicit
%Exgit.Credentials{} struct. Host-bound credentials refuse to
emit auth headers when the URL host doesn't match the bound
pattern, defending against cross-origin leaks through redirects
or user-supplied URLs.
TLS
verify_tls: true (default) uses :public_key.cacerts_get/0 and
enables hostname verification via :ssl.pkix_verify_hostname/3.
Redirects
Disabled by default to prevent any chance of credential leaks
through an unexpected redirect target. Opt-in via
redirect: :same_origin or redirect: :follow; host-binding on
:auth remains in force either way.
Summary
Functions
Compute the auth headers for a specific request URL. Exposed for
testing; production call-sites use request_opts/5 or do_request/5.
Discover the server's protocol-v2 capability advertisement.
Return {capabilities, updated_transport} so the caller can thread
the transport forward and avoid re-discovering on every call.
Fetch a pack for the given wants (20-byte SHAs) via the
protocol-v2 fetch command.
List the remote's refs via the protocol-v2 ls-refs command.
Build a transport for the git smart-HTTP remote at url.
Push ref updates and the accompanying pack_bytes via
git-receive-pack.
Build the keyword list we'd pass to Req.request/1 for the given
request. Exposed publicly for test introspection; production code
goes through do_request/5.
Types
Functions
Compute the auth headers for a specific request URL. Exposed for
testing; production call-sites use request_opts/5 or do_request/5.
This is the enforcement point for credential host-binding: a
%Exgit.Credentials{} with a non-matching host pattern returns []
regardless of what the caller thought they attached.
Discover the server's protocol-v2 capability advertisement.
Performs the info/refs?service=git-upload-pack discovery GET and
parses the advertisement into a map keyed by capability name
(string keys; well-known entries like :version get structured
values). Returns {:error, :server_does_not_support_v2} for
protocol-v1-only servers, {:error, {:malformed_response, reason}}
when the body is not valid pkt-line framing.
A struct carrying a memoized capabilities_cache (see
capabilities_cached/1) returns the cached result without HTTP.
Return {capabilities, updated_transport} so the caller can thread
the transport forward and avoid re-discovering on every call.
The default fetch/ls-refs paths still accept a non-cached transport
(to preserve backward compatibility with struct-sharing callers);
capabilities_cached/1 is the opt-in path for workflows that make
many small fetches against the same transport.
Fetch a pack for the given wants (20-byte SHAs) via the
protocol-v2 fetch command.
Returns {:ok, pack_bytes, summary}. With :object_store the
pack is stream-parsed straight into the store — pack_bytes is
<<>> and summary carries :objects and the updated :store.
Without it, pack_bytes is the raw pack for the caller to parse.
Sideband channel-3 server messages surface as
{:error, {:server_error, msg}}.
Options
:haves— SHAs the client already has, for negotiation.:depth— shallow-clone depth (deepen).:filter— partial-clone filter spec (e.g."blob:none").:object_store— anExgit.ObjectStoreto stream objects into as they arrive (bounded memory on multi-GB packs).:sideband,:thin_pack,:ofs_delta— explicit feature overrides; by default they're negotiated from the server's advertised capabilities.
@spec ls_refs( t(), keyword() ) :: {:ok, [Exgit.Transport.ref_entry()], Exgit.Transport.ls_refs_meta()} | {:error, term()}
List the remote's refs via the protocol-v2 ls-refs command.
Returns {:ok, refs, meta} where refs is a list of
{ref_name, sha} 2-tuples and meta carries protocol-v2
side-channel data (:head symref target, :peeled tag targets) —
see Exgit.Transport.ls_refs_meta/0. Invalid ref names from the
server are dropped (with [:exgit, :security, :ref_rejected]
telemetry); more than max_refs refs aborts with
{:error, {:too_many_refs, cap}}.
Options
:prefix— ref-prefix filter or list of filters (e.g."refs/heads/"); default[](all refs).:symrefs— ask for symref targets, revealing where HEAD points (defaulttrue).:peeled— ask forpeeled:<sha>attributes on annotated tags (defaulttrue).
Build a transport for the git smart-HTTP remote at url.
Options
:auth— credentials; a bare auth tuple (auto-wrapped in a host-bound%Exgit.Credentials{}) or an explicit%Exgit.Credentials{}struct. See the module doc.:user_agent— theuser-agentheader value.:connect_timeout— connect timeout in milliseconds (default 10_000).:receive_timeout— response-body timeout in milliseconds (default 300_000);:infinitydisables.:verify_tls— TLS peer verification (defaulttrue).:connect_options— extra transport options merged into the library's TLS defaults (custom CA bundles, mTLS, SNI).:redirect— redirect policy:false(default),:same_origin, or:follow.:max_refs— cap on refs accepted from a single ls-refs response (default 1000000).ls_refs/2aborts with{:error, {:too_many_refs, cap}}when a server exceeds it — a backstop against a hostile server streaming unbounded refs into client memory.
@spec push(t(), [Exgit.Transport.ref_update()], binary(), keyword()) :: {:ok, %{ref_results: [{String.t(), :ok | :error}]}} | {:error, term()}
Push ref updates and the accompanying pack_bytes via
git-receive-pack.
Each update is a {ref, old_sha, new_sha} 3-tuple (20-byte SHAs;
nil means the all-zero SHA, i.e. create/delete). Returns
{:ok, %{ref_results: [{ref, :ok | :error}]}} parsed from the
server's report-status response, or
{:error, {:malformed_response, reason}} when the report is not
valid pkt-line framing.
@spec request_opts( t(), atom(), String.t(), [{String.t(), String.t()}], binary() | nil ) :: keyword()
Build the keyword list we'd pass to Req.request/1 for the given
request. Exposed publicly for test introspection; production code
goes through do_request/5.