# `Minga.Git`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/git.ex#L1)

Git operations, delegated to a configurable backend.

In production, uses `Minga.Git.System` which shells out to the `git`
CLI. In tests, swap to `Minga.Git.Stub` to avoid spawning OS processes:

    config :minga, git_module: Minga.Git.Stub

# `diff_opt`

```elixir
@type diff_opt() :: {:path, String.t()} | {:staged, boolean()} | {:commit, String.t()}
```

Accepted diff options. Unknown keys, duplicate keys, and invalid values are rejected. `:commit` may be combined with `:staged`, but only when `:staged` is `false`.

# `diff_opts`

```elixir
@type diff_opts() :: [diff_opt()]
```

# `log_entry`

```elixir
@type log_entry() :: Minga.Git.LogEntry.t()
```

A structured log entry.

# `stash_entry`

```elixir
@type stash_entry() :: Minga.Git.StashEntry.t()
```

A structured stash entry.

# `status_entry`

```elixir
@type status_entry() :: Minga.Git.StatusEntry.t()
```

A structured status entry for one file.

# `status_opt`

```elixir
@type status_opt() ::
  {:untracked_mode, untracked_mode()} | {:timeout_ms, pos_integer()}
```

Accepted `status/2` options.

# `status_opts`

```elixir
@type status_opts() :: [status_opt()]
```

# `untracked_mode`

```elixir
@type untracked_mode() :: :no | :normal | :all
```

How much untracked-file detail `git status` should enumerate.

# `ahead_behind`

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

Returns ahead/behind counts relative to the upstream tracking branch.

# `blame_line`

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

Gets blame information for a specific line of a file.

Returns `{:ok, blame_text}` with a human-readable blame string,
or `:error` if blame fails.

# `branch_create`

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

Creates a new branch and checks it out.

# `branch_delete`

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

Deletes a branch.

# `branch_list`

```elixir
@spec branch_list(String.t()) ::
  {:ok, [Minga.Git.BranchInfo.t()]} | {:error, String.t()}
```

Lists all branches (local and remote).

# `branch_switch`

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

Switches to an existing branch.

# `commit`

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

Creates a commit with the given message.

Options: `:amend` (boolean, default false) to amend the previous commit.

# `conflict_count`

```elixir
@spec conflict_count(GenServer.server()) :: non_neg_integer()
```

Returns the parsed merge conflict count for a tracked buffer.

# `conflicts`

```elixir
@spec conflicts(GenServer.server()) :: [Minga.Git.MergeConflict.Region.t()]
```

Returns parsed merge conflict regions for a tracked buffer.

# `current_branch`

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

Returns the current branch name for a git repository.

Returns `{:ok, branch_name}` or `:error` if it can't be determined
(e.g., detached HEAD, not a git repo).

# `diff`

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

Returns the diff for a specific file or all changes.

Options: `:path` (file path), `:staged` (boolean, default false), `:commit` (commit hash).
`:commit` may be combined with `:staged, false`; commit diffs can still be narrowed with `:path`.

# `diff_lines`

```elixir
@spec diff_lines([String.t()], [String.t()]) :: [Minga.Core.Diff.hunk()]
```

Computes line-level diff hunks between two lists of lines.

# `discard`

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

Discards working tree changes for a file. Destructive and irreversible.

For tracked files, runs `git checkout -- <path>`.
For untracked files, deletes the file.

# `fetch_remotes`

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

Fetches from all remotes.

# `gutter_signs`

```elixir
@spec gutter_signs(GenServer.server()) :: %{required(non_neg_integer()) =&gt; atom()}
```

Returns gutter sign indicators (line → hunk type) for a tracked buffer.

# `hunk_at`

```elixir
@spec hunk_at(GenServer.server(), non_neg_integer()) :: Minga.Core.Diff.hunk() | nil
```

Returns the hunk at a specific line, or nil.

# `hunks`

```elixir
@spec hunks(GenServer.server()) :: [Minga.Core.Diff.hunk()]
```

Returns all diff hunks for a tracked buffer.

# `last_commit_message`

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

Returns the message of the most recent commit.

# `log`

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

Returns recent commits as structured entries.

Options: `:count` (default 10), `:path` (limit to file).

# `lookup_repo`

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

Finds the Repo process for a git root path, or nil if not started.

# `modeline_info`

```elixir
@spec modeline_info(GenServer.server()) :: Minga.Git.Buffer.modeline_info()
```

Returns modeline-ready git info (branch, hunk counts) for a tracked buffer.

# `pull`

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

Pulls from the upstream remote (fetch + merge).

# `push`

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

Pushes the current branch to its upstream remote.

# `refresh_repo`

```elixir
@spec refresh_repo(pid()) :: :ok
```

Triggers a background refresh of the Repo's cached git state.

# `relative_path`

```elixir
@spec relative_path(String.t(), String.t()) :: String.t()
```

Returns the path of a file relative to the git root.

# `repo_status`

```elixir
@spec repo_status(pid()) :: [status_entry()]
```

Returns cached status entries from a running Repo process.

# `repo_summary`

```elixir
@spec repo_summary(pid()) :: map()
```

Returns a summary map (branch, ahead/behind, file counts) from a Repo.

# `revert_hunk`

```elixir
@spec revert_hunk([String.t()], Minga.Core.Diff.hunk()) :: [String.t()]
```

Reverts a single hunk, restoring the base content for those lines.

# `root_for`

```elixir
@spec root_for(String.t()) :: {:ok, String.t()} | :not_git
```

Finds the git repository root for a file path.

Returns `{:ok, root_path}` if the file is inside a git repo, or
`:not_git` if it isn't.

# `show_head`

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

Reads the HEAD version of a file from git.

Returns `{:ok, content}` with the file content at HEAD, or `:error`
if the file doesn't exist in HEAD (new file, not tracked, etc.).

# `show_staged`

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

Reads the staged (index) version of a file from git.

Returns `{:ok, content}` with the file content in the index, or `:error`
if the file is not staged.

# `stage`

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

Stages specific files (equivalent to `git add`).

# `stage_patch`

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

Applies a unified diff patch to the git index (staging area).

# `stash`

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

Saves current worktree changes to a stash.

# `stash_drop`

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

Drops a stash by index.

# `stash_list`

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

Lists stashes for a repository.

# `stash_pop`

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

Pops the most recent stash.

# `status`

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

Returns a structured list of changed files with their status.

# `sync_tracked_buffer`

```elixir
@spec sync_tracked_buffer(pid(), String.t()) :: :ok
```

Synchronizes a tracked buffer cache with the provided content.

# `tracking_pid`

```elixir
@spec tracking_pid(pid()) :: pid() | nil
```

Returns the git tracking process for a buffer, or nil if untracked.

Composes the Tracker lookup so callers don't need to know about
the Tracker → Buffer two-step.

# `unstage`

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

Unstages specific files from the index (equivalent to `git reset HEAD -- <paths>`).

# `unstage_all`

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

Unstages all staged files (equivalent to `git reset HEAD`).

---

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