Trebejo.Git.Local (Trebejo v2.0.0)

Copy Markdown View Source

Local Git operations — clone, commit, pull, push, branch, stash, merge, etc.

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. Arguments are shell-quoted before being joined into the final command line, so values containing whitespace, quotes or shell metacharacters are safe.

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.

Builds churn arguments for git log.

Builds git options for a repository path and optional timeout.

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

Cherry-picks a commit.

Returns churn metrics — the most frequently changed files.

Clones a repository from url to path.

Clones a repository from a URL to a target path.

Creates a commit with the given message in a repository.

Commits all staged changes.

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.

Checks if a repository contains no commits.

Shows diff of changes.

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

Returns a tree string of existing (already-cloned) repositories from a list of {:repo_exists | :repo_error, map(), ...} tuples.

Fetches all remotes in a repository.

Fetches all repository changes from origin.

Fetches from origin in the given repository path.

Fetches repository updates from origin.

Gets the commit count for a repository.

Returns the currently checked-out branch name.

Gets the current commit hash for a repository.

Gets the first commit hash for a repository.

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.

Gets the short (7-char) commit hash for HEAD.

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.

Gets the current working directory.

Checks if gh is available.

Checks if glab is available.

Returns true if the repository has stash entries.

Returns true if there are uncommitted changes in the repository.

Returns the list of all local branches.

Shows commit history (log).

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

Merges a branch into the current branch.

Aborts a merge in progress.

Merges a commit into the current branch.

Pulls from origin using the repository's main_branch.

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

Rebase a branch onto another.

Reverts a commit.

Runs a git command with options.

Runs a system command with telemetry instrumentation.

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

Sets up a credential helper for git.

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

Sets up an SSH key for git.

Quotes a binary string for safe shell use.

Squashes commits into one.

Stages all changes.

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

Stashes uncommitted changes if the working tree is dirty.

Restores stashed changes.

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.

build_churn_args(opts)

@spec build_churn_args(keyword()) :: [binary()]

Builds churn arguments for git log.

build_git_opts(repo_path, timeout)

@spec build_git_opts(binary(), integer() | nil) :: keyword()

Builds git options for a repository path and optional timeout.

checkout(repo)

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

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

cherry_pick(repo_path, commit_hash)

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

Cherry-picks a commit.

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 or max commits and counts how many times each file has been modified. Results are sorted by churn count descending.

When include_authors: true, uses --format=COMMIT:%an to track which authors touched each file, so callers can compute risk scores like churn * (1 + log(num_authors)).

Options

  • :period — Time period for git log (default: "6.months"). Ignored if :max_commits is set.
  • :max_commits — Limit to the last N commits (uses --max-count). Overrides :period.
  • :no_merges — Exclude merge commits via --no-merges (default: false)
  • :top — Return only the top N files (default: 20)
  • :branch — Git branch to analyze (default: current branch)
  • :include_authors — Track distinct authors per file (default: false). When true, each result includes :authors (list of author names).
  • :timeout — Command timeout in milliseconds (default: nil, meaning Arrea's default). Passed to Arrea.Command.execute/2.

Examples

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

iex> Trebejo.Git.Local.churn(".", max_commits: 1000, no_merges: true, include_authors: true)
{:ok, [%{file: "lib/foo.ex", churn: 15, authors: ["Alice", "Bob"]}, ...]}

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 a URL to a target path.

commit(repo, message)

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

Creates a commit with the given message in a repository.

commit_all(repo_path, message)

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

Commits all staged changes.

config(attr, opts \\ [])

@spec config(
  binary(),
  keyword()
) :: binary()

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

Accepts an optional cd: option to scope the lookup to a specific repository directory.

config_global(attr, opts \\ [])

@spec config_global(
  binary(),
  keyword()
) :: binary()

Gets a Git configuration value from the global scope.

Accepts an optional cd: option (kept for signature parity with config/2; ignored at the global scope).

config_local(attr, opts \\ [])

@spec config_local(
  binary(),
  keyword()
) :: binary()

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

Accepts an optional cd: option to scope the lookup to a specific repository directory.

conflict_files(repo_path \\ ".")

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

Lists files with merge conflicts in the repo.

contains_no_commits?(err)

@spec contains_no_commits?(any()) :: boolean()

Checks if a repository contains no commits.

create_branch(repo_path, branch_name)

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

Creates a new branch.

delete_branch(repo_path, branch_name)

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

Deletes a branch.

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 from a list of {:repo_exists | :repo_error, map(), ...} tuples.

fetch(repo)

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

Fetches all remotes in a repository.

fetch_all_changes(repo_path)

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

Fetches all repository changes from origin.

fetch_origin(repo_path)

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

Fetches from origin in the given repository path.

fetch_repository_updates(repo_path)

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

Fetches repository updates from origin.

get_commit_count(repo_path)

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

Gets the commit count for a repository.

get_current_branch(repo_path)

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

Returns the currently checked-out branch name.

get_current_commit(repo_path)

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

Gets the current commit hash for a repository.

get_first_commit(repo_path)

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

Gets the first commit hash for a repository.

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_short_commit(repo_path)

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

Gets the short (7-char) commit hash for HEAD.

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.

get_working_directory()

@spec get_working_directory() :: binary()

Gets the current working directory.

gh_available?()

@spec gh_available?() :: boolean()

Checks if gh is available.

glab_available?()

@spec glab_available?() :: boolean()

Checks if 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_local_branches(repo_path)

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

Returns the list of all local branches.

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(repo_path, branch)

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

Merges a branch into the current branch.

merge_abort(repo_path \\ ".")

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

Aborts a merge in progress.

merge_commit(repo_path, commit_hash)

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

Merges a commit into the current branch.

parse_churn_output(output, include_authors?)

@spec parse_churn_output(binary(), boolean()) :: [binary()]

Parses churn output.

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").

rebase(repo_path, branch)

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

Rebase a branch onto another.

revert(repo_path, commit_hash)

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

Reverts a commit.

run_git(git_args, opts \\ [])

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

Runs a git command with options.

run_system_cmd(cmd, telemetry_cmd_line, opts \\ [])

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

Runs a system command with telemetry instrumentation.

set_user_info(name, email)

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

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

setup_credential_helper(credential_helper)

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

Sets up a credential helper for git.

setup_credentials(opts \\ [])

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

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

setup_ssh_key(ssh_key)

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

Sets up an SSH key for git.

shell_quote(str)

@spec shell_quote(binary()) :: binary()

Quotes a binary string for safe shell use.

squash(repo_path, commit_hash)

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

Squashes commits into one.

stage_all(repo_path)

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

Stages all changes.

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_pop(repo_path)

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

Restores stashed changes.

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()]) :: :ok
@spec sync(map()) :: {: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.