Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Allow nested Git repositories in the Windows sandbox (#38080)
## Why

Git rejects repositories owned by the primary user when commands run as the
sandbox user. Trusting only the enclosing worktree leaves nested repositories
unavailable.

## What changed

Add both the worktree root and its `/*` wildcard to Git's `safe.directory`
environment configuration. Update the directory and gitfile unit tests to
expect both entries.

GitOrigin-RevId: 4b8e2bc8cc52cff580451ffbd17eb71325957404
  • Loading branch information
iceweasel-oai authored and copyberry committed Aug 11, 2026
commit 7c47952f7c2c27f7d45cb7f9d4a42671814ef71e
41 changes: 22 additions & 19 deletions codex-rs/windows-sandbox-rs/src/sandbox_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,25 @@ pub fn ensure_codex_home_exists(p: &Path) -> Result<()> {
Ok(())
}

/// Adds a git safe.directory entry to the environment when running inside a repository.
/// git will not otherwise allow the Sandbox user to run git commands on the repo directory
/// which is owned by the primary user.
/// Adds git safe.directory entries for the repository and nested repositories.
/// git will not otherwise allow the Sandbox user to run git commands on repositories
/// which are owned by the primary user.
pub fn inject_git_safe_directory(env_map: &mut HashMap<String, String>, cwd: &Path) {
if let Some(git_root) = find_git_worktree_root_for_safe_directory(cwd) {
let mut cfg_count: usize = env_map
.get("GIT_CONFIG_COUNT")
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or(0);
let git_path = git_root.to_string_lossy().replace("\\\\", "/");
env_map.insert(
format!("GIT_CONFIG_KEY_{cfg_count}"),
"safe.directory".to_string(),
);
env_map.insert(format!("GIT_CONFIG_VALUE_{cfg_count}"), git_path);
cfg_count += 1;
let nested_git_path = format!("{git_path}/*");
for git_path in [git_path, nested_git_path] {
env_map.insert(
format!("GIT_CONFIG_KEY_{cfg_count}"),
"safe.directory".to_string(),
);
env_map.insert(format!("GIT_CONFIG_VALUE_{cfg_count}"), git_path);
cfg_count += 1;
}
env_map.insert("GIT_CONFIG_COUNT".to_string(), cfg_count.to_string());
}
}
Expand Down Expand Up @@ -77,13 +80,13 @@ mod tests {
let mut env_map = HashMap::new();
inject_git_safe_directory(&mut env_map, &nested);

let git_path = safe_directory_value(&repo);
let expected = HashMap::from([
("GIT_CONFIG_COUNT".to_string(), "1".to_string()),
("GIT_CONFIG_COUNT".to_string(), "2".to_string()),
("GIT_CONFIG_KEY_0".to_string(), "safe.directory".to_string()),
(
"GIT_CONFIG_VALUE_0".to_string(),
safe_directory_value(&repo),
),
("GIT_CONFIG_VALUE_0".to_string(), git_path.clone()),
("GIT_CONFIG_KEY_1".to_string(), "safe.directory".to_string()),
("GIT_CONFIG_VALUE_1".to_string(), format!("{git_path}/*")),
]);
assert_eq!(env_map, expected);
}
Expand All @@ -103,13 +106,13 @@ mod tests {
let mut env_map = HashMap::new();
inject_git_safe_directory(&mut env_map, &nested);

let git_path = safe_directory_value(&repo);
let expected = HashMap::from([
("GIT_CONFIG_COUNT".to_string(), "1".to_string()),
("GIT_CONFIG_COUNT".to_string(), "2".to_string()),
("GIT_CONFIG_KEY_0".to_string(), "safe.directory".to_string()),
(
"GIT_CONFIG_VALUE_0".to_string(),
safe_directory_value(&repo),
),
("GIT_CONFIG_VALUE_0".to_string(), git_path.clone()),
("GIT_CONFIG_KEY_1".to_string(), "safe.directory".to_string()),
("GIT_CONFIG_VALUE_1".to_string(), format!("{git_path}/*")),
]);
assert_eq!(env_map, expected);
}
Expand Down
Loading