Back to Gitoxide

SHA256 / Object-Hash Transition Plan

etc/plan/sha256-support.md

0.54.09.7 KB
Original Source

SHA256 / Object-Hash Transition Plan

Source issue: GitoxideLabs/gitoxide#281
Imported on: 2026-04-22
Last reconciled: 2026-05-08 Working assumption: checkboxes in this file reflect current checkout, not only historical issue state.

Mission

Make object-hash kind first-class across config, protocol, storage, tests, and clone flow so SHA1 and SHA256 are both deliberate runtime choices instead of SHA1 being hidden fallback everywhere.

Constraints

  • Use current workspace as source of truth.
  • Treat old issue checkmarks as historical context only.
  • Prefer end-to-end correctness over isolated enum or parser support.
  • Keep scope on actual supported transition path, not abandoned pack-index-v3 speculation.

Reconciled Status

  • Remove hash-type specific methods from gix-hash and lean on gix_hash::Kind-parametric usage. Evidence: gix-hash still contains new_sha1, new_sha256, from_20_bytes, from_32_bytes, null_sha1, null_sha256.
  • Remove len-20 assumptions from all relevant code paths. Evidence: big progress exists, but 79 non-plan/non-changelog Kind::Sha1.null()/ObjectId::null(...Sha1) call sites still remain, plus a few explicit 20-byte comments and helpers.
  • Provide visible CLI path for choosing object hash kind. Evidence: src/plumbing/options/mod.rs and src/plumbing/options/free.rs expose clap object_hash fields, which produce --object-hash.
  • Propagate sha256 feature support through crates that participate in object traversal, object parsing, and object-id storage. Evidence: 31 workspace packages now define a sha256 feature, including gix-object, gix-index, gix-protocol, gix-ref, gix-refspec, gix-traverse, and top-level gix.
  • Make SHA256-only builds compile where supported by feature flags. Evidence: cargo check -p gix --no-default-features --features sha256 fails because gix/src/config/cache/incubate.rs and gix/src/config/tree/sections/core.rs still name gix_hash::Kind::Sha1 unconditionally.
  • Remove default sha1 feature from gix-hash and deal with fallout. Evidence: gix-hash has default = [], docs.rs explicitly enables sha1, root gitoxide chooses SHA1 via features, and justfile contains compile-guard checks for missing hash selection.
  • Remove SHA1 mention from gix-features feature toggles. Evidence: gix-features/Cargo.toml has no SHA1/SHA256 feature toggles anymore.
  • Parameterize hash length when decoding non-blob objects. Evidence: gix-object tree decoding hotspot uses hash_kind.len_in_bytes().
  • Add Sha256 enum variant and hasher support. Evidence: gix_hash::Kind::Sha256, ObjectId::Sha256, and Hasher::Sha256 exist.
  • Add broad dual-hash test hooks for reading refs and objects of different lengths. Evidence: justfile runs SHA256 fixture suites for gix-object, gix-ref-tests, gix-traverse-tests, and top-level gix.
  • Add write/read roundtrip coverage for different hash lengths, ideally with stronger repo-level verification. Evidence: targeted tests exist, but no obvious repo-conversion or full transition verifier is present.
  • Handle remote object-hash mismatch during clone by configuring repository accordingly. Evidence: gix/src/clone/fetch/mod.rs still has unimplemented!("configure repository to expect a different object hash as advertised by the server").

Deferred / Out Of Scope

  • Pack index v3 transition work stays deferred unless Git actually relies on it. Rationale: issue itself already demoted this from active task to decision point.

Current Snapshot

Workspace signals on 2026-05-08:

  • gix-hash default hash feature: removed
  • compile-guard checks for missing hash selection in justfile: 8
  • crates/packages with explicit sha256 = ... feature declarations: 31
  • gix-traverse hash feature declarations: sha1 and sha256
  • Kind::Sha1.null()/ObjectId::null(...Sha1) occurrences outside this plan and changelogs: 79
  • object-format=sha1 fixture occurrences outside this plan: 10
  • object-format=sha256 fixture occurrences outside this plan: 0
  • clone path unimplemented!() for hash mismatch: 1
  • visible object-hash CLI fields: present in repo and no-repo plumbing options
  • cargo check -p gix --no-default-features --features sha256: fails on unconditional Kind::Sha1 references in gix

Dual-hash test hooks already exist in justfile for at least:

  • gix
  • gix-filter
  • gix-diff
  • gix-status-tests
  • gix-commitgraph
  • gix-object
  • gix-ref-tests
  • gix-pack
  • gix-diff-tests
  • gix-traverse-tests
  • gix-blame
  • gix-refspec
  • gix-worktree-stream
  • gix-hash

Confirmed Done

  • gix-commitgraph Evidence: issue marked it complete, dev-dependencies enable gix-hash with sha1 and sha256, and justfile runs it with GIX_TEST_FIXTURE_HASH=sha1 and sha256.
  • gix-traverse feature declaration Evidence: gix-traverse/Cargo.toml now defines both sha1 and sha256.
  • extensions.objectFormat=sha256 parsing Evidence: gix/src/config/tree/sections/extensions.rs accepts sha256 behind the feature, and gix/tests/gix/config/tree.rs covers lowercase and uppercase SHA256.

Remaining Hotspots

  • gix/src/config/cache/incubate.rs Repository object hash still falls back to SHA1 when config does not say otherwise, and the unconditional Kind::Sha1 references break SHA256-only gix builds.
  • gix/src/config/tree/sections/core.rs core.abbrev validation still passes Kind::Sha1 unconditionally for a context that may compile without SHA1.
  • gix-protocol/src/fetch/refmap/init.rs object-format capability parsing still rejects anything except sha1.
  • gix/src/clone/fetch/mod.rs Clone still aborts on remote hash mismatch instead of configuring repo state.
  • gix/Cargo.toml Top-level sha256 currently forwards to gix-hash, gix-pack, and optional gix-worktree-stream, but not to all direct dependencies that define their own hash features; confirm whether this is intentional feature unification or an under-forwarding gap.
  • gix-worktree-stream/Cargo.toml Its sha256 feature forwards only gix-hash/sha256. This may be acceptable because dependencies currently avoid SHA-specific cfgs, but it should be verified against feature-isolated builds.

Execution Order

Batch 1: hash API, features, and explicit selection

  • gix-hash Remove remaining SHA1/SHA256-shaped helper APIs where Kind-based forms can replace them.
  • feature propagation Add and forward sha256 features where crates already have hash-sensitive APIs or compile guards, including gix-traverse.
  • gitoxide CLI surface Keep the clap-derived --object-hash selection on plumbing commands.
  • SHA256-only compile Replace unconditional Kind::Sha1 fallbacks in gix config/cache paths with feature-aware defaults or required configuration.
  • gix-refspec Keep object-hash-looking refspec parsing honest under SHA256-heavy inputs.

Batch 2: config and object parsing

  • gix Teach extensions.objectFormat config parsing to accept sha256.
  • gix Decide the correct default/fallback behavior when no extensions.objectFormat exists and SHA1 is not compiled in.
  • gix-object Keep object parsers hash-length aware and extend tests around non-SHA1 trees and related decode paths.
  • gix-ref Expand refs and reflog read/write coverage to both hash lengths.
  • gix-index Extend checksum and extension tests to SHA256-sized object ids.
  • gix-traverse Add sha256 feature support and a SHA256 fixture run in justfile.

Batch 3: protocol and transport

  • gix-transport Add negotiation fixtures that advertise object-format=sha256.
  • gix-protocol Accept and preserve SHA256 object-format negotiation end to end.

Batch 4: storage layer

  • gix-odb Strengthen loose/packed lookup and prefix behavior under SHA256.
  • gix-pack Finish pack data, index, multi-index, and verification assumptions that still lean on SHA1-shaped fixtures or sentinels.

Batch 5: porcelain behavior and sentinel cleanup

  • gix Replace clone hash-mismatch unimplemented!() with real repo initialization/configuration.
  • gix-diff Remove SHA1-only sentinel assumptions where caller hash kind should drive impossible ids.
  • gix-blame Same sentinel cleanup where SHA1 null ids are only placeholders.
  • gix-traverse Replace remaining SHA1 defaults in traversal state with caller/repository hash kind where traversal starts from generic object ids.
  • broad repo sweep Review remaining Kind::Sha1.null() occurrences one by one and separate acceptable sentinels from real SHA1 assumptions.

Batch 6: acceptance coverage

  • dual-hash refs/object read suite
  • dual-hash write/read suite
  • repo-level transition or conversion verification
  • CI gating for SHA1 and SHA256 critical paths

Immediate Next Moves

  • Fix SHA256-only gix compilation by removing unconditional Kind::Sha1 references from config/cache code paths.
  • Make fetch negotiation accept object-format=sha256.
  • Remove clone-time unimplemented!() for remote hash mismatch.
  • Turn current scattered dual-hash tests into named acceptance criteria.

Exit Criteria

  • SHA256 repository format parses through config without immediate rejection.
  • SHA256-only top-level gix build compiles when requested by features.
  • protocol negotiation can roundtrip object-format=sha256.
  • clone/fetch can initialize repo state for non-SHA1 remotes without panicking or aborting.
  • no important code path relies on implicit SHA1-only object-id shape.
  • CI and local test entrypoints exercise both SHA1 and SHA256 where behavior differs.