github GitoxideLabs/gitoxide gix-v0.87.0
gix v0.87.0

53 minutes ago

Bug Fixes (BREAKING)

  • remove need-more-recent-msrv as it's not required anymore
    It was mostly meant to be internal, but the name didn't indicate
    this, hence the breaking change.

Test

  • assert fetched pack contents rather than pack checksums
    fetch_pack and fetch_pack_without_local_destination asserted the
    data_hash/index_hash of a pack produced by the host git. Those cover
    the pack's compressed bytes, so they silently encode which zlib
    implementation that git is linked against: with a zlib-ng-linked git
    (Arch, among others) the 219-byte commit in that pack deflates to 152 bytes
    where stock zlib produces 153, making the pack 268 bytes instead of 269 and
    changing both checksums.

    Nothing about the fetch itself differs — re-deflating the very same object
    payloads with stock zlib and re-hashing reproduces the expected checksum
    byte for byte, and every other assertion in both tests already passes.

    Assert the pack's object ids instead, read back from the index that was just
    written. They hash uncompressed content, so they are identical on every host,
    and they are what the checksums were standing in for. num_objects,
    pack_version and index_version are untouched; Entry::crc32 is
    deliberately not used, as it too is computed over compressed bytes.

New Features (BREAKING)

  • add Git-compatible commit signature verification with Commit::verify()
    Breaking because it also adds config::tree::Key::default_value(), which gives Any
    a default_value field.

    Expose repository-aware verification on commits while delegating verifier
    execution and result parsing to gix-object plumbing. Resolve supported
    signature formats, configured programs, trust thresholds, SSH allowed
    signers and revocations, repository-relative paths, and commit verification
    time according to Git configuration.

  • add commit::Info::generation
    That way it's evident if a commit-graph was present for this node.
    Breaking, as it adds a new public field to a structure.

Changed (BREAKING)

  • remove the tree-editor feature toggle

    The workspace MSRV now exceeds Rust 1.75, which stabilized the language feature
    that originally required tree editing to be gated.

    Make tree-editing APIs available unconditionally.

New Features

  • expose git notes in gix::Repository

    Add the notes feature and Repository::notes as the porcelain layer over gix-note
    for repeated queries and mutations.

    Select the default notes ref from core.notesRef, including the GIT_NOTES_REF
    environment override represented in config::tree, and fall back to
    refs/notes/commits. Discover additional display refs from notes.displayRef or
    GIT_NOTES_DISPLAY_REF, expand glob patterns, preserve display order, and avoid
    duplicates.

    For mutations, accept conventional short notes-ref names, write note blobs and
    notes commits, and update refs with compare-and-swap expectations so concurrent
    changes are not silently overwritten.

  • add Repository::delete_local_branches().
    asdf

    Validate the entire batch before changing references, reject branches checked
    out in any worktree, and delete references and reflogs in one transaction
    without requiring commit traversal.

    Remove matching local branch configuration under lock and report when
    configuration cleanup fails after reference deletion. Share checked-out branch
    discovery with fetch updates and use restricted repository opening throughout
    the affected tests.

  • add Git-compatible commit signing via Commit::sign()

    Expose repository-aware commit signing while delegating signature creation
    to gix-object plumbing. Resolve gpg.format, per-format programs, signing
    keys, committer identity fallback, and gpg.ssh.defaultKeyCommand from Git
    configuration, including trusted paths and shell commands.

    Add commit_signing_options_if_enabled() so porcelain callers honor
    commit.gpgSign without resolving signer configuration while signing is
    disabled. Preserve caller control over resolved program arguments and
    environment, including non-interactive GPG operation.

  • add commit signature verification to gix-object via commit::SignedData::verify()

    Add feature-gated plumbing for verifying OpenPGP, X.509, and SSH commit
    signatures with fully resolved programs, arguments, environments, trust
    thresholds, and SSH policy inputs. Keep repository configuration out of
    the object crate while exposing Git-compatible status, identity, key, and
    fingerprint results.

    Stream signed commit data directly to OpenPGP and SSH verifiers without
    reconstructing it. Use a temporary payload only where gpgsm requires a
    file, and cover Git status parsing plus unsupported and mismatched formats.

  • expose Git quoting utilities in gix

  • expose Git-compatible editor selection

    Add `Repository::editor()`` to resolve the interactive editor with Git's
    precedence rules. Honor GIT_EDITOR ahead of trusted core.editor, consider VISUAL
    only for capable terminals, fall back through EDITOR to vi, and report no editor
    for an unconfigured dumb terminal.

    Route GIT_EDITOR through the configuration environment-override framework
    so isolated repositories and environment permissions remain effective. Cover
    precedence, dumb terminals, the no-op editor, and isolation.

  • recognize SHA-256 commit signature headers

    Teach commit parsing and signature extraction about the gpgsig-sha256
    header used by Git when signing SHA-256 commits. Treat it like gpgsig
    when locating the embedded signature while preserving the actual header
    name when reconstructing the signed payload.

    Cover both full commit parsing and token iteration so callers observe
    the signature consistently through either API.

  • add support for GIT_ALLOW_PROTOCOL

  • honor GIT_INDEX_FILE when opening repositories via discover_with_environment_overrides()

    Map GIT_INDEX_FILE to the new gitoxide.core.indexFile configuration key
    and use it for index reads and writes, allowing to implement hooks for the first time.

    As a fix, Repository-local environment overrides are no longer inherited when opening
    submodules, linked worktrees, or their main repository. This prevents an
    alternate index, worktree, or Git directory from leaking into another
    repository.

    The selected index path remains stable until the repository is reloaded, and
    empty index-file overrides are rejected.

  • support cloning a single revision

    A full object ID passed through with_ref_name() produced an object-ID refspec
    mapping and panicked while clone assumed every mapping had a name. Branch and
    tag checkout also retained ordinary clone tracking semantics instead of offering
    a single-revision mode.

    Add PrepareFetch::with_revision() and gix clone --revision for full refs, HEAD,
    and full object IDs. Revision clones use a one source-only implicit refspec,
    detach HEAD to the fetched commit, create no ordinary refs, persist no fetch
    refspec, and disable tag following. Existing with_ref_name() and --ref behavior
    stays unchanged.

    This follows Git commit 337855629f59 (builtin/clone: teach git-clone(1) the
    --revision= option) and its t/t5621-clone-revision.sh behavior.

  • add config() function

    Factor the non-repository portion of configuration initialization out of
    repository opening and expose it as gix::config(). The new API accepts a future
    git directory and the same open::Options used by open and clone, preserving
    source permissions, conditional includes, environment handling, and override
    precedence.

Chore

  • Stabilize fixtures on Windows
    • Normalize the precomputed diff fixture assets to LF before writing blobs,
      populating the index, and creating commits. Git for Windows may check these
      assets out with CRLF, which changes their object IDs and adds carriage returns
      to index paths, causing fixture setup to fail at git mv cli c.

    • Prevent Git Bash from rewriting revision arguments before Git sees them, and
      normalize the two pathspec baseline cases where Git for Windows applies native
      path validation to repository-format paths. Exclude a glob baseline whose
      backslash behavior is specific to Git for Windows rather than Git paths.

    • Generate pathological .gitmodules entries as configuration data instead
      of trying to create module directories whose names cannot be represented
      on Windows. Use Git Bash bundled Perl for binary fixture construction so
      regeneration does not depend on a separately installed Python interpreter.

    • Also pass a literal carriage return to sed through Bash ANSI-C quoting when
      normalizing the jj diff assets. Unlike GNU sed, BSD sed does not interpret
      backslash-r in a single-quoted expression, so the previous spelling could remove
      a trailing letter r on macOS instead of stripping CRLF endings.

Bug Fixes

  • don't limit is_dirty() to the current working directory

  • resolve the empty pattern in <rev>^{/} like Git, instead of skipping it.
    The parser dropped the find() delegate call whenever the pattern in
    <rev>^{/<pattern>} was empty, turning the whole navigation step into a
    no-op on the grounds that an empty pattern matches everything.

    That reasoning only holds for a commit anchor and a non-negated pattern.
    Git routes <rev>^{/...} through GET_OID_COMMITTISH and searches from
    the peeled commit even when the pattern is empty - object-name.c notes
    "$commit^{/}. Some regex implementation may reject empty regex, but this
    is safe". Thus git rev-parse 'b-tag^{/}' yields the commit the
    annotated tag points at, while gix returned the tag object itself. A
    negated empty pattern matches no commit at all, so Git fails HEAD^{/!-}
    while gix silently succeeded with HEAD.

    Now the parser always forwards the pattern to Navigate::find(), whose
    implementation in gix already handles the empty case correctly on both
    the revparse-regex and the substring fallback paths: it peels the
    anchor to a commit first and treats an empty pattern as match-all, which
    fails naturally when negated. The delegate behind gix revision explain
    makes no assumption about patterns and needs no change.

    The make_rev_spec_parse_repos fixture gains baselines for @^{/},
    @^{/!-} and b-tag^{/}; its archive needs regeneration.

  • correctly handdle GIT_PROTOCOL_FROM_USER when evaluating protocol permissions

    Align helper protocol permissions with Git by applying policy by transport
    name, using the known-safe, ext, and user defaults, and parsing
    GIT_PROTOCOL_FROM_USER as a Git boolean.

  • peel annotated tags before navigating a rev-spec, like in Git.
    <tag>^, <tag>^<n>, <tag>~<n> and <tag>^{/<text>} navigated from the tag
    object itself rather than from the commit it points at. In a repository whose
    annotated b-tag names the merge commit b, Git resolves b-tag^ to d,
    while gix reported "Object 212d0f0 was a tag, but needed it to be a commit",
    and b-tag^{/G} failed with a kind mismatch instead of resolving to g.

    Git routes these forms through GET_OID_COMMITTISH, which dereferences tags
    first. The three navigation sites now peel to a commit the way ^{commit}
    already did, keeping the original object id as the replacement key. Peeling is
    the identity on a commit, so commit anchors resolve and fail exactly as before.
    A blob or tree anchor now reports that it could not be peeled to a commit.

    <tag>~0 still yields the tag. The parser in gix-revision skips the delegate
    call entirely for a zero-length ancestor walk, which is correct for a commit
    anchor and is pinned by a test there; changing it touches the public Navigate
    contract and belongs in its own change.

  • reject ./.. revspec paths that leave the worktree, like in Git.
    At the worktree root, HEAD:./../this resolved to the blob at this while
    HEAD:../this correctly failed. Git rejects both:

    $ git rev-parse 'HEAD:./../this'
    fatal: './../this' is outside repository at '/…/make_basic_repo'
    

    Containment is delegated to Repository::normalize_path(), which normalizes
    against an empty current_dir so that a .. with nothing left to consume
    fails. In gix_path's normalize_inner() a leading . stays in the buffer,
    so .. pushes the empty current_dir onto it, pop() removes the . and
    succeeds — where the same path without the leading . pops an empty buffer,
    gets false, and correctly yields None. Only the worktree root is affected;
    from some/very, HEAD:./../../this already resolved and
    HEAD:./../../../this already failed.

    Dropping the current-directory components before normalizing keeps the fix
    inside the revspec parser.

    The alternative is normalize_inner() itself, which is a two-line change and
    would make the documented promise of Repository::normalize_path() — "Paths
    which traverse outside of the repository are rejected" — true for every
    caller. I left it alone because it also changes callers unrelated to this
    work: gix_submodule::File::path() would start rejecting a .gitmodules
    path = ./../evil that it accepts today, and gix_pathspec::Pattern::normalize()
    would start rejecting ./../x. Both look like improvements, but they belong
    in a change against gix-path, not in this one.

  • resolve ./ and ../ revspec paths against the current directory, like in Git.
    gitrevisions(7) states that a path starting with ./ or ../ is relative to the
    current working directory and gets converted to be relative to the working tree's root
    directory. gix passed these paths on verbatim, so in a repository with dir/g1.txt
    committed, and with the current directory being dir/, git 2.50.1 and gix disagreed:

    revspec git gix
    HEAD:./g1.txt the blob Could not find path "./g1.txt" in tree
    HEAD:../f1.txt the blob at the root Could not find path "../f1.txt" in tree
    :./g1.txt the blob Path "./g1.txt" did not exist in index at stage 0
    HEAD:./ the tree of dir delegate.peel_until(Path("./")) failed

    gix-revision's parser forwards such paths intact on purpose, and the Navigate
    delegate documentation promises the conversion, but the gix implementation of it
    never performed one.

    The tree lookup in peel_until() and index_lookup() now route paths with either
    prefix through Repository::normalize_path(). Like Git, this requires the current
    directory to be inside a worktree, and paths traversing above the worktree stay
    rejected.

  • respect filter driver configuration precedence
    A filter driver declared in user configuration incorrectly won over a
    repository-local declaration with the same name. A regression test demonstrates
    both the local property override and inheritance of a user-level property that
    is not overridden.

    Merge repeated named filter sections in configuration order and replace
    only the properties present in each later section. This matches Git's
    read_convert_config() behavior in convert.c as inspected at cf5497b14c.

  • respect inherited core.symlinks when cloning.

    Load global configuration with the clone open options before repository
    initialization, and preserve an effective core.symlinks=false as a
    high-precedence override. Combine that result with probed filesystem
    capabilities so either source can disable symlinks.

    Match Git initialization by persisting core.symlinks=false only when the
    filesystem probe fails; never write a local true that masks inherited
    configuration. Add a portable plumbing-built symlink fixture and cover both
    configuration-false/probe-true and configuration-true/probe-false clones.

  • let GIT_WORK_TREE override core.bare

Commit Statistics

  • 79 commits contributed to the release over the course of 30 calendar days.
  • 30 days passed between releases.
  • 26 commits were understood as conventional.
  • 2 unique issues were worked on: #1353, #1930

Commit Details

view details
  • #1353
    • Respect inherited core.symlinks when cloning. (ed9a650)
  • #1930
    • Support cloning a single revision (b1174b6)
  • Uncategorized
    • Update manifests prior to release (ebe9095)
    • Merge pull request #2930 from GitoxideLabs/gix-notes (7424676)
    • Expose git notes in gix::Repository (d934f5b)
    • Merge pull request #2926 from cruessler/remove-object-hash-from-options (b2d919a)
    • Merge pull request #2924 from abemedia/fix--don't-limit-is_dirty()-to-the-current-working-directory (ea9a6d4)
    • Review (44ba4b4)
    • Don't limit is_dirty() to the current working directory (cb74ef6)
    • Merge pull request #2905 from GitoxideLabs/various-improvements (f3bbfad)
    • Add Repository::delete_local_branches(). (61c5e1e)
    • Adapt to changes in gix-object (object signing) (723d3de)
    • Add Git-compatible commit signing via Commit::sign() (8017175)
    • Add Git-compatible commit signature verification with Commit::verify() (15809f9)
    • Add commit signature verification to gix-object via commit::SignedData::verify() (5b90699)
    • Expose Git quoting utilities in gix (a9f090e)
    • Expose Git-compatible editor selection (e1c56e4)
    • Recognize SHA-256 commit signature headers (a30f442)
    • Adapt to changes in gix-testtools (0cbe539)
    • Merge pull request #2919 from cruessler/require-object-hash-in-file-store-at (e5452ba)
    • Adapt to changes in gix-ref (5810922)
    • Merge pull request #2916 from cruessler/require-object-hash-in-store-at (dd8c759)
    • Adapt to changes in gix-odb (1dc741f)
    • Adapt to changes in gix-odb (a0b93a3)
    • Merge pull request #2911 from codeAnqiang-ma/fix/empty-regex-revspec (05f905e)
    • Merge pull request #2910 from codeAnqiang-ma/fix/relative-date-month-rollover (566fea1)
    • Review (a8b1be5)
    • Resolve the empty pattern in <rev>^{/} like Git, instead of skipping it. (6746715)
    • Adapt to changes in gix-date (613ff86)
    • Merge pull request #2899 from ameyypawar/url-remote-helper (dbd162d)
    • Add support for GIT_ALLOW_PROTOCOL (287ab8f)
    • Correctly handdle GIT_PROTOCOL_FROM_USER when evaluating protocol permissions (77b5848)
    • Adapt to changes in gix-url (2648dc1)
    • Adapt to changes in gix-url (62e140d)
    • Merge pull request #2895 from abemedia/feat--honor-GIT_INDEX_FILE-via-gitoxide.core.indexFile (aa3b006)
    • Remove the tree-editor feature toggle (3a4350c)
    • Remove need-more-recent-msrv as it's not required anymore (f1c8911)
    • Honor GIT_INDEX_FILE when opening repositories via discover_with_environment_overrides() (f9b3891)
    • Honor GIT_INDEX_FILE via gitoxide.core.indexFile (e1856d0)
    • Merge pull request #2894 from ameyypawar/refspec-push-source (ef41caa)
    • Adapt to changes in gix-refspec. (9fa50a5)
    • Merge pull request #2719 from cruessler/blame-untracked-changes (95e0213)
    • Adapt to changes in gix-blame (93d4019)
    • Merge pull request #2824 from GitoxideLabs/fetch-revision (7a34c17)
    • Merge pull request #2875 from cruessler/dont-use-feature-gated-method-in-doctest (0760b60)
    • Don't use feature-gated method in doctests without feature-gate (368438c)
    • Merge pull request #2871 from shuvamk/fix/revspec-peel-tag-before-traversal (d14aefb)
    • Review (e99f637)
    • Peel annotated tags before navigating a rev-spec, like in Git. (232dd1c)
    • Merge pull request #2870 from shuvamk/fix/revspec-relative-paths (5510bce)
    • Review (dffd5ef)
    • Reject ./.. revspec paths that leave the worktree, like in Git. (92e3133)
    • Resolve ./ and ../ revspec paths against the current directory, like in Git. (6171a05)
    • Merge pull request #2867 from GitoxideLabs/fix-url-authority-parsing (cc3ee80)
    • Release gix-path v0.12.4, gix-command v0.9.2, gix-config-value v0.19.1, gix-url v0.37.1, gix-credentials v0.39.1, gix-transport v0.58.1 (ab4fcb0)
    • Merge pull request #2861 from GitoxideLabs/git-filter-config (a7e0a2e)
    • Respect filter driver configuration precedence (4f480b9)
    • Merge pull request #2855 from EliahKagan/claude/run-ci/detect-uncommitted-fixture-archives (5708de4)
    • Commit the make_clone_with_symlink fixture archive (18e42fa)
    • Merge pull request #2852 from GitoxideLabs/delta-tree-parallelism (4a6cf9d)
    • Make sure gix-pack uses the parallel feature (79e3478)
    • Merge pull request #2848 from EliahKagan/claude/run-ci/which (b049777)
    • Quote the shallow clone's file:// URL in the same fixture (3dabe4a)
    • Quote the git path and the URL in the credential-helper baseline (951f40f)
    • Use command -v rather than which to locate git in a fixture (51afc13)
    • Merge pull request #2846 from GitoxideLabs/open-options-on-clone (50713b0)
    • Add config() function (9923d76)
    • Merge pull request #2841 from danielcadev/codex/fix-bare-git-work-tree-override (da71d06)
    • Refactor worktree and bare repo handling as it got quite messy (f03905e)
    • Review (275c26a)
    • Let GIT_WORK_TREE override core.bare (639535e)
    • Merge pull request #2837 from nikicat/fix-pack-checksum-assertions (93ad8f9)
    • Review (03ef906)
    • Assert fetched pack contents rather than pack checksums (29aa7cd)
    • Merge pull request #2839 from GitoxideLabs/tix-improvements (4b3bf5a)
    • Add commit::Info::generation (e768682)
    • Merge pull request #2830 from GitoxideLabs/fix-jj-test-on-windows (82711e1)
    • Stabilize fixtures on Windows (75444cb)
    • Merge pull request #2812 from GitoxideLabs/report-july (ae8845a)

Don't miss a new gitoxide release

NewReleases is sending notifications on new releases.