Apero.Git (Apero v1.0.0)

Copy Markdown View Source

Git utilities for repository management, configuration and synchronisation.

Provides functionality for configuring Git, cloning repositories, synchronising changes and managing multiple projects.

Security

All user-supplied values (commit messages, branch names, file paths) are passed as argument lists — never interpolated into shell strings — to prevent shell injection attacks.

Summary

Functions

Stages files in a repository.

Shows who last modified each line of a file (blame).

Returns true if the given branch exists locally or remotely.

Checks out the main_branch of a repository, stashing uncommitted changes first if necessary.

Returns churn metrics — the most frequently changed files.

Clones a repository from url to path.

Clones a repository from url to target_path.

Creates a commit with the given message in a repository.

Gets a Git configuration value (local → global → system).

Gets a Git configuration value from the global scope.

Gets a Git configuration value from the local (repo) scope.

Lists files with merge conflicts in the repo.

Creates a GitHub PR via the GitHub CLI (gh).

Creates a GitLab MR via the GitLab CLI (glab).

Shows diff of changes.

Ensures a repository is cloned. If it already exists, updates it.

Returns a tree string of existing (already-cloned) repositories.

Fetches all remotes in a repository.

Fetches from origin in the given repository path.

Returns the currently checked-out branch name.

Returns the configured Git user email, or nil if not set.

Returns the configured Git user name, or nil if not set.

Returns the machine's hostname.

Returns the current system user email from the USER_EMAIL env variable, falling back to "user@domain.com".

Returns the current system user name.

Returns a map with the current Git user information.

Checks if the GitHub CLI (gh) is available.

Checks if the GitLab CLI (glab) is available.

Returns true if the repository has stash entries.

Returns true if there are uncommitted changes in the repository.

Lists open issues via the GitHub CLI (gh).

Shows commit history (log).

Marks a conflicted file as resolved (after manual fix).

Aborts a merge in progress.

Pulls from origin using the repository's main_branch.

Pulls from origin for the given branch (default: "main").

Sets Git user name, email, and URL rewrite rules globally.

Configures Git credentials (SSH key path or credential helper).

Stages all changes and creates a commit with the given message.

Stashes uncommitted changes if the working tree is dirty.

Pushes stash entries in a repository.

Synchronises a list of repositories (checkout → fetch → pull).

Updates an existing repository by fetching from origin.

Functions

add(repo_path, files)

@spec add(binary(), :all | [binary()] | binary()) ::
  :ok | {:ok, binary()} | {:error, any()}

Stages files in a repository.

Pass :all to stage everything, a list of paths, or a single path binary.

blame(repo_path, file)

@spec blame(binary(), binary()) :: {:ok, binary()} | {:error, binary()}

Shows who last modified each line of a file (blame).

branch_exists?(branch)

@spec branch_exists?(binary()) :: boolean()

Returns true if the given branch exists locally or remotely.

checkout(repo)

@spec checkout(map()) :: {:ok, map()} | {:error, any()}

Checks out the main_branch of a repository, stashing uncommitted changes first if necessary.

churn(repo_path \\ ".", opts \\ [])

@spec churn(
  binary(),
  keyword()
) :: {:ok, [map()]} | {:error, binary()}

Returns churn metrics — the most frequently changed files.

Runs git log --name-only for the given period and counts how many times each file has been modified. Results are sorted by churn count descending.

Options

  • :period — Time period for git log (default: "6.months")
  • :top — Return only the top N files (default: 20)
  • :branch — Git branch to analyze (default: current branch)

Examples

iex> Apero.Git.churn(".")
{:ok, [%{file: "lib/foo.ex", churn: 15}, ...]}

clone(repo)

@spec clone(map()) :: {:ok, map()} | {:error, any()}

Clones a repository from url to path.

clone_repository(url, target_path)

@spec clone_repository(binary(), binary()) :: {:ok, binary()} | {:error, any()}

Clones a repository from url to target_path.

commit(repo, message)

@spec commit(map(), binary()) :: {:ok, map()} | {:error, any()}

Creates a commit with the given message in a repository.

config(attr)

@spec config(binary()) :: binary()

Gets a Git configuration value (local → global → system).

config_global(attr)

@spec config_global(binary()) :: binary()

Gets a Git configuration value from the global scope.

config_local(attr)

@spec config_local(binary()) :: binary()

Gets a Git configuration value from the local (repo) scope.

conflict_files(repo_path \\ ".")

@spec conflict_files(binary()) :: {:ok, [binary()]} | {:error, binary()}

Lists files with merge conflicts in the repo.

create_gh_pr(title, base \\ "main", head \\ "HEAD", opts \\ [])

@spec create_gh_pr(binary(), binary(), binary(), keyword()) ::
  {:ok, binary()} | {:error, binary()}

Creates a GitHub PR via the GitHub CLI (gh).

create_glab_mr(title, source_branch, target_branch \\ "main", opts \\ [])

@spec create_glab_mr(binary(), binary(), binary(), keyword()) ::
  {:ok, binary()} | {:error, binary()}

Creates a GitLab MR via the GitLab CLI (glab).

diff(repo_path \\ ".", opts \\ [])

@spec diff(
  binary(),
  keyword()
) :: {:ok, binary()} | {:error, binary()}

Shows diff of changes.

ensure_clone(atom)

@spec ensure_clone(nil) :: {:error, :no_repo_defined}

Ensures a repository is cloned. If it already exists, updates it.

Accepts a single repo map, a list of repo maps, or nil.

ensure_clone(repos, workspace_path)

@spec ensure_clone([map()], binary()) :: [
  {:repo_exists | :repo_cloned, map()} | {:repo_error, map(), term()}
]
@spec ensure_clone(map(), binary()) ::
  {:repo_exists | :repo_cloned, map()} | {:repo_error, map(), term()}

existing_repos(repos)

@spec existing_repos([tuple()]) :: binary()

Returns a tree string of existing (already-cloned) repositories.

fetch(repo)

@spec fetch(map()) :: {:ok, map()} | {:error, any()}

Fetches all remotes in a repository.

fetch_origin(repo_path)

@spec fetch_origin(binary()) :: {:ok, binary()} | {:error, binary()}

Fetches from origin in the given repository path.

get_current_branch(repo_path)

@spec get_current_branch(binary()) :: {:ok, binary()} | {:error, binary()}

Returns the currently checked-out branch name.

get_git_user_email()

@spec get_git_user_email() :: binary() | nil

Returns the configured Git user email, or nil if not set.

get_git_user_name()

@spec get_git_user_name() :: binary() | nil

Returns the configured Git user name, or nil if not set.

get_hostname()

@spec get_hostname() :: binary()

Returns the machine's hostname.

get_system_user_email()

@spec get_system_user_email() :: binary()

Returns the current system user email from the USER_EMAIL env variable, falling back to "user@domain.com".

get_system_user_name()

@spec get_system_user_name() :: binary()

Returns the current system user name.

get_user_info()

@spec get_user_info() :: map()

Returns a map with the current Git user information.

Falls back to system user information if Git is not configured.

gh_available?()

@spec gh_available?() :: boolean()

Checks if the GitHub CLI (gh) is available.

glab_available?()

@spec glab_available?() :: boolean()

Checks if the GitLab CLI (glab) is available.

has_stash?(repo_path)

@spec has_stash?(binary()) :: boolean()

Returns true if the repository has stash entries.

has_uncommitted_changes?(repo_path)

@spec has_uncommitted_changes?(binary()) :: boolean()

Returns true if there are uncommitted changes in the repository.

list_issues(repo_path \\ ".", opts \\ [])

@spec list_issues(
  binary(),
  keyword()
) :: {:ok, binary()} | {:error, binary()}

Lists open issues via the GitHub CLI (gh).

log(repo_path \\ ".", opts \\ [])

@spec log(
  binary(),
  keyword()
) :: {:ok, binary()} | {:error, binary()}

Shows commit history (log).

mark_resolved(repo_path, file)

@spec mark_resolved(binary(), binary()) :: :ok | {:error, binary()}

Marks a conflicted file as resolved (after manual fix).

merge_abort(repo_path \\ ".")

@spec merge_abort(binary()) :: {:ok, binary()} | {:error, binary()}

Aborts a merge in progress.

pull(repo)

@spec pull(map()) :: {:ok, map()} | {:error, any()}

Pulls from origin using the repository's main_branch.

pull_origin(repo_path, branch \\ "main")

@spec pull_origin(binary(), binary()) :: {:ok, binary()} | {:error, binary()}

Pulls from origin for the given branch (default: "main").

set_user_info(name, email)

@spec set_user_info(binary(), binary()) :: :ok

Sets Git user name, email, and URL rewrite rules globally.

setup_credentials(opts \\ [])

@spec setup_credentials(keyword()) :: :ok | {:error, binary()}

Configures Git credentials (SSH key path or credential helper).

stage_and_commit(repo, message)

@spec stage_and_commit(map(), binary()) :: {:ok, map()} | {:error, any()}

Stages all changes and creates a commit with the given message.

stash_if_needed(repo_path)

@spec stash_if_needed(binary()) ::
  {:ok, {:stashed, binary()} | :clean} | {:error, any()}

Stashes uncommitted changes if the working tree is dirty.

Returns {:ok, {:stashed, branch}}, {:ok, :clean}, or {:error, reason}.

stash_push(repo_path, opts \\ [])

@spec stash_push(
  binary(),
  keyword()
) :: {:ok, binary()} | {:error, binary()}

Pushes stash entries in a repository.

Options

  • :message — stash description (default: "auto-stash")
  • :include_untracked — include untracked files (default: true)

sync(repos)

@spec sync([map()] | map()) :: :ok | {:ok, map()} | {:error, any()}

Synchronises a list of repositories (checkout → fetch → pull).

update_existing_repository(repo_path)

@spec update_existing_repository(binary()) :: {:ok, binary()} | {:error, any()}

Updates an existing repository by fetching from origin.