Exgit.Object.Tree (exgit v0.1.0)

Copy Markdown View Source

A git tree object.

decode/1 is byte-exact: it preserves the mode string verbatim so that decode |> encode reproduces the original tree bytes (and thus the same SHA). Historical git repositories occasionally contain legacy modes such as 100664; normalizing those during decode would silently change the tree's SHA and corrupt verification.

new/1 applies git's canonical ordering (dirs sort as if they had a trailing /) and — by default — normalizes modes via canonical_mode/1. Pass strict: true to raise on non-canonical modes instead of silently coercing, or build the struct directly for a raw, unvalidated tree.

Round-trip note

Tree.new(decoded.entries) is NOT always equivalent to decodednew/1 canonicalizes modes, so a decoded tree with legacy 100664 mode will have its mode rewritten by new/1 and its SHA will change. If you need byte-exact round-trip, preserve the decoded struct and don't pass it through new/1.

Path traversal

decode/1 validates each entry's name against the rules in Exgit.RefName for path components — rejecting /, .., ., empty names, and case-insensitive .git/.gitmodules entries. Hostile trees from a pack never reach a materialize/checkout/write path.

Summary

Functions

Normalize a mode string to the canonical git spelling. Used by new/1 but NOT by decode/1. Octal modes canonicalize by their file-type bits: directories ("040000""40000"), symlinks ("0120000""120000") and gitlinks ("0160000""160000") keep their type; everything else coerces to 100644 / 100755 based on the executable bit. Non-octal strings are returned unchanged by the one-arg form; the two-arg form with strict: true raises ArgumentError on any non-canonical input.

Build a canonical tree from a list of {mode, name, sha} entries.

Types

entry()

@type entry() :: {mode(), name :: String.t(), sha :: binary()}

mode()

@type mode() :: String.t()

t()

@type t() :: %Exgit.Object.Tree{entries: [entry()]}

Functions

canonical_mode(mode)

@spec canonical_mode(String.t()) :: String.t()

Normalize a mode string to the canonical git spelling. Used by new/1 but NOT by decode/1. Octal modes canonicalize by their file-type bits: directories ("040000""40000"), symlinks ("0120000""120000") and gitlinks ("0160000""160000") keep their type; everything else coerces to 100644 / 100755 based on the executable bit. Non-octal strings are returned unchanged by the one-arg form; the two-arg form with strict: true raises ArgumentError on any non-canonical input.

canonical_mode(mode, strict)

@spec canonical_mode(String.t(), boolean()) :: String.t() | no_return()

decode(bytes)

@spec decode(binary()) :: {:ok, t()} | {:error, term()}

encode(tree)

@spec encode(t()) :: iolist()

new(entries, opts \\ [])

@spec new(
  [entry()],
  keyword()
) :: t()

Build a canonical tree from a list of {mode, name, sha} entries.

Options:

  • :strict — when true, raise ArgumentError on any mode that is not one of the five canonical git modes (40000, 100644, 100755, 120000, 160000). Default: false, which canonicalizes via canonical_mode/1: zero-padded modes normalize to their canonical form ("040000""40000", "0120000""120000"), other octal modes coerce to 100644 / 100755 based on the executable bit, and non-octal strings pass through unchanged.

sha(tree)

@spec sha(t()) :: Exgit.Object.sha()

sha_hex(tree)

@spec sha_hex(t()) :: String.t()