58 issue fixes · 14 other changes
v0.5.20 is mostly a performance and memory release for add, gc/repack, clone, status, blame and revwalks, plus fixes to diff, ignore, merge, fetch and push.
No-argument `grit diff` now uses the same HEAD-to-worktree scope for patch, stat, name-only, name-status, and numstat output. Staged-only files therefore appear alongside unstaged files, while explicit path-limited behavior is preserved.
Directory-only ignore patterns now participate only when the queried path is a directory. This prevents `!pkg/` from accidentally overriding an earlier `*` for files below `pkg`, so status, ignore check, and add agree with Git.
Reset orphan detection now recognizes the worktree whose HEAD is being moved even when repository discovery and worktree enumeration spell the same path differently. This prevents that old HEAD tip from being hidden as externally reachable, allowing reset output to report the commits it actually makes unreachable.
Adds regression coverage around empty bare repositories served by system Git over the SSH transport. It covers advertisement parsing, handshake completion, no-op fetch, clone success and empty-remote messaging, and successful push without receive-pack’s spurious hung-up diagnostic; production behavior is unchanged.
Receive-pack now finds the exact end of one incoming pack instead of waiting for the SSH input stream to close. It verifies the pack trailer, ingests the update, and emits report-status while the push client still holds its write side open.
Fetch now keeps successful tracking-ref updates when one ref is blocked by a file/directory namespace collision or lock. The blocked row becomes a typed rejection with a reason and, when the blocker is stale, guidance to prune; local, network, and bundle fetch paths share the behavior.
Bulk staging now reads, converts, hashes, compresses, and stores new worktree blobs in one parallel worker pass, while batching large index insertion. The round-3 correction keeps the fast all-new path for untracked files but restores existing-loose checks for tracked restage and gives every concurrent loose write its own temporary file.
Gitlink entries are now counted from their synthetic `Subproject commit <oid>` bytes before the pickaxe occurrence cache can read the referenced OID. This prevents bounded `-S` history walks from trying to load submodule commits from the superproject object database.
Broad adds during a conflicted merge now stage resolved and unrelated files, persist those index changes, and reject only files that still contain conflict markers. The failure names every rejected repository-relative path and appears consistently in human, JSON, and Markdown CLI modes.
Populated checkout now carries its known result tree through index writing and rebuilds the cache-tree when bulk index replacement cleared it. Fresh grit clones therefore write the standard TREE extension immediately, avoiding repeated full index/tree reconstruction in later status and diff operations.
Default `RevListOptions` now selects the existing bounded walk and commit-graph paths, so library callers using `RevWalk::walk` no longer materialize all history for a small max-count. The options are normalized against repository commit-graph configuration, and public docs/tests describe and verify the behavior.
This change removes expensive Windows switch-preparation metadata patterns while preserving checkout safety. It narrows unstaged worktree diffing to paths proven suspicious, serializes the default Windows stat preload, and retains safe wholesale-delete scanning, including mixed stat-drift and racily dirty paths.
Commit creation now accepts documented contact-form identities, expands them to timestamped Git headers, rejects control-character injection, and validates serialized identity headers before writing an object. The round-2 correction preserves Git-style author-date override behavior for inherited malformed/non-numeric date tails.
This change replaces comparator-time linear index scans with one stage-0 path-to-position map, then sorts stage outcome paths using constant-time map lookups. It also adds a reproducible 20k-file initial-add benchmark and records a baseline for the docs site.
Rebase planning now enumerates only commits reachable from the feature tip while hiding the fork’s ancestors, then topologically orders that small set. It reuses the range across planning stages and makes the bounded walk honor repository grafts consistently.
Routes update and explicit-path staging through a directory-grouped tracked scan, preserving an explicit empty restriction when no tracked index path matches. It also avoids redundant staged-commit and Windows path-stat work while retaining index/tree correctness, with regression and metadata-budget coverage.
This replaces the quadratic add-outcome sorter with one stage-0 path-position map, making each comparison a constant-time lookup. It also maps lossy display bytes for non-UTF-8 paths, preserving tracked-scan ordering that the first review found could otherwise regress.
This change restructures repack planning around shared payloads, reuses delta indexes and selected encodings, parallelizes loose-blob reads, and co-generates trusted pack-index rows. It also repairs the bitmap streaming writer so it retrieves real object bytes instead of serializing empty planned entries.
Merge continuation now uses the same configurable auto-stage policy as normal commits. After users edit away conflict markers, the default flow stages the resolved worktree in memory and records the merge instead of reporting the old conflicts again; the CLI now surfaces genuine continuation errors rather than synthesizing a second conflict outcome.
Add outcomes now build one stage-0 path-to-index-position map and use constant-time comparator lookups instead of rescanning the full index for every comparison. The round-2 correction keys that map with the same UTF-8/lossy strings exposed by StageOutcome, preserving index order for legal non-UTF-8 paths.
Caches each blob’s normalized rename-line hash/weight table once and reuses it across all inexact rename and copy candidate comparisons. This preserves existing grit results while removing repeated per-pair line hashing from the hot path.
Common embedder failures now expose stable typed errors instead of message strings. The round-two fix makes missing blame paths consistent across blame_file and compute_blame by classifying the error in their shared compute path, while preserving the caller-facing revision label.
Reset's orphan calculation now peels every non-moving ref and worktree tip and retains it only when it names a commit. The shared two-sided reachability painter also skips non-commit tips defensively, preventing blob/tree tags from being interpreted as commits.
This change avoids rebuilding an already consolidated clone while preserving garbage-collection semantics. A single-pack/no-loose repository skips repack only after proving that the active prune policy would not remove any packed object; full rewrites without loose objects use existing packed bytes instead of an expensive fresh delta search.
Initial checkout now walks a populated tree in bounded chunks instead of retaining a full-tree diff, while preserving conversion attributes and promisor-object fetching across both bulk and fallback materialization. It returns a Git-clean worktree and complete index for large repositories.
Plain revision counts with exclusion tips now stop at the hidden ancestry boundary instead of materializing the whole include and exclude histories. The CLI inherits the improvement through the existing RevWalk::count API, with no CLI, public API, or repository-format change.
Path-limited default diffs now preserve grit's documented “all uncommitted changes” scope. A staged-only file is visible in human and JSON output and causes `--exit-code` to return 1, while the pathspec still excludes unrelated files.
CRLF clean/smudge decisions now use the canonical attributes matcher instead of a duplicate simplified glob implementation. Anchored, bracket-class, and directory-scoped `.gitattributes` rules therefore protect binary content correctly during add and checkout.
Pack ingestion now hashes whole objects incrementally, bounds concurrent inflated bytes, limits delta-resolution batches, and avoids materializing the full pack while deciding whether it is thin. The round-two patch prevents oversized-object deadlock and restores SHA-256 thin-pack detection.
This change lets Windows clean-tree status trust matching index metadata instead of rereading tracked file contents, while preserving content verification for racily clean entries. It also avoids a duplicate hash after a non-racy stat refresh and adds Git-oracle regressions for the performance and racy-correctness paths.
This change adds a GC/repack fast path for already packed repositories and chooses on-disk delta reuse for pack-only rewrites. It also adds a public force-recompression option, updates stats to distinguish skipped writes, and documents/tests the behavior.
Clean tracked files are now hashed before any loose-object write, so a clean add after clone refreshes index metadata without duplicating packed blobs. Parallel loose publication uses per-object/process/thread/attempt temporary names and bounded ENOENT retries, while preserving required loose materialization for objects found only in cruft packs.
Replaces the super-linear stack propagation used by ref `--contains` queries with one shared generation/date-ordered heap walk carrying merged tip bitsets. The round-2 correction keeps commit dates strictly as heap ordering data, restoring generation semantics for merge-base and other callers.
Routes ordinary literal add pathspecs through a tracked-index scan narrowed to matching paths, instead of repeatedly matching every pathspec against the entire large index. Validation retains conflict-stage correctness through direct stage 1–3 lookups and one shared unmerged-path list, while tracked refresh now preserves file↔symlink type changes when core.filemode is false.
Reuse-only repacks now enumerate reachable objects without retaining every object body in memory. Traversal still reads commits, trees, and tags as needed and preserves the send list, while blob payloads remain on disk until pack serialization.
This change makes loose-object gc pack selection deterministic, routes pack serialization through configured zlib compression (including true level 0), and avoids parallel preparation overhead for packs below 2,048 entries. It relocates the issue-1653 CLI regression to grit-cli so Cargo supplies the binary, but currently deletes and relaxes an existing library regression.
This change ensures a blob-to-symlink type change is restaged even when core.filemode=false and the worktree object ID equals the index object ID. It adds a system-Git index oracle plus strict fsck regression, and the final commit message now satisfies review policy.
This change makes GC’s fast single-pack skip conditional on being an automatic GC optimization with no requested repack side effects. It also strengthens the bitmap regression so it proves GC actually rebuilt the pack instead of accepting a bitmap created by fixture setup.
This change removes Axum's effective 2 MiB receive-pack request cap by streaming push bodies into grit-protocol on a blocking reader. It keeps fetch requests capped at 32 MiB and adds a typed gzip expansion guard for pushes, allowing normal large gzip streams while refusing bombs and malformed streams with deliberate client errors.
Custom-primary repositories now fail safely and immediately when fetch, push, fsck, or unbundle would bypass their configured object store. Bulk staging and auto-stage commits now write through the custom primary, while files-backed transport behavior remains unchanged.
Adds a parsed object-id prefix that compares digest bytes without per-object hex allocation and exposes exact lower/upper bounds for indexed custom stores. Abbreviation now scans the minimum prefix once and derives the unique length from the candidates, while all built-in stores share the same prefix representation.
Porcelain ref mutations now consistently use the Repository's configured RefStore instead of reopening .git-backed files. The round-two fix closes the remaining false-success switch path by installing symbolic or detached HEAD even when the injected store initially has no valid HEAD.
Hook scripts now receive usable child-process paths and an explicitly resolved interpreter on Windows instead of an MSYS-incompatible verbatim path. Spawn failures are typed and retain their OS cause, while post-commit failures are surfaced as non-fatal diagnostics after the commit has already succeeded.
This makes literal pathspec matching fast when macOS Unicode precomposition is enabled and resolves many argv pathspecs without reloading config or probing `.git` for every argument. It adds a Repository-scoped typed resolver, routes library and thin CLI call sites through it, and tests both Unicode correctness and cached resolution.
Graph-backed full-history walks now use commit-graph parent and committer-time data without inflating every commit merely to cache its subject or tree. Subjects and commit payloads remain lazy for callers that render rows, while graph lookup reuse removes repeated binary searches.
This branch replaces lifetime-retained inflated pack objects with pack-backed delta metadata, iteratively resolves deep chains, streams whole-object hashing through the hash abstraction, and restores bounded two-worker resolution. The latest follow-up selectively restores owned inflated bases to regain speed, but that selection currently retains enough payloads to exceed a gigabyte on the cargo pack.
Garbage collection now avoids object-graph walks when a fresh single pack or the loose-object grace window makes them unnecessary. When a walk is needed, repack and prune share one lazy reachable set, while gc preserves existing commit-graph Bloom-filter policy instead of paying to create filters on first write.
All built-in ref stores now reject malformed transaction names and all-zero direct object IDs at prepare time, before locking or writing. The behavior is covered across files, memory, and reftable, with a files-backend round-trip proving rejected writes leave a repository clean for system Git.
Adds safe, typed ways for embedders to create Git trees and commits without an index or worktree. Raw `Objects::write` now rejects malformed structured objects before storage, while an explicit literal-write escape hatch remains for corruption tests.
RepositoryBuilder now treats symlinked, relative, and canonical spellings of the same object directory as identical. It canonicalizes both sides of the custom ODB comparison, retains rejection of genuinely different directories, and turns the previously stale documentation snippet into a compiled example.
MemoryRefStore iteration no longer holds its non-reentrant mutex while invoking caller callbacks. Walks retain an Arc snapshot, allowing callback reads, nested walks, and writes without deadlocking while preserving a stable in-progress view.
This change makes clean status trust valid index stat data, persists verified racy-index refreshes, and removes avoidable tracked/untracked filesystem probes. It also preserves correctness for smudged entries and symlinked ignore files, with Git-compatible regression coverage.
Blame now coalesces pending lines by commit and path in a date-ordered suspect queue, avoiding repeated traversal of shared merge ancestry. It also bounds retained blob and diff-map data, fixes an empty-parent panic for merges that introduce a path, and adds Git-parity coverage plus a realistic merge-heavy benchmark.
Caches reverse-index offset lookup, keeps reusable packed payloads on disk, avoids redundant object-walk path/payload work, and streams upload-pack output through sideband instead of materializing the full response. The round-2 amendment correctly traverses trees whose directory mode has non-canonical permission bits.
Receive-pack now evaluates branch ancestry through the quarantine-aware ODB, so newly pushed commits are visible before migration. The new exact two-sided paint walk remains correct under non-monotonic commit dates and can use corrected commit-graph generations as a safe bound.
Replaces breadth-first pack delta rounds with per-root depth-first walks. Workers release parent payloads after their final child, cap retained ancestor bytes at 16 MiB per worker, and rebuild evicted ancestors only when a later sibling needs them.
Porcelain add and commit can now stage new and bulk-restaged files into a custom object-store primary instead of requiring filesystem loose-object writes. Repository fsck now validates objects provided by a custom primary, and tests prove those objects stay out of `.git/objects` and remain valid when exported for system Git.
Push packs are streamed to a quarantine temp file, then unpacked or indexed without the old whole-pack Vec/copy/reverify path. Embedders gain typed refusal/unpack outcomes and Rust policy hooks, receive-pack scripts get GIT_DIR=., and a tested server example plus network docs make the public API discoverable.
Adds one typed, kind-checked path for reading a commit or only its root tree, exposed on both Odb and repository-scoped Objects. It replaces private read/check/parse helpers across history, stash, merge, pull, bitmap, commit-graph, fixtures, examples, and docs, cutting 200 net lines.
This change removes fixed startup work by deferring clap construction to the selected command, while preserving full-tree help and completions. It also reduces allocations in bounded retained revwalks and reuses the trusted single-pack reader within a thread-scoped ODB read context.
This branch repairs the CI failures across CLI contracts/snapshots, rustdoc/public API checks, and merge dry-run exit behavior. Round 2 also makes ephemeral snapshot-path redaction portable beyond Linux and fixes the one prohibited commit subject.
Restores the CLI schema/doc validation infrastructure needed by CI, refreshes affected committed contracts, and synchronizes the public API snapshot. The round-two correction ensures clone validation and drift checks use the published v3 contract rather than v2.
This branch repairs the main-CI regressions around add hunk context, config unset argument validation, generated snapshots, and the grit-lib public API snapshot. The round-2 amendment preserves the frozen add v1 JSON contract and introduces add v2 for the newly stable `staged_hunks` and `staged_lines` keys.
This change lets the shared argv preprocessor recognize `add` as a command eligible for attached diff-flag normalization, rewriting `-U0` to clap's explicit `-U=0` shape before parsing. It also adds a direct unit test for the `grit add --hunks … -U0` argument sequence.
Branch and tag queries now reuse the files ref store's stamp-validated packed-refs view instead of mapping and indexing the same large file multiple times. Branch listing also avoids unused per-tip peels and parallel-thread-policy setup for a single listed branch, while preserving peeling whenever reachability or points-at filters need it.
Deletes the second, flattened repository-fsck facade from maintenance and leaves repository_fsck as the single supported API. Existing tests now call that canonical API directly, including explicit progress/cancellation and severity evaluation, while the ref-formatting coverage moves beside the implementation it exercises.
Linked-worktree add, remove, move, lock, unlock, and repair now expose structured WorktreeError values instead of free-form Error::Message strings. The refactor also centralizes registered-worktree lookup, reuses BranchError and Odb::commit_tree, and adds public-API compatibility tests against system Git.
This corrects tracked-file staging when an indexed gitlink is replaced by a regular file. The replacement is now staged as a normal blob with regular-file mode, including when `core.filemode=false`, while actual directory-backed submodules keep their existing path.
Adds a presentation-free grit-lib operation that verifies an annotated tag by short name and returns its ref oid, target oid, and verification report. The CLI now only invokes that operation, translates typed errors, and renders the existing output.
Removes obsolete grit-lib helper APIs left over from the retired Git-compatible CLI and coverage-only test calls, then deletes their now-unreachable private implementations. The result is 1,732 net lines smaller, with no production call path changed.
This removes per-line ownership and repeated intermediate conversions from the xdiff path used by hunk restore, makes hunk application borrow untouched preimage rows, and avoids writing an unreachable loose blob merely to derive its object ID. The external library and CLI APIs are unchanged; compatibility tests confirm selected restore content, clean full restore, ident expansion, fsck validity, and absence of the synthetic loose object.
Remote inspection now loads each standard remote-tracking prefix once and classifies advertised branches by set membership, eliminating repeated packed-ref parsing. The round-2 correction limits that cache to directory-like wildcard destinations and preserves direct resolution for unusual prefixes, retaining correctness on both files and reftable backends.