Skip to content

fix: handle loose ref path-prefix collisions#2645

Merged
Sebastian Thiel (Byron) merged 2 commits into
mainfrom
try-find-reference-path-prefix-collision
Jun 16, 2026
Merged

fix: handle loose ref path-prefix collisions#2645
Sebastian Thiel (Byron) merged 2 commits into
mainfrom
try-find-reference-path-prefix-collision

Conversation

@Byron

@Byron Sebastian Thiel (Byron) commented Jun 15, 2026

Copy link
Copy Markdown
Member

Tasks

  • Reproduce the loose-reference path-prefix collision.
  • Ensure repo.try_find_reference() returns Ok(None) for that case.
  • Handle the Windows ref_contents() normalization case without falling through to packed refs.
  • Address CI fallout from the transaction regression test.
  • Address review feedback about prefix-helper metadata checks.

Created by Codex on behalf of Byron. Byron will review before this is ready to merge.

Reported issue

$issue-full-auto Look at the fix in gitbutlerapp/gitbutler#14153 and reproduce the underlying issue in gix or gix-ref (or both). Then make sure that repo.try_find_reference() returns Ok(None) in that moment.

Context

GitButler PR: gitbutlerapp/gitbutler#14153

The issue is a loose reference path-prefix collision: refs/heads/A exists as a file, and lookup asks for refs/heads/A/new.

Git baseline: /Users/byron/dev/github.com/git/git at 7760f83b59; refs/refs-internal.h documents ENOTDIR as “ref prefix is not a directory”.

Changes

  • Treat NotADirectory as absence for the current loose ref lookup candidate in gix-ref find.
  • Make ref_contents() preserve Windows path-prefix collisions as NotADirectory instead of normalizing them to ordinary absence.
  • Avoid packed-ref fallback when an existing loose ref file is a path prefix of the requested ref, including cases where Windows reports the collision as NotFound or PermissionDenied.
  • Add gix-ref and gix regression tests, including packed-overlay coverage for the collision behavior.
  • Update the transaction rename/delete regression to expect the earlier NotADirectory error now surfaced on Windows too.
  • Address review feedback by making the Windows prefix helper use explicit metadata checks and stop early when an intermediate prefix is missing.

Validation

  • cargo fmt --check
  • cargo check -p gix-ref --target aarch64-pc-windows-msvc --features sha1
  • cargo test -p gix-ref --test refs file::transaction::prepare_and_commit::delete::rename_a_to_a_slash_b_in_one_transaction -- --exact
  • cargo test -p gix-ref --test refs file::store::find::loose::prefix_file_collision_is_not_found -- --exact
  • cargo test -p gix --test gix try_find_reference_with_existing_ref_as_path_prefix_returns_none

Head validated locally: 27c0ba6b4f6ab96300cf5f67764a6f1ac65b6047

@Byron
Sebastian Thiel (Byron) force-pushed the try-find-reference-path-prefix-collision branch 2 times, most recently from b7324d7 to 7ea1274 Compare June 15, 2026 02:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes loose-ref lookup behavior when a reference file exists that is a path prefix of the requested ref (e.g. refs/heads/A exists as a file while looking up refs/heads/A/new), ensuring try_find_reference()/try_find() return Ok(None) and don’t incorrectly fall back to packed-refs.

Changes:

  • Update gix-ref file-store lookup to treat NotADirectory as “not found” for that lookup and to avoid packed-ref fallback in prefix-collision scenarios.
  • Add regression tests in gix-ref for loose + packed-overlay behavior around prefix collisions (with Windows-specific conditional coverage).
  • Add a gix regression test asserting repo.try_find_reference() returns None for the collision case.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
gix-ref/src/store/file/find.rs Adjusts loose ref lookup error handling and adds prefix-collision detection to prevent packed fallback.
gix-ref/tests/refs/file/store/find.rs Adds regression coverage for prefix collisions (including packed-overlay behavior on non-Windows).
gix/tests/gix/repository/reference.rs Adds repository-level regression test for try_find_reference() collision behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gix-ref/src/store/file/find.rs Outdated
The GitButler branch creation flow reported that
`repo.try_find_reference("refs/heads/A/new")` could fail with a low-level
`NotADirectory` error when `refs/heads/A` already exists as a loose ref. That
lookup is asking whether the longer ref exists; the path-prefix collision
matters to creation/update code, but find should report absence for that
candidate.

Git reference: `refs/refs-internal.h` documents `ENOTDIR` as the case where a
ref prefix is not a directory, alongside `ENOENT` for non-existing refs. For
lookup, both mean the requested ref candidate was not found.

Co-authored-by: Sebastian Thiel <[email protected]>
@Byron
Sebastian Thiel (Byron) force-pushed the try-find-reference-path-prefix-collision branch from 7ea1274 to 9f432ef Compare June 16, 2026 02:43
@Byron
Sebastian Thiel (Byron) marked this pull request as ready for review June 16, 2026 02:43
Copilot AI review requested due to automatic review settings June 16, 2026 02:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f432ef509

ℹ️ 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 (@codex) review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "Codex (@codex) address that feedback".

Comment thread gix-ref/src/store/file/find.rs
Copilot AI review requested due to automatic review settings June 16, 2026 03:09
@Byron
Sebastian Thiel (Byron) force-pushed the try-find-reference-path-prefix-collision branch from c0cf531 to 84400b8 Compare June 16, 2026 03:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comment thread gix-ref/src/store/file/find.rs
On Windows, opening a path below an existing loose ref file can surface as
NotFound or PermissionDenied and ref_contents() previously normalized those
cases to Ok(None). That allowed callers to continue to packed refs even though
the loose namespace path was blocked.

Teach ref_contents() to detect file prefixes before normalizing those Windows
errors, and return NotADirectory for that specific collision so existing lookup
handling returns Ok(None) without consulting packed refs.

Co-authored-by: Sebastian Thiel <[email protected]>
@Byron
Sebastian Thiel (Byron) force-pushed the try-find-reference-path-prefix-collision branch from 27c0ba6 to c9191ab Compare June 16, 2026 04:07
@Byron
Sebastian Thiel (Byron) merged commit 4f089fc into main Jun 16, 2026
32 checks passed
@Byron
Sebastian Thiel (Byron) deleted the try-find-reference-path-prefix-collision branch June 16, 2026 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants