Current section
Files
Jump to
Current section
Files
lib/check/git.ex
defmodule Check.Git do
@moduledoc "Helpers for resolving git diff ranges relative to the base branch."
# SHA of git's empty tree object - used to diff a root commit that has no parent.
@empty_tree "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
@doc """
Git revision range (as diff args) for committed changes relative to `base_branch`.
Three-dot `base...HEAD` collapses to nothing when the current branch *is* the
base branch, so running on `main` would never report any changes. On the base
branch we instead compare against the latest commit.
- feature branch: `base...HEAD` (commits made since the base branch)
- base branch: `HEAD~1...HEAD` (the latest commit), or the empty tree on a root commit
Working-tree/uncommitted changes are never included.
## Examples
iex> Check.Git.committed_diff_range("main")
["main...HEAD"]
"""
@spec committed_diff_range(String.t()) :: [String.t()]
def committed_diff_range(base_branch) do
if current_branch() == base_branch do
if parent_commit?(), do: ["HEAD~1...HEAD"], else: [@empty_tree, "HEAD"]
else
["#{base_branch}...HEAD"]
end
end
@doc "Name of the currently checked-out branch, or `nil` if it cannot be resolved."
@spec current_branch() :: String.t() | nil
def current_branch do
case System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"], stderr_to_stdout: true) do
{output, 0} -> String.trim(output)
_ -> nil
end
end
@doc "True when `HEAD` has a parent commit (i.e. it is not the root commit)."
@spec parent_commit?() :: boolean()
def parent_commit? do
match?(
{_, 0},
System.cmd("git", ["rev-parse", "--verify", "--quiet", "HEAD~1"], stderr_to_stdout: true)
)
end
end