# `PromptRunner.Git`
[🔗](https://github.com/nshkrdotcom/prompt_runner_sdk/blob/v0.12.1/lib/prompt_runner/git.ex#L1)

Git commit helpers for single-repo and multi-repo prompt runs, plus the
read-only repository inspection used by the verifier and by `watch`.

Everything in the inspection surface uses `git -C <root>` rather than the
`:cd` option so a path that does not exist reports a git error instead of
raising, and nothing in it writes: not the working tree, not the index, not a
local branch, and not a remote-tracking ref.

# `source`

```elixir
@type source() :: PromptRunner.Plan.t() | PromptRunner.Config.t()
```

# `cmd`

```elixir
@spec cmd(String.t(), [String.t()]) :: {String.t(), non_neg_integer()}
```

Runs a git command in `root` and returns `{output, exit_code}`.

# `commit_count`

```elixir
@spec commit_count(String.t()) :: non_neg_integer() | nil
```

Returns the number of commits reachable from HEAD, or nil when unavailable.

# `commit_multi_repo`

```elixir
@spec commit_multi_repo(source(), String.t(), [{String.t(), String.t()}]) :: [
  {String.t(), term()}
]
```

# `commit_repo`

```elixir
@spec commit_repo(String.t(), String.t(), String.t(), String.t()) ::
  {:ok, String.t()} | {:skip, atom()} | {:error, atom()}
```

# `commit_single_repo`

```elixir
@spec commit_single_repo(source(), String.t(), String.t(), String.t() | nil) ::
  {:ok, String.t()} | {:skip, atom()} | {:error, atom()}
```

# `ls_remote`

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

Resolves `ref` on `remote` and returns its object id, under a bounded timeout.

`git ls-remote` is a pure query: it opens a connection, reads the remote's ref
advertisement, and writes nothing. Unlike `git fetch` it does not create or
move remote-tracking refs, so a verify clause built on it cannot alter the
repository it is judging — a gate that mutates anything in its subject is a
gate that can change the thing it measures.

The timeout matters because a verify clause runs after the model work is
already spent: an unreachable remote must not hang the run.

Returns `{:error, :ref_absent}` when the remote has no such ref.

# `status_lines`

```elixir
@spec status_lines(String.t()) :: {:ok, [String.t()]} | {:error, String.t()}
```

Returns the trimmed `git status --porcelain` lines for `root`.

An empty list means the working tree is clean.

# `upstream_ref`

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

Returns the upstream ref for the current branch (for example `origin/main`),
or nil when the branch has no upstream configured.

# `value`

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

Runs a git command in `root` and returns its trimmed output, or nil on failure.

# `worktree?`

```elixir
@spec worktree?(String.t()) :: boolean()
```

Returns true when `root` is inside a git work tree.

---

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