Exgit.Diff (exgit v0.1.0)

Copy Markdown View Source

Compare two git trees and return a list of changes.

Changes are returned as maps with explicit keys so callers can pattern match on the operation, path, modes, and SHAs:

%{op: :added,             path: String.t(), new_mode: String.t(), new_sha: binary()}
%{op: :removed,           path: String.t(), old_mode: String.t(), old_sha: binary()}
%{op: :modified,          path: String.t(), old_mode: String.t(), new_mode: String.t(),
                          old_sha: binary(), new_sha: binary()}
%{op: :mode_changed,      path: String.t(), old_mode: String.t(), new_mode: String.t(),
                          old_sha: binary(), new_sha: binary()}
%{op: :submodule_change,  path: String.t(), old_sha: binary(), new_sha: binary()}
%{op: :type_changed,      path: String.t(), old_mode: String.t(), new_mode: String.t(),
                          old_sha: binary(), new_sha: binary()}

Options

  • :prefix — path prefix for the produced change entries. Default "".
  • :max_depth — maximum tree recursion depth. Protects against a hostile tree with a circular reference or a pathological nesting that would overflow the stack. Default 256 (git itself caps around 4096).
  • :max_changes — cap the number of change entries. Prevents a single Diff.trees call from producing millions of entries on a hostile input. Default nil (unbounded; caller takes responsibility).

Defense in depth

Diff.trees/4 is the one path that walks arbitrary (possibly remote-sourced) tree graphs recursively. It must not overflow the stack or loop forever on a tree object that references itself (directly or indirectly). The :max_depth cap is the guard; a tree that exceeds it returns {:error, {:max_depth_exceeded, n}} so callers can distinguish "legitimately deep" from "hostile".

Summary

Types

change()

@type change() :: %{:op => atom(), :path => String.t(), optional(atom()) => term()}

Functions

trees(repo, tree_a_sha, tree_b_sha, opts \\ [])

@spec trees(term(), binary() | nil, binary() | nil, keyword()) ::
  {:ok, [change()]} | {:error, term()}