Skip to content

Skip object database lookups for the null id when diffing - #15746

Closed
krlvi wants to merge 1 commit into
masterfrom
diff-skip-null-id-lookup
Closed

Skip object database lookups for the null id when diffing#15746
krlvi wants to merge 1 commit into
masterfrom
diff-skip-null-id-lookup

Conversation

@krlvi

@krlvi krlvi commented Sep 4, 2026

Copy link
Copy Markdown
Member

The performance story

but status took 5.4 seconds in a sandbox clone of this repository with ~240 changed files, while git status took 0.09s in the same checkout. Sampling the process showed 85% of wall time inside gix_odb::Store::consolidate_with_disk_state: readdir plus a stat() of every file in .git/objects/pack, repeated once per changed file. That checkout has 555 packs, so each rescan touched ~1900 files.

The chain that gets there:

  1. but-core hands the diff platform the null id for worktree-side resources (filter_from_state only sets new_root when the id is null).
  2. With core.autocrlf=input (or a text=auto attribute), gix's end-of-line filter runs in auto-text mode and asks the object database for the index version of the file, using that id, to decide whether CRLF conversion is safe.
  3. A null id can never be found. After all pack indices are loaded, a miss under the default RefreshMode::AfterAllIndicesLoaded makes gix rescan the pack directory in case a new pack appeared.

So the cost is roughly changed files × pack files in stat() calls, all system time, and it scales with how fragmented the object store is. core.autocrlf=input is a common global setting on macOS and Linux, so this is not specific to the sandbox.

The fix

compute_with_filter now wraps the repository in a small Find/FindHeader shim that answers null ids with "not found" without consulting the object database. Every other lookup passes through unchanged. The desktop app's worktree diffs share this code path, so it benefits as well.

The proper long-term fix belongs in gix-diff's convert_to_diffable, which should skip the lookup when the id is null; this shim is harmless to keep either way.

Benchmark

Sandbox: gitbutler clone, 555 packs, 1852 refs, 238 uncommitted files, core.autocrlf=input. Debug build, warm cache, best of three.

but status --json real user sys
before 5.41s 0.70s 2.39s
after 0.41s 0.23s 0.22s

Output is byte-identical before and after. What remains is graph traversal over the sandbox's 36k commits and ordinary diffing.

Worktree-side resources reach the diff platform with the null id. With
core.autocrlf=input (or text=auto), the end-of-line filter looks that id up
to compare against the index version, the lookup misses, and gix rescans the
pack directory on every miss. In a repository with hundreds of packs this made
`but status` spend seconds in stat() for a couple of hundred changed files.
The repository is now wrapped so null-id lookups answer immediately.
Copilot AI lite review requested due to automatic review settings September 4, 2026 14:59
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T15:05:11.563811Z e152da0 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added the rust Pull requests that update Rust code label Sep 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change is narrowly scoped to object lookup behavior for null IDs, matches the described root cause, and does not appear to alter diff output semantics for non-null objects.

Pull request overview

This PR improves but status performance by avoiding expensive object database refresh work triggered by lookups of the null object id during worktree diff generation in but-core.

Changes:

  • Wraps gix::Repository with a small Find/FindHeader shim that immediately returns “not found” for null OIDs.
  • Routes diff-platform object lookups through that shim when setting diff resources, preventing ODB rescans on guaranteed misses.
File summaries
File Description
crates/but-core/src/unified_diff.rs Adds a NullIdIsMissing Find/FindHeader wrapper and uses it in UnifiedPatch::compute_with_filter() to short-circuit null-id lookups during diff preparation.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Byron Byron left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's quite a footgun, particularly because ultimately I ran into it I suppose. Regarding NullIds being looked up, I will take a look at that in gix as my understanding is that these already serve as marker to say such resources should be looked up in the worktree.

GitoxideLabs/gitoxide#2853 contains many improvements, and one of them is that after the first pack refresh, it will have a cooldown during which it won't trigger a refresh again, effectively neutering this foot gun.

Let's merge this and see if I can ship a better fix with #15676 as well, after all I am still puzzled why this lookup on null happens at all.

@slarse

slarse commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Holding off on merging this until I've got my performance benchmarks up and runng. I think I've got a working repro of the performance characteristics of this PR in a controlled environment. Will try to get the benchies running today so we can merge probably tomorrow.

@slarse

slarse commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

This is made obsolete by #15676

@slarse slarse closed this Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants