Files
codex/codex-rs/worktree/src/paths.rs
Benjamin Carlsson 798833fe97 Add managed worktree creation (#42196)
## What changed

- Add `WorktreeManager::create` to create detached, Desktop-compatible worktrees from `HEAD` or an explicit base while preserving the source working-directory path.
- Isolate worktree Git operations from inherited repository selectors, hooks, filesystem monitors, and configured content filters.
- Validate the destination working directory and roll back incomplete worktrees and empty allocation buckets on failure.

## Testing

- Cover layout, base selection, annotated tags, nested working directories, Git environment isolation, filter suppression, source checkout preservation, unsafe symlinks, and creation rollback.

GitOrigin-RevId: bf172c3ff4268dab603d00a1d547485fcd0de368
2026-09-02 03:46:24 +00:00

47 lines
1.4 KiB
Rust

//! Allocates Desktop-compatible worktree buckets and removes empty buckets.
use anyhow::Context;
use anyhow::Result;
use anyhow::bail;
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::Path;
use std::path::PathBuf;
use uuid::Uuid;
pub(crate) fn allocate_worktree_root(root: &Path, repository_name: &OsStr) -> Result<PathBuf> {
fs::create_dir_all(root)
.with_context(|| format!("cannot create worktree root {}", root.display()))?;
#[cfg(target_os = "macos")]
{
let marker = root.join(".metadata_never_index");
fs::write(&marker, b"").with_context(|| {
format!("cannot disable Spotlight indexing at {}", marker.display())
})?;
}
for _ in 0..=u16::MAX {
let identifier = Uuid::new_v4().simple().to_string();
let bucket = root.join(&identifier[..4]);
match fs::create_dir(&bucket) {
Ok(()) => return Ok(bucket.join(repository_name)),
Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {}
Err(error) => {
return Err(error).with_context(|| {
format!("cannot create worktree bucket {}", bucket.display())
});
}
}
}
bail!("all managed worktree identifiers are in use")
}
pub(crate) fn remove_empty_bucket(checkout: &Path) {
if let Some(bucket) = checkout.parent() {
let _ = fs::remove_dir(bucket);
}
}