Revision history for Git-Native

0.004     2026-08-09 20:34:42Z
  - Repository: add compare-and-swap direct reference updates.
    reference_create(..., expected_old => $oid) uses
    git_reference_create_matching, and reference_set_target($name, $new_oid,
    expected_old => $old_oid) uses lookup plus git_reference_set_target.
    Stale expected OIDs throw Git::Native::Error with is_not_matched true.
    expected_old => undef atomically requires an absent ref when creating.
    The create path needs Git::Libgit2 to bind
    git_reference_create_matching; until that parallel binding lands it
    throws a clear function-not-bound error. libgit2 has no
    git_reference_set_target_matching function: git_reference_set_target
    already performs the atomic write by calling git_reference_create_matching
    with the looked-up ref's OID.
  - Remote: fetch and push now return a Git::Native::Remote::Result that
    carries per-ref outcomes. libgit2 returns 0 even when individual refs
    were skipped (non-fast-forward on fetch) or rejected (pre-receive
    hook, protected branch, server-side non-ff on push) — the only way to
    see those outcomes was through the per-ref callbacks. Git::Native now
    installs them and surfaces the verdicts:
      $r = $remote->fetch(refspecs => [...]);
      for my $u (@{$r->updated})  { ... }   # accepted moves, {ref, from, to}
      for my $r2 (@{$r->rejected}) { ... }  # push rejections, {ref, reason}
    `from` is undef for refs that did not exist locally before; `reason`
    is the libgit2 / server message ("" on a clean success).
  - Remote: install a certificate_check callback so SSH remotes work on
    libgit2 < 1.7 (which has no built-in known_hosts checking). Without it
    every git+ssh fetch/push/list_refs failed with GIT_ECERTIFICATE (-17)
    "invalid or unknown remote ssh hostkey". The hostkey is verified
    against ~/.ssh/known_hosts (hashed, plain, [host]:port and wildcard
    entries) by SHA256/SHA1 fingerprint, mirroring the git CLI. Set
    GIT_NATIVE_SSH_INSECURE=1 to accept any hostkey. HTTPS remotes keep
    libgit2's own TLS validation.
  - Oid: fix the `eq` overload so an Oid compares equal to its hex string
    (e.g. `$ref->target eq $known_sha`). It previously compared the 20 raw
    bytes against the right-hand side verbatim, so Oid-vs-hex-string never
    matched. Oid-vs-Oid is unaffected.
  - Error: libgit2 failures now surface as a Throwable Git::Native::Error
    (with code/klass/message) as documented, instead of leaking the
    low-level Git::Libgit2::Error. check_rc is re-homed in Git::Native::Error
    and used by every wrapper. klass now carries a real decoded category
    (Git::Libgit2 0.005), no longer always 0.
  - Error: predicates over the libgit2 code - is_not_found, is_exists,
    is_auth, is_certificate, is_conflict, is_not_fast_forward,
    is_unborn_branch, is_invalid_spec. The long tail is reachable via
    ->code and the GIT_E* constants now exported by Git::Libgit2.
  - Repository: new object($oid) - look up an object of unknown kind and
    return the matching typed wrapper (Blob / Tree / Commit / Tag).
  - Config: new get_bool($key) - read a git-style boolean via libgit2's
    git_config_get_bool (true/yes/on/1, false/no/off/0, integers by
    non-zero); undef when unset, Git::Native::Error on a non-boolean value.
    Repository->config_bool($key) is the snapshot-backed convenience, like
    config_string. Requires Git::Libgit2 0.005.
  - Config: get_string no longer swallows every error as undef - only
    GIT_ENOTFOUND maps to undef now, other failures throw (matching
    get_bool).

0.003     2026-05-27 18:55:00Z
  - New Git::Native::Config class - get_string / set_string / snapshot
  - Repository: config (live), config_snapshot (read-only), and a
    config_string($key) convenience that reads off a fresh snapshot
  - Git::Native->reference_name_is_valid($name) - static refname
    validator, no repository handle required

0.002     2026-05-27 18:02:46Z
  - Initial release
  - Moo-based high-level wrapper around Git::Libgit2 / libgit2 — no
    fork/exec, no XS
  - Tests: Error throw/catch (t/11-error.t), Credential constructors
    (t/35-credential.t), compile-check of all modules (t/00-load.t)
  - Core classes: Repository, Reference, Config, Blob, Tree, TreeBuilder,
    Commit, Signature, Oid, Object, Error (Throwable)
  - Remote class with fetch/push, wildcard-push expansion (libgit2 quirk),
    push --prune via connect+ls diff
  - Credential class for SSH key/agent, HTTPS userpass, default, username
  - General-purpose surface: clone (non-bare), Revwalker, Branch, Tag,
    status, status_for_path
  - Reference accessors/predicates: resolve, shorthand, symbolic_target,
    is_branch/is_remote/is_tag, plus set_target / symbolic_set_target
  - Repository HEAD surface: head, head_unborn, head_detached, set_head,
    reference_symbolic_create; init(initial_branch => 'main') pins HEAD
  - Commit accessors: summary, time (epoch), time_offset (minutes)
  - RAII handle ownership via DESTROY -> git_*_free, child objects hold
    strong ref to parent to prevent use-after-free
  - All tests run with GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_SYSTEM=/dev/null
    to avoid polluting the user's gitconfig
  - Live SSH/HTTPS auth tests (opt-in via TEST_GIT_NATIVE_* env vars)
