# `Git.Changes`
[🔗](https://github.com/joshrotenberg/git_wrapper_ex/blob/main/lib/git/changes.ex#L1)

Higher-level change analysis helpers that compose lower-level `Git` functions.

Provides structured views of file changes between refs, uncommitted work,
merge conflicts, and change summaries.

All functions accept an optional keyword list. Use `:config` to specify the
repository via a `Git.Config` struct; when omitted a default config is built
from the environment.

# `between`

```elixir
@spec between(String.t(), String.t(), keyword()) :: {:ok, [map()]} | {:error, term()}
```

Returns structured file changes between two refs.

Uses `git diff --name-status ref1 ref2` to get per-file status. Each entry
is a map with `:status`, `:path`, and `:old_path` (non-nil for renames and
copies).

## Options

  * `:config` - a `Git.Config` struct
  * `:stat` - when `true`, also includes line-level stats per file

## Examples

    {:ok, changes} = Git.Changes.between("v1.0.0", "v2.0.0")
    hd(changes).status  #=> :added
    hd(changes).path    #=> "lib/new_file.ex"

# `conflicts`

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

Detects files with merge conflicts.

Uses `git ls-files --unmerged` to reliably detect conflicted files, then
deduplicates paths since each conflicted file appears multiple times in
the unmerged listing.

## Options

  * `:config` - a `Git.Config` struct

## Examples

    {:ok, conflicts} = Git.Changes.conflicts()
    conflicts  #=> ["lib/conflicted.ex"]

# `staged`

```elixir
@spec staged(keyword()) :: {:ok, Git.Diff.t()} | {:error, term()}
```

Returns only staged changes as a diff.

Uses `Git.diff(staged: true, stat: true)` to show what is in the index
but not yet committed.

Returns `{:ok, Git.Diff.t()}`.

# `stats`

```elixir
@spec stats(keyword()) ::
  {:ok,
   %{
     files_changed: non_neg_integer(),
     insertions: non_neg_integer(),
     deletions: non_neg_integer()
   }}
  | {:error, term()}
```

Returns summary statistics of unstaged working tree changes.

Uses `Git.diff(stat: true)` and extracts file count, insertions, and
deletions into a simple map.

Returns `{:ok, %{files_changed: n, insertions: n, deletions: n}}`.

# `summary`

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

Returns a one-call summary of changes between two refs.

Includes the number of files changed, total insertions and deletions,
and per-file details.

## Options

  * `:config` - a `Git.Config` struct

## Examples

    {:ok, summary} = Git.Changes.summary("v1.0.0", "v2.0.0")
    summary.files_changed  #=> 3
    summary.insertions     #=> 42
    summary.deletions      #=> 10

# `uncommitted`

```elixir
@spec uncommitted(keyword()) :: {:ok, map()} | {:error, term()}
```

Returns structured info about all uncommitted changes.

Groups status entries into `:staged` (files in the index), `:modified`
(tracked files with working tree changes), and `:untracked` files.

## Options

  * `:config` - a `Git.Config` struct

## Examples

    {:ok, uncommitted} = Git.Changes.uncommitted()
    uncommitted.staged    #=> [%{path: "lib/foo.ex", status: :modified}]
    uncommitted.untracked #=> ["new_file.ex"]

# `unstaged`

```elixir
@spec unstaged(keyword()) :: {:ok, Git.Diff.t()} | {:error, term()}
```

Returns only unstaged changes as a diff.

Uses `Git.diff(stat: true)` to show working tree changes that have not
been staged.

Returns `{:ok, Git.Diff.t()}`.

---

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