mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Fix remote turn diff display roots (#23261)
## Why `TurnDiffTracker` computes a display root so turn diffs can be rendered repo-relative. For remote exec-server turns, the selected turn `cwd` may exist only inside the selected environment, but `run_turn` was discovering the git root through the local host filesystem. When that lookup failed, nested remote-session diffs fell back to the nested `cwd` and showed `/tmp/...`-prefixed paths instead of repo-relative paths. ## What changed - Resolve the diff display root from the primary selected turn environment when one exists, using that environment's filesystem and `cwd`. - Add `codex_git_utils::get_git_repo_root_with_fs(...)` so git-root discovery can run against an `ExecutorFileSystem`, including remote environments. - Reuse that helper from `resolve_root_git_project_for_trust(...)` and add coverage for `.git` gitdir-pointer detection. ## Validation - Devbox Bazel: `//codex-rs/core:core-unit-tests --test_filter=get_git_repo_root_with_fs_detects_gitdir_pointer` - Devbox Docker-backed remote-env repro: `//codex-rs/core:core-all-test --test_filter=apply_patch_turn_diff_paths_stay_repo_relative_when_session_cwd_is_nested`
This commit is contained in:
committed by
GitHub
Unverified
parent
bb43044cba
commit
9286ff2805
@@ -2,6 +2,7 @@ use codex_exec_server::LOCAL_FS;
|
||||
use codex_git_utils::GitInfo;
|
||||
use codex_git_utils::GitSha;
|
||||
use codex_git_utils::collect_git_info;
|
||||
use codex_git_utils::get_git_repo_root_with_fs;
|
||||
use codex_git_utils::get_has_changes;
|
||||
use codex_git_utils::git_diff_to_remote;
|
||||
use codex_git_utils::recent_commits;
|
||||
@@ -526,6 +527,20 @@ async fn resolve_root_git_project_for_trust_returns_none_outside_repo() {
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_git_repo_root_with_fs_detects_gitdir_pointer() {
|
||||
let tmp = TempDir::new().expect("tempdir");
|
||||
let proj = tmp.path().join("proj");
|
||||
let nested = proj.join("nested");
|
||||
std::fs::create_dir_all(&nested).unwrap();
|
||||
std::fs::write(proj.join(".git"), "gitdir: /tmp/fake-worktree\n").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
get_git_repo_root_with_fs(LOCAL_FS.as_ref(), &nested.abs()).await,
|
||||
Some(proj.abs())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_root_git_project_for_trust_regular_repo_returns_repo_root() {
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp dir");
|
||||
|
||||
@@ -71,6 +71,7 @@ use codex_analytics::build_track_events_context;
|
||||
use codex_async_utils::OrCancelExt;
|
||||
use codex_features::Feature;
|
||||
use codex_git_utils::get_git_repo_root;
|
||||
use codex_git_utils::get_git_repo_root_with_fs;
|
||||
use codex_hooks::HookEvent;
|
||||
use codex_hooks::HookEventAfterAgent;
|
||||
use codex_hooks::HookPayload;
|
||||
@@ -370,8 +371,17 @@ pub(crate) async fn run_turn(
|
||||
// Although from the perspective of codex.rs, TurnDiffTracker has the lifecycle of a Task which contains
|
||||
// many turns, from the perspective of the user, it is a single turn.
|
||||
#[allow(deprecated)]
|
||||
let display_root = get_git_repo_root(turn_context.cwd.as_path())
|
||||
.unwrap_or_else(|| turn_context.cwd.clone().into_path_buf());
|
||||
let display_root = match turn_context.environments.primary() {
|
||||
Some(turn_environment) => get_git_repo_root_with_fs(
|
||||
turn_environment.environment.get_filesystem().as_ref(),
|
||||
&turn_environment.cwd,
|
||||
)
|
||||
.await
|
||||
.unwrap_or_else(|| turn_environment.cwd.clone())
|
||||
.into_path_buf(),
|
||||
None => get_git_repo_root(turn_context.cwd.as_path())
|
||||
.unwrap_or_else(|| turn_context.cwd.clone().into_path_buf()),
|
||||
};
|
||||
let turn_diff_tracker = Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::with_display_root(
|
||||
display_root,
|
||||
)));
|
||||
|
||||
@@ -38,6 +38,23 @@ pub fn get_git_repo_root(base_dir: &Path) -> Option<PathBuf> {
|
||||
find_ancestor_git_entry(base).map(|(repo_root, _)| repo_root)
|
||||
}
|
||||
|
||||
/// Return the repository root for `cwd` using the provided filesystem.
|
||||
///
|
||||
/// This mirrors [`get_git_repo_root`] for local paths, but works when `cwd`
|
||||
/// only exists inside a selected remote environment.
|
||||
pub async fn get_git_repo_root_with_fs(
|
||||
fs: &dyn ExecutorFileSystem,
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> Option<AbsolutePathBuf> {
|
||||
let base = match fs.get_metadata(cwd, /*sandbox*/ None).await {
|
||||
Ok(metadata) if metadata.is_directory => cwd.clone(),
|
||||
_ => cwd.parent()?,
|
||||
};
|
||||
find_ancestor_git_entry_with_fs(fs, &base)
|
||||
.await
|
||||
.map(|(repo_root, _)| repo_root)
|
||||
}
|
||||
|
||||
/// Timeout for git commands to prevent freezing on large repositories
|
||||
const GIT_COMMAND_TIMEOUT: TokioDuration = TokioDuration::from_secs(5);
|
||||
const DISABLED_HOOKS_PATH: &str = if cfg!(windows) { "NUL" } else { "/dev/null" };
|
||||
@@ -729,11 +746,8 @@ pub async fn resolve_root_git_project_for_trust(
|
||||
fs: &dyn ExecutorFileSystem,
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> Option<AbsolutePathBuf> {
|
||||
let base = match fs.get_metadata(cwd, /*sandbox*/ None).await {
|
||||
Ok(metadata) if metadata.is_directory => cwd.clone(),
|
||||
_ => cwd.parent()?,
|
||||
};
|
||||
let (repo_root, dot_git) = find_ancestor_git_entry_with_fs(fs, &base).await?;
|
||||
let repo_root = get_git_repo_root_with_fs(fs, cwd).await?;
|
||||
let dot_git = repo_root.join(".git");
|
||||
if fs
|
||||
.get_metadata(&dot_git, /*sandbox*/ None)
|
||||
.await
|
||||
|
||||
@@ -31,6 +31,7 @@ pub use info::default_branch_name;
|
||||
pub use info::get_git_remote_urls;
|
||||
pub use info::get_git_remote_urls_assume_git_repo;
|
||||
pub use info::get_git_repo_root;
|
||||
pub use info::get_git_repo_root_with_fs;
|
||||
pub use info::get_has_changes;
|
||||
pub use info::get_head_commit_hash;
|
||||
pub use info::git_diff_to_remote;
|
||||
|
||||
Reference in New Issue
Block a user