Presubmit.Commit (presubmit v0.2.2)

Copy Markdown View Source

A change set: the trees before and after, the per-file changes between them, and (for real commits) the metadata and message.

Build one with head/1, rev/2, staged/1, or new/1 for a synthetic change set in tests. All four produce the same struct, so assertions do not care where a commit came from.

Summary

Functions

Loads HEAD of the repository at opts[:repo] (default: the current directory).

Builds a synthetic change set from in-memory trees.

The same change set restricted to changes whose path matches pattern.

Loads the commit named by rev (any revision git understands).

Loads the staged index of the repository at opts[:repo] as a change set against HEAD, or against opts[:base] (any tree-ish) when given.

Loads the working directory of the repository at opts[:repo] as a change set against HEAD: staged and unstaged edits and untracked files alike.

Types

person()

@type person() :: %{name: String.t(), email: String.t(), date: DateTime.t()}

t()

@type t() :: %Presubmit.Commit{
  after: Presubmit.Tree.t(),
  author: person() | nil,
  base: String.t() | nil,
  before: Presubmit.Tree.t(),
  changes: [Presubmit.FileChange.t()],
  committer: person() | nil,
  elixir: Presubmit.Source.Diff.t() | nil,
  message: Presubmit.Message.t() | nil,
  parents: [String.t()],
  repo: Path.t() | nil,
  sha: String.t() | nil,
  source: :head | :rev | :staged | :worktree | :synthetic
}

Functions

head(opts \\ [])

@spec head(keyword()) :: t()

Loads HEAD of the repository at opts[:repo] (default: the current directory).

new(opts)

@spec new(keyword()) :: t()

Builds a synthetic change set from in-memory trees.

Options

  • :before — map of path to contents for the parent tree (default: empty).
  • :after — map of path to contents for the resulting tree (required).
  • :renames — list of {old_path, new_path} pairs. Without this, a moved file is reported as a deletion plus an addition, exactly as git would without rename detection.
  • :message — raw commit message; omit for a message-less change set.

Examples

Commit.new(
  before: %{"lib/a.ex" => "defmodule A do\nend\n"},
  after: %{"lib/a.ex" => "defmodule A do\n  def x, do: 1\nend\n"},
  message: "[a] add x/0"
)

restrict(commit, pattern)

@spec restrict(t(), Presubmit.Pattern.t()) :: t()

The same change set restricted to changes whose path matches pattern.

The before and after trees are left whole, so tree-wide invariants still see the whole repository; only the changes — and the structural diff derived from them — are narrowed.

rev(rev, opts \\ [])

@spec rev(String.t(), keyword()) :: t()

Loads the commit named by rev (any revision git understands).

Raises Presubmit.MergeCommitError for merge commits and Presubmit.ShallowCloneError when the parent is not present locally.

staged(opts \\ [])

@spec staged(keyword()) :: t()

Loads the staged index of the repository at opts[:repo] as a change set against HEAD, or against opts[:base] (any tree-ish) when given.

The result has no sha or message; message assertions raise Presubmit.NoMessageError. On an unborn branch the before tree is empty.

base: is what an amend needs: measured against HEAD^, the index is the commit that git commit --amend will produce.

worktree(opts \\ [])

@spec worktree(keyword()) :: t()

Loads the working directory of the repository at opts[:repo] as a change set against HEAD: staged and unstaged edits and untracked files alike.

Like staged/1, the result has no sha or message and accepts base:. The repository's index is not touched (see Presubmit.Git.write_worktree_tree/1).