diff --git a/codex-rs/git-utils/src/patch_paths.rs b/codex-rs/git-utils/src/patch_paths.rs index 0c98df22de..d6889fce89 100644 --- a/codex-rs/git-utils/src/patch_paths.rs +++ b/codex-rs/git-utils/src/patch_paths.rs @@ -240,7 +240,7 @@ pub(crate) fn stage_effective_paths( ) -> io::Result<()> { let confined = confine_patch_paths(git, git_root, paths)?; let mut existing = Vec::new(); - for path in confined.into_exact_leaves() { + for path in confined.into_exact_leaves()? { let joined = git_root.join(&path); if let Ok(metadata) = std::fs::symlink_metadata(&joined) { if leaf_is_traversable_directory(metadata.file_type()) { @@ -324,10 +324,28 @@ pub(crate) struct ConfinedPatchPaths { } impl ConfinedPatchPaths { - fn into_exact_leaves(self) -> Vec { + fn into_exact_leaves(self) -> io::Result> { self.entries .into_iter() - .map(|entry| entry.exact_leaf) + .map(|entry| { + let expected_depth = entry.exact_leaf.split('/').count(); + entry + .candidates + .into_iter() + .find(|candidate| { + candidate.origin == ConfinedPathOrigin::Raw + && candidate.role == ConfinedPathRole::Leaf + && candidate.depth == expected_depth + && candidate.path == entry.exact_leaf + }) + .map(|candidate| candidate.path) + .ok_or_else(|| { + io::Error::new( + io::ErrorKind::InvalidData, + "confined patch path is missing its exact raw leaf", + ) + }) + }) .collect() } } diff --git a/codex-rs/git-utils/src/patch_paths_tests.rs b/codex-rs/git-utils/src/patch_paths_tests.rs index 0616c460cc..8a0e72e21a 100644 --- a/codex-rs/git-utils/src/patch_paths_tests.rs +++ b/codex-rs/git-utils/src/patch_paths_tests.rs @@ -475,13 +475,15 @@ fn assert_longest_existing_alias_prefix_carries_unresolved_suffix() { assert_eq!( confine_patch_paths(&git, root, &paths) .expect("confine exact leaves") - .into_exact_leaves(), + .into_exact_leaves() + .expect("extract exact leaves"), paths ); assert!( confine_patch_paths(&git, root, &[]) .unwrap() .into_exact_leaves() + .expect("extract no exact leaves") .is_empty() ); use ConfinedPathOrigin::*;