Skip to content

[codex] fix add case-renamed tracked paths#435

Open
genedna wants to merge 14 commits into
mainfrom
codex/fix-add-case-alias
Open

[codex] fix add case-renamed tracked paths#435
genedna wants to merge 14 commits into
mainfrom
codex/fix-add-case-alias

Conversation

@genedna

@genedna genedna commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • handle case-renamed tracked paths as aliases during libra add instead of reporting a false LBR-CASE-001 collision
  • keep real case-fold twins protected by comparing same-file identity before suppressing a collision
  • update status/untracked scanning so a case-renamed tracked directory does not continue to appear as ?? Slides/

Root Cause

On case-insensitive filesystems, a directory renamed from tracked slides/... to on-disk Slides/... resolves to the same files. libra add Slides matched those working-tree entries, but the add collision guard compared only folded path strings against the index and treated Slides/... as a new case-fold twin of tracked slides/....

Validation

  • cargo +nightly fmt --all --check
  • LIBRA_SKIP_WEB_BUILD=1 cargo test --test command_test case_handling_test -- --nocapture
  • LIBRA_SKIP_WEB_BUILD=1 cargo test --test command_test add_test:: -- --nocapture
  • LIBRA_SKIP_WEB_BUILD=1 cargo test --test command_test status_test:: -- --nocapture
  • LIBRA_SKIP_WEB_BUILD=1 cargo test --test command_test status_json_test:: -- --nocapture
  • LIBRA_SKIP_WEB_BUILD=1 cargo test path_case::tests::same_file_case_alias_requires_same_entry -- --nocapture
  • LIBRA_SKIP_WEB_BUILD=1 cargo clippy --all-targets --all-features -- -D warnings
  • manual CLI repro in a temp repo: tracked slides/a.txt, renamed directory to Slides, then libra add Slides staged slides/a.txt as modified and status --short no longer reported ?? Slides/

Copilot AI review requested due to automatic review settings July 7, 2026 02:18

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.

Pull request overview

This PR adjusts Libra’s case-handling on case-insensitive filesystems so that case-only renames of already-tracked paths are treated as aliases (same underlying filesystem entry) instead of triggering false LBR-CASE-001 collisions during libra add, and so status/untracked scanning stops reporting the renamed directory as ?? Slides/.

Changes:

  • Add a “same underlying entry” alias check (is_same_file_case_alias) and use it to suppress false case-fold collisions during libra add.
  • Update status and untracked scanning to treat case-renamed tracked paths as tracked aliases (preventing ?? Slides/ from persisting).
  • Add integration + unit tests covering the directory-alias add flow and the alias identity requirement.

Reviewed changes

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

Show a summary per file
File Description
tests/command/case_handling_test.rs Adds an integration test that reproduces the case-renamed tracked directory scenario and asserts correct staging/status JSON behavior.
src/utils/path_case.rs Introduces is_same_file_case_alias and a unit test ensuring aliasing requires same-file identity (not just fold equality).
src/command/add.rs Computes ignore_case once, threads a match context through pathspec logic, and suppresses false collisions when the candidate is a same-entry case alias.
src/command/status.rs Suppresses “new/untracked” classification for worktree entries that are case-only aliases of tracked paths.
src/command/status_untracked.rs Passes tracked-path context into file scanning so case-only aliases are treated as tracked during untracked enumeration.
src/command/status_untracked_paths.rs Adds case-fold-aware tracked descendant detection and same-entry alias checks to avoid collapsing/reporting case-renamed tracked dirs as untracked.

Comment thread src/command/status.rs Outdated
Comment thread src/command/status_untracked_paths.rs Outdated
Comment thread src/command/status_untracked_paths.rs Outdated
@genedna genedna marked this pull request as ready for review July 7, 2026 02:24
genedna added 5 commits July 7, 2026 10:36
Signed-off-by: Eli Ma <eli@patch.sh>
Signed-off-by: Quanyi Ma <eli@patch.sh>
Signed-off-by: Quanyi Ma <eli@patch.sh>
Copilot AI review requested due to automatic review settings July 7, 2026 02:56

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.

Pull request overview

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

Comment thread src/command/status.rs
@genedna

genedna commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@genedna

genedna commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

@claude review

Copilot AI review requested due to automatic review settings July 7, 2026 08:36

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.

Pull request overview

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

Comment thread web/package.json
"vitest": "^4.1.6"
},
"packageManager": "pnpm@11.1.0",
"packageManager": "pnpm@11.10.0",
Comment thread src/command/add.rs
Comment on lines +1256 to +1263
fn path_starts_with_casefold(path: &Path, parent: &Path) -> bool {
let mut path_components = path.components();
for parent_component in parent.components() {
let Some(path_component) = path_components.next() else {
return false;
};
let path_key = crate::utils::path_case::fold_path_key(
path_component.as_os_str().to_string_lossy().as_ref(),
Comment on lines +86 to +93
fn path_starts_with_casefold(path: &Path, parent: &Path) -> bool {
let mut path_components = path.components();
for parent_component in parent.components() {
let Some(path_component) = path_components.next() else {
return false;
};
let path_key = crate::utils::path_case::fold_path_key(
path_component.as_os_str().to_string_lossy().as_ref(),
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.

2 participants