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

Higher-level branch workflow operations that compose multiple lower-level
`Git` commands.

All functions accept an optional keyword list. The `:config` key, when
present, must be a `Git.Config` struct and is forwarded to every underlying
git invocation.

# `cleanup_merged`

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

Finds branches merged into a target and deletes them.

## Options

  * `:target` - branch to check against (default: current branch)
  * `:force` - use force delete, `-D` (default: `false`)
  * `:exclude` - list of branch names to never delete
    (default: `["main", "master", "develop"]`)
  * `:dry_run` - just return the list without deleting (default: `false`)

The current branch and any branches in the `:exclude` list are always
skipped.

Returns `{:ok, [String.t()]}` with the list of deleted (or would-be-deleted)
branch names.

# `create_and_checkout`

```elixir
@spec create_and_checkout(
  String.t(),
  keyword()
) :: {:ok, Git.Checkout.t()} | {:error, term()}
```

Creates a new branch and checks it out in one call.

Equivalent to `git checkout -b <branch_name>`.

Returns `{:ok, Git.Checkout.t()}` on success.

# `current`

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

Returns the name of the current branch.

Uses `git rev-parse --abbrev-ref HEAD`.

Returns `{:ok, String.t()}` on success.

# `delete_branch`

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

Deletes a branch locally and, when `:remote` is given, on the remote too.

## Options

  * `:remote` - remote to also delete the branch from, e.g. `"origin"`;
    `true` is shorthand for `"origin"`. When omitted, only the local branch is
    deleted.
  * `:force` - use `-D` instead of `-d` for the local delete (default `false`)
  * `:config` - a `Git.Config` struct

Returns `{:ok, :done}` when the requested deletions succeed.

# `delete_squashed`

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

Finds local branches whose changes were squash-merged into a target and
deletes them.

A squash-merge collapses a branch's commits into a single new commit on the
target, so the branch never becomes an ancestor of the target and
`git branch --merged` (and therefore `cleanup_merged/1`) never reports it.
This uses the standard cherry-based heuristic instead: for each candidate
branch `b`, it builds a synthetic commit carrying `b`'s tree on top of the
merge-base with the target, then asks `git cherry` whether an equivalent
change already exists in the target. If it does, `b` was squash-merged.

Because detection is a heuristic and deletion uses `git branch -D` (force),
this defaults to `:dry_run` `true`: it returns the branches that *would* be
deleted and touches nothing. Pass `dry_run: false` to actually delete.

## Options

  * `:target` - branch to check against (default: the current branch)
  * `:exclude` - branch names to never delete
    (default: `["main", "master", "develop"]`)
  * `:dry_run` - return the branches that would be deleted without deleting
    them (default: `true`)
  * `:config` - a `Git.Config` struct

The current branch and the target branch are always skipped.

Returns `{:ok, deleted_names}` (or the would-be-deleted names under
`:dry_run`).

# `divergence`

```elixir
@spec divergence(String.t(), String.t(), keyword()) ::
  {:ok, %{ahead: non_neg_integer(), behind: non_neg_integer()}}
  | {:error, term()}
```

Returns ahead/behind counts between two branches.

Uses `git rev-list --count --left-right branch1...branch2`.

Returns `{:ok, %{ahead: non_neg_integer(), behind: non_neg_integer()}}`.

# `exists?`

```elixir
@spec exists?(
  String.t(),
  keyword()
) :: {:ok, boolean()} | {:error, term()}
```

Checks whether a branch exists locally.

Uses `git rev-parse --verify refs/heads/<branch_name>`.

Returns `{:ok, true}` when the branch exists, `{:ok, false}` otherwise.

# `merged`

```elixir
@spec merged(keyword()) :: {:ok, [Git.Branch.t()]} | {:error, term()}
```

Lists branches that have been merged into the current branch or a specified
target.

## Options

  * `:target` - branch to check against (default: current branch)

Returns `{:ok, [Git.Branch.t()]}`.

# `no_merged`

```elixir
@spec no_merged(keyword()) :: {:ok, [Git.Branch.t()]} | {:error, term()}
```

Lists branches that have NOT been merged into the current branch or a
specified target.

## Options

  * `:target` - branch to check against (default: current branch)

Returns `{:ok, [Git.Branch.t()]}`.

# `prune_gone`

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

Deletes local branches whose upstream tracking branch was deleted on the
remote.

Fetches with `--prune` (so gone upstreams are detected), then deletes every
local branch reported as `[gone]`. This is orthogonal to `cleanup_merged/1`:
a gone branch may still be unmerged, and a merged branch may still have a live
upstream.

## Options

  * `:remote` - remote to prune against (default `"origin"`)
  * `:exclude` - branch names to never delete (default `["main", "master", "develop"]`)
  * `:force` - use `-D` instead of `-d` (default `false`)
  * `:dry_run` - return the branches that would be deleted without deleting
    (default `false`)
  * `:config` - a `Git.Config` struct

Returns `{:ok, deleted_names}` (or the would-be-deleted names under
`:dry_run`). The current branch is always skipped.

# `recent`

```elixir
@spec recent(keyword()) ::
  {:ok,
   [
     %{
       name: String.t(),
       date: String.t(),
       author: String.t(),
       subject: String.t()
     }
   ]}
  | {:error, term()}
```

Lists branches sorted by most recent commit.

## Options

  * `:count` - maximum number of branches to return (default: `10`)

Uses `git for-each-ref` with `--sort=-committerdate`.

Returns `{:ok, [%{name: String.t(), date: String.t(), author: String.t(), subject: String.t()}]}`.

# `rename`

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

Renames a branch.

Uses `git branch -m <old_name> <new_name>`.

Returns `{:ok, :done}` on success.

---

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