diff --git a/codex-rs/worktree/src/manager.rs b/codex-rs/worktree/src/manager.rs index e0b20248e9..1492b4ed51 100644 --- a/codex-rs/worktree/src/manager.rs +++ b/codex-rs/worktree/src/manager.rs @@ -25,7 +25,7 @@ pub fn ensure_worktree(req: WorktreeRequest) -> Result { let branch = req.branch.clone(); let slug = paths::slugify_name(&branch)?; ensure_safe_branch_name(&repo.root, &branch)?; - let worktree_git_root = paths::sibling_worktree_git_root(&repo.root, &branch)?; + let worktree_git_root = paths::sibling_worktree_git_root(&repo.primary_root, &branch)?; let workspace_cwd = worktree_git_root.join(&repo.relative_cwd); if worktree_git_root.exists() { @@ -445,6 +445,7 @@ fn display_branch_or_name(info: &WorktreeInfo) -> &str { struct SourceRepo { root: PathBuf, + primary_root: PathBuf, relative_cwd: PathBuf, common_git_dir: PathBuf, repo_name: String, @@ -462,9 +463,13 @@ impl SourceRepo { let common_git_dir = absolutize(&source_cwd, Path::new(&common_git_dir_raw)) .canonicalize() .unwrap_or_else(|_| absolutize(&source_cwd, Path::new(&common_git_dir_raw))); + let primary_root = primary_worktree_root(&root) + .unwrap_or_else(|_| root.clone()) + .canonicalize() + .unwrap_or_else(|_| root.clone()); let origin = git::stdout(&root, &["remote", "get-url", "origin"]).ok(); let id = paths::repo_fingerprint(&common_git_dir, origin.as_deref()); - let repo_name = root + let repo_name = primary_root .file_name() .context("repository root has no directory name")? .to_string_lossy() @@ -475,6 +480,7 @@ impl SourceRepo { .to_path_buf(); Ok(Self { root, + primary_root, relative_cwd, common_git_dir, repo_name, diff --git a/codex-rs/worktree/tests/git_backend.rs b/codex-rs/worktree/tests/git_backend.rs index 6f9bbc4cdc..473108c487 100644 --- a/codex-rs/worktree/tests/git_backend.rs +++ b/codex-rs/worktree/tests/git_backend.rs @@ -94,6 +94,36 @@ fn creates_reuses_lists_and_removes_managed_worktree() -> anyhow::Result<()> { Ok(()) } +#[test] +fn creates_sibling_from_sibling_using_primary_repo_name() -> anyhow::Result<()> { + let fixture = GitFixture::new()?; + let first = codex_worktree::ensure_worktree(WorktreeRequest { + codex_home: fixture.codex_home.path().to_path_buf(), + source_cwd: fixture.repo.path().to_path_buf(), + branch: "fcoury/worktrees".to_string(), + base_ref: None, + dirty_policy: DirtyPolicy::Fail, + })?; + + let second = codex_worktree::ensure_worktree(WorktreeRequest { + codex_home: fixture.codex_home.path().to_path_buf(), + source_cwd: first.info.workspace_cwd, + branch: "fcoury/test".to_string(), + base_ref: None, + dirty_policy: DirtyPolicy::Fail, + })?; + + let canonical_repo = fixture.repo.path().canonicalize()?; + assert_eq!( + second.info.worktree_git_root, + canonical_repo.with_file_name(format!( + "{}.fcoury-test", + canonical_repo.file_name().unwrap().to_string_lossy() + )) + ); + Ok(()) +} + #[test] fn copy_tracked_preserves_staged_and_unstaged_diffs() -> anyhow::Result<()> { let fixture = GitFixture::new()?;