# `Exgit.Config`
[🔗](https://github.com/ivarvong/exgit/blob/v0.1.0/lib/exgit/config.ex#L1)

Parser and emitter for git-style INI configuration files.

`parse/1` accepts arbitrary input — caller- or filesystem-supplied
text — and returns a tagged `{:ok, _} | {:error, _}` result. It
never raises on untrusted input.

## Threat model

`.git/config` is treated as **caller-controlled** input, not
remote-controlled. Exgit does not fetch or persist config received
over the wire, and it does not **act on** any config value:

  * no `core.sshCommand` / `core.fsmonitor` / `core.hookspath` —
    there is no code path that executes a command out of config
  * no `http.proxy` — Req's proxy comes from env or the explicit
    transport opts, not from config
  * no `insteadOf` / `pushInsteadOf` URL rewriting
  * no `include` / `includeIf` expansion — config files are read
    as-is; `[include] path=...` entries are parsed but ignored
  * no `~/` or `${VAR}` path expansion — paths in config values
    are treated as opaque strings

This keeps the blast radius of a hostile `.git/config` to "data
the caller reads back out of `repo.config` and uses themselves."
A consumer who reads e.g. `Config.get(config, "core", "sshCommand")`
and hands it to `System.cmd/2` is outside exgit's trust boundary.

**If submodule support is added**, `.gitmodules` URLs become a
remote-controlled surface and will need separate validation —
refusing `file://`, `ssh://user@host/…;command`, and any URL
containing shell-metacharacters. Not currently present.

# `section_key`

```elixir
@type section_key() :: {String.t(), String.t() | nil}
```

# `t`

```elixir
@type t() :: %Exgit.Config{sections: [{section_key(), [{String.t(), String.t()}]}]}
```

# `add`

```elixir
@spec add(t(), String.t(), String.t() | nil, String.t(), String.t()) :: t()
```

Append another value for `key` without replacing existing ones.

Unlike `set/5` (which replaces), `add/5` preserves existing values.
This is the equivalent of `git config --add section.key value` and is
required for multi-valued keys like `remote.<n>.fetch`.

# `encode`

```elixir
@spec encode(t()) :: iolist()
```

# `get`

```elixir
@spec get(t(), String.t(), String.t() | nil, String.t()) :: String.t() | nil
```

# `get_all`

```elixir
@spec get_all(t(), String.t(), String.t() | nil, String.t()) :: [String.t()]
```

# `new`

```elixir
@spec new() :: t()
```

# `parse`

```elixir
@spec parse(String.t()) :: {:ok, t()} | {:error, term()}
```

# `read`

```elixir
@spec read(Path.t()) :: {:ok, t()} | {:error, term()}
```

# `set`

```elixir
@spec set(t(), String.t(), String.t() | nil, String.t(), String.t()) :: t()
```

# `write`

```elixir
@spec write(t(), Path.t()) :: :ok | {:error, term()}
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
