diff --git a/codex-rs/app-server/src/fuzzy_file_search.rs b/codex-rs/app-server/src/fuzzy_file_search.rs index 6c83a0f4ec7..fcb05852ba6 100644 --- a/codex-rs/app-server/src/fuzzy_file_search.rs +++ b/codex-rs/app-server/src/fuzzy_file_search.rs @@ -46,6 +46,7 @@ pub(crate) async fn run_fuzzy_file_search( threads, cancel_flag, COMPUTE_INDICES, + true, ) { Ok(res) => Ok((root, res)), Err(err) => Err((root, err)), diff --git a/codex-rs/core/src/rollout/list.rs b/codex-rs/core/src/rollout/list.rs index b27b3382e32..b142bc2d04e 100644 --- a/codex-rs/core/src/rollout/list.rs +++ b/codex-rs/core/src/rollout/list.rs @@ -1,12 +1,11 @@ use std::cmp::Reverse; use std::io::{self}; +use std::num::NonZero; use std::path::Path; use std::path::PathBuf; - -use codex_file_search as file_search; -use std::num::NonZero; use std::sync::Arc; use std::sync::atomic::AtomicBool; + use time::OffsetDateTime; use time::PrimitiveDateTime; use time::format_description::FormatItem; @@ -15,6 +14,7 @@ use uuid::Uuid; use super::SESSIONS_SUBDIR; use crate::protocol::EventMsg; +use codex_file_search as file_search; use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::RolloutLine; use codex_protocol::protocol::SessionSource; @@ -515,6 +515,7 @@ pub async fn find_conversation_path_by_id_str( threads, cancel, compute_indices, + false, ) .map_err(|e| io::Error::other(format!("file search failed: {e}")))?; diff --git a/codex-rs/core/tests/suite/rollout_list_find.rs b/codex-rs/core/tests/suite/rollout_list_find.rs index 88409a46169..a38f0565c0f 100644 --- a/codex-rs/core/tests/suite/rollout_list_find.rs +++ b/codex-rs/core/tests/suite/rollout_list_find.rs @@ -1,5 +1,6 @@ #![allow(clippy::unwrap_used, clippy::expect_used)] use std::io::Write; +use std::path::Path; use std::path::PathBuf; use codex_core::find_conversation_path_by_id_str; @@ -8,8 +9,8 @@ use uuid::Uuid; /// Create sessions/YYYY/MM/DD and write a minimal rollout file containing the /// provided conversation id in the SessionMeta line. Returns the absolute path. -fn write_minimal_rollout_with_id(codex_home: &TempDir, id: Uuid) -> PathBuf { - let sessions = codex_home.path().join("sessions/2024/01/01"); +fn write_minimal_rollout_with_id(codex_home: &Path, id: Uuid) -> PathBuf { + let sessions = codex_home.join("sessions/2024/01/01"); std::fs::create_dir_all(&sessions).unwrap(); let file = sessions.join(format!("rollout-2024-01-01T00-00-00-{id}.jsonl")); @@ -40,7 +41,7 @@ fn write_minimal_rollout_with_id(codex_home: &TempDir, id: Uuid) -> PathBuf { async fn find_locates_rollout_file_by_id() { let home = TempDir::new().unwrap(); let id = Uuid::new_v4(); - let expected = write_minimal_rollout_with_id(&home, id); + let expected = write_minimal_rollout_with_id(home.path(), id); let found = find_conversation_path_by_id_str(home.path(), &id.to_string()) .await @@ -48,3 +49,33 @@ async fn find_locates_rollout_file_by_id() { assert_eq!(found.unwrap(), expected); } + +#[tokio::test] +async fn find_handles_gitignore_covering_codex_home_directory() { + let repo = TempDir::new().unwrap(); + let codex_home = repo.path().join(".codex"); + std::fs::create_dir_all(&codex_home).unwrap(); + std::fs::write(repo.path().join(".gitignore"), ".codex/**\n").unwrap(); + let id = Uuid::new_v4(); + let expected = write_minimal_rollout_with_id(&codex_home, id); + + let found = find_conversation_path_by_id_str(&codex_home, &id.to_string()) + .await + .unwrap(); + + assert_eq!(found, Some(expected)); +} + +#[tokio::test] +async fn find_ignores_granular_gitignore_rules() { + let home = TempDir::new().unwrap(); + let id = Uuid::new_v4(); + let expected = write_minimal_rollout_with_id(home.path(), id); + std::fs::write(home.path().join("sessions/.gitignore"), "*.jsonl\n").unwrap(); + + let found = find_conversation_path_by_id_str(home.path(), &id.to_string()) + .await + .unwrap(); + + assert_eq!(found, Some(expected)); +} diff --git a/codex-rs/file-search/src/lib.rs b/codex-rs/file-search/src/lib.rs index 82b8fcb6953..17265a85f6b 100644 --- a/codex-rs/file-search/src/lib.rs +++ b/codex-rs/file-search/src/lib.rs @@ -105,6 +105,7 @@ pub async fn run_main( threads, cancel_flag, compute_indices, + true, )?; let match_count = matches.len(); let matches_truncated = total_match_count > match_count; @@ -121,6 +122,7 @@ pub async fn run_main( /// The worker threads will periodically check `cancel_flag` to see if they /// should stop processing files. +#[allow(clippy::too_many_arguments)] pub fn run( pattern_text: &str, limit: NonZero, @@ -129,6 +131,7 @@ pub fn run( threads: NonZero, cancel_flag: Arc, compute_indices: bool, + respect_gitignore: bool, ) -> anyhow::Result { let pattern = create_pattern(pattern_text); // Create one BestMatchesList per worker thread so that each worker can @@ -157,6 +160,14 @@ pub fn run( .hidden(false) // Don't require git to be present to apply to apply git-related ignore rules. .require_git(false); + if !respect_gitignore { + walk_builder + .git_ignore(false) + .git_global(false) + .git_exclude(false) + .ignore(false) + .parents(false); + } if !exclude.is_empty() { let mut override_builder = OverrideBuilder::new(search_directory); diff --git a/codex-rs/tui/src/file_search.rs b/codex-rs/tui/src/file_search.rs index 7f15e46abba..61327cfd245 100644 --- a/codex-rs/tui/src/file_search.rs +++ b/codex-rs/tui/src/file_search.rs @@ -172,6 +172,7 @@ impl FileSearchManager { NUM_FILE_SEARCH_THREADS, cancellation_token.clone(), compute_indices, + true, ) .map(|res| res.matches) .unwrap_or_default();