[codex] preserve fsmonitor for worktree Git reads (#26880)

Codex forces `core.fsmonitor=false` on internal Git commands so a
repository cannot select an executable fsmonitor helper. This also
disables Git's built-in daemon for `status`, `diff`, and `ls-files`,
turning those worktree reads into full scans in large repositories.

Read the raw effective `core.fsmonitor` value and preserve it only when
Git interprets it as true and advertises built-in daemon support through
`git version --build-options`. Query uncommon boolean spellings back
through Git using the exact effective value. Unset, false, helper paths,
malformed values, probe failures, and unsupported Git builds continue to
force `core.fsmonitor=false`.

Centralize this policy in `git-utils` while keeping process execution in
the existing local and workspace-command adapters. Probe once per
worktree workflow and reuse the result for its Git commands, including
the TUI `/diff` path. Metadata-only commands and repository discovery
remain disabled without probing. Each probe and requested Git process
keeps its own existing timeout, and the decision is not cached because
layered and conditional Git configuration can change while Codex runs.

---------

Co-authored-by: Chris Bookholt <bookholt@openai.com>
This commit is contained in:
Tamir Duberstein
2026-06-08 21:32:46 -07:00
committed by GitHub
Unverified
parent 08cb633c06
commit dffc4bf75d
6 changed files with 831 additions and 203 deletions
+209 -10
View File
@@ -279,7 +279,10 @@ fn trim_git_suffix(value: &str) -> &str {
}
pub async fn get_has_changes(cwd: &Path) -> Option<bool> {
let output = run_git_command_with_timeout(&["status", "--porcelain"], cwd).await?;
let git = Path::new("git");
let fsmonitor = detect_local_fsmonitor_override(git, cwd).await;
let output =
run_git_command_with_timeout_from(git, &["status", "--porcelain"], cwd, fsmonitor).await?;
if !output.status.success() {
return None;
}
@@ -390,12 +393,53 @@ pub async fn git_diff_to_remote(cwd: &Path) -> Option<GitDiffToRemote> {
/// Run a git command with a timeout to prevent blocking on large repositories
async fn run_git_command_with_timeout(args: &[&str], cwd: &Path) -> Option<std::process::Output> {
let mut command = Command::new("git");
// These callers only inspect repository metadata. Worktree workflows probe
// once and pass their override directly to the lower-level runner.
run_git_command_with_timeout_from(
Path::new("git"),
args,
cwd,
crate::FsmonitorOverride::Disabled,
)
.await
}
struct LocalFsmonitorProbeRunner<'a> {
git: &'a Path,
cwd: &'a Path,
}
impl crate::FsmonitorProbeRunner for LocalFsmonitorProbeRunner<'_> {
async fn run_probe(&mut self, args: &[&str]) -> Option<Vec<u8>> {
// Both probes are fast, bounded metadata queries that do not inspect the
// worktree or index, so do not reduce the requested command's timeout.
let mut command = Command::new(self.git);
command.args(args).current_dir(self.cwd).kill_on_drop(true);
match timeout(GIT_COMMAND_TIMEOUT, command.output()).await {
Ok(Ok(output)) if output.status.success() => Some(output.stdout),
_ => None,
}
}
}
async fn detect_local_fsmonitor_override(git: &Path, cwd: &Path) -> crate::FsmonitorOverride {
let mut runner = LocalFsmonitorProbeRunner { git, cwd };
crate::detect_fsmonitor_override(&mut runner).await
}
async fn run_git_command_with_timeout_from(
git: &Path,
args: &[&str],
cwd: &Path,
fsmonitor: crate::FsmonitorOverride,
) -> Option<std::process::Output> {
let mut command = Command::new(git);
command
.env("GIT_OPTIONAL_LOCKS", "0")
// Keep internal Git helper commands independent of configured hook directories.
// Keep internal Git commands independent of repository-selected hooks
// and fsmonitor helpers while preserving built-in fsmonitor acceleration.
.args(["-c", &format!("core.hooksPath={DISABLED_HOOKS_PATH}")])
.args(["-c", "core.fsmonitor=false"])
.args(["-c", fsmonitor.git_config_arg()])
.args(args)
.current_dir(cwd)
.kill_on_drop(true);
@@ -684,9 +728,15 @@ async fn find_closest_sha(cwd: &Path, branches: &[String], remotes: &[String]) -
}
async fn diff_against_sha(cwd: &Path, sha: &GitSha) -> Option<String> {
let output =
run_git_command_with_timeout(&["diff", "--no-textconv", "--no-ext-diff", &sha.0], cwd)
.await?;
let git = Path::new("git");
let fsmonitor = detect_local_fsmonitor_override(git, cwd).await;
let output = run_git_command_with_timeout_from(
git,
&["diff", "--no-textconv", "--no-ext-diff", &sha.0],
cwd,
fsmonitor,
)
.await?;
// 0 is success and no diff.
// 1 is success but there is a diff.
let exit_ok = output.status.code().is_some_and(|c| c == 0 || c == 1);
@@ -695,8 +745,13 @@ async fn diff_against_sha(cwd: &Path, sha: &GitSha) -> Option<String> {
}
let mut diff = String::from_utf8(output.stdout).ok()?;
if let Some(untracked_output) =
run_git_command_with_timeout(&["ls-files", "--others", "--exclude-standard"], cwd).await
if let Some(untracked_output) = run_git_command_with_timeout_from(
git,
&["ls-files", "--others", "--exclude-standard"],
cwd,
fsmonitor,
)
.await
&& untracked_output.status.success()
{
let untracked: Vec<String> = String::from_utf8(untracked_output.stdout)
@@ -722,7 +777,7 @@ async fn diff_against_sha(cwd: &Path, sha: &GitSha) -> Option<String> {
null_device,
&file_owned,
];
run_git_command_with_timeout(&args_vec, cwd).await
run_git_command_with_timeout_from(git, &args_vec, cwd, fsmonitor).await
});
let results = join_all(futures_iter).await;
for extra in results.into_iter().flatten() {
@@ -849,6 +904,8 @@ pub async fn current_branch_name(cwd: &Path) -> Option<String> {
mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
#[test]
fn canonicalize_git_remote_url_normalizes_github_variants() {
@@ -886,4 +943,146 @@ mod tests {
assert_eq!(canonicalize_git_remote_url(remote), None);
}
}
#[cfg(unix)]
#[tokio::test]
async fn fsmonitor_override_rejects_configured_helper() {
let temp_dir = tempfile::tempdir().expect("create temp dir");
let git = temp_dir.path().join("git");
let log = temp_dir.path().join("git.log");
std::fs::write(
&git,
"#!/bin/sh\n\
printf '%s\\n' \"$*\" >>\"$0.log\"\n\
case \"$1\" in\n\
config) printf '/tmp/fsmonitor-helper\\000' ;;\n\
*) printf 'worktree output\\n' ;;\n\
esac\n",
)
.expect("write fake Git");
let mut permissions = std::fs::metadata(&git)
.expect("read fake Git metadata")
.permissions();
permissions.set_mode(0o755);
std::fs::set_permissions(&git, permissions).expect("mark fake Git executable");
// The config response mirrors:
// git -c core.fsmonitor=/tmp/fsmonitor-helper \
// config --null --get core.fsmonitor
let fsmonitor = detect_local_fsmonitor_override(&git, temp_dir.path()).await;
let output = run_git_command_with_timeout_from(
&git,
&["status", "--porcelain"],
temp_dir.path(),
fsmonitor,
)
.await
.expect("run fake Git");
assert_eq!(
(output.status.code(), output.stdout),
(Some(0), b"worktree output\n".to_vec())
);
let disabled_hooks = format!("core.hooksPath={DISABLED_HOOKS_PATH}");
assert_eq!(
std::fs::read_to_string(log)
.expect("read fake Git log")
.lines()
.map(str::to_string)
.collect::<Vec<_>>(),
vec![
"config --null --get core.fsmonitor".to_string(),
"config --null --type=bool --fixed-value --get core.fsmonitor /tmp/fsmonitor-helper"
.to_string(),
format!("-c {disabled_hooks} -c core.fsmonitor=false status --porcelain"),
]
);
}
#[cfg(unix)]
#[tokio::test]
async fn fsmonitor_override_uses_effective_layered_config_value() {
let temp_dir = tempfile::tempdir().expect("create temp dir");
let repo = temp_dir.path().join("repo");
std::fs::create_dir(&repo).expect("create repository directory");
let init_status = std::process::Command::new("git")
.args(["init", "-q"])
.current_dir(&repo)
.status()
.expect("initialize test repository");
assert_eq!(init_status.code(), Some(0), "initialize test repository");
let git = temp_dir.path().join("git");
let global_config = temp_dir.path().join("git.global");
let log = temp_dir.path().join("git.log");
std::fs::write(
&git,
"#!/bin/sh\n\
printf '%s\\n' \"$*\" >>\"$0.log\"\n\
case \"$1\" in\n\
config)\n\
GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=\"$0.global\" exec git \"$@\"\n\
;;\n\
version) printf 'feature: fsmonitor--daemon\\n' ;;\n\
*) printf 'worktree output\\n' ;;\n\
esac\n",
)
.expect("write layered-config Git");
let mut permissions = std::fs::metadata(&git)
.expect("read layered-config Git metadata")
.permissions();
permissions.set_mode(0o755);
std::fs::set_permissions(&git, permissions).expect("mark layered-config Git executable");
let global_status = std::process::Command::new("git")
.args([
"config",
"--file",
global_config.to_str().expect("global config path"),
"core.fsmonitor",
"/tmp/fsmonitor-helper",
])
.status()
.expect("write global fsmonitor helper");
assert_eq!(
global_status.code(),
Some(0),
"write global fsmonitor helper"
);
let local_status = std::process::Command::new("git")
.args(["config", "core.fsmonitor", "true"])
.current_dir(&repo)
.status()
.expect("write local built-in fsmonitor config");
assert_eq!(
local_status.code(),
Some(0),
"write local built-in fsmonitor config"
);
let fsmonitor = detect_local_fsmonitor_override(&git, repo.as_path()).await;
let output = run_git_command_with_timeout_from(
&git,
&["status", "--porcelain"],
repo.as_path(),
fsmonitor,
)
.await
.expect("run Git with layered config");
assert_eq!(
(output.status.code(), output.stdout),
(Some(0), b"worktree output\n".to_vec())
);
let actual = std::fs::read_to_string(log).expect("read layered-config Git log");
let disabled_hooks = format!("core.hooksPath={DISABLED_HOOKS_PATH}");
assert_eq!(
actual.lines().map(str::to_string).collect::<Vec<_>>(),
vec![
"config --null --get core.fsmonitor".to_string(),
"version --build-options".to_string(),
format!("-c {disabled_hooks} -c core.fsmonitor=true status --porcelain"),
]
);
}
}