mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Add worktree startup helpers
This commit is contained in:
@@ -31,6 +31,7 @@ codex-core = { workspace = true }
|
||||
codex-exec = { workspace = true }
|
||||
codex-execpolicy = { workspace = true }
|
||||
codex-features = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-login = { workspace = true }
|
||||
codex-mcp-server = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
|
||||
@@ -39,10 +39,13 @@ mod app_cmd;
|
||||
#[cfg(target_os = "macos")]
|
||||
mod desktop_app;
|
||||
mod mcp_cmd;
|
||||
mod worktree_cmd;
|
||||
#[cfg(not(windows))]
|
||||
mod wsl_paths;
|
||||
|
||||
use crate::mcp_cmd::McpCli;
|
||||
use crate::worktree_cmd::WorktreeCli;
|
||||
use crate::worktree_cmd::run_worktree_command;
|
||||
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
@@ -150,6 +153,9 @@ enum Subcommand {
|
||||
|
||||
/// Inspect feature flags.
|
||||
Features(FeaturesCli),
|
||||
|
||||
/// Manage Codex-created local git worktrees.
|
||||
Worktree(WorktreeCli),
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -1028,6 +1034,14 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
|
||||
disable_feature_in_config(&interactive, &feature).await?;
|
||||
}
|
||||
},
|
||||
Some(Subcommand::Worktree(worktree_cli)) => {
|
||||
reject_remote_mode_for_subcommand(
|
||||
root_remote.as_deref(),
|
||||
root_remote_auth_token_env.as_deref(),
|
||||
"worktree",
|
||||
)?;
|
||||
run_worktree_command(worktree_cli)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
use clap::Parser;
|
||||
use codex_core::config::find_codex_home;
|
||||
use codex_git_utils::CodexWorktreePruneCandidate;
|
||||
use codex_git_utils::CodexWorktreePruneMode;
|
||||
use codex_git_utils::CodexWorktreePruneOptions;
|
||||
use codex_git_utils::CodexWorktreePruneSkipReason;
|
||||
use codex_git_utils::CodexWorktreePruneSkipped;
|
||||
use codex_git_utils::prune_codex_managed_worktrees;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub(crate) struct WorktreeCli {
|
||||
#[command(subcommand)]
|
||||
pub(crate) subcommand: WorktreeSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub(crate) enum WorktreeSubcommand {
|
||||
/// Remove old Codex-managed local git worktrees.
|
||||
Prune(WorktreePruneCommand),
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
pub(crate) struct WorktreePruneCommand {
|
||||
/// Only prune worktrees created from this source repository.
|
||||
#[arg(long = "repo", value_name = "DIR")]
|
||||
repo: Option<PathBuf>,
|
||||
|
||||
/// Only prune worktrees created before this Unix timestamp (seconds).
|
||||
#[arg(long = "created-before", value_name = "UNIX_SECONDS")]
|
||||
created_before: Option<u64>,
|
||||
|
||||
/// Only prune worktrees last used before this Unix timestamp (seconds).
|
||||
#[arg(long = "last-used-before", value_name = "UNIX_SECONDS")]
|
||||
last_used_before: Option<u64>,
|
||||
|
||||
/// Print matching worktrees without deleting them.
|
||||
#[arg(long = "dry-run", default_value_t = false)]
|
||||
dry_run: bool,
|
||||
}
|
||||
|
||||
pub(crate) fn run_worktree_command(worktree_cli: WorktreeCli) -> anyhow::Result<()> {
|
||||
match worktree_cli.subcommand {
|
||||
WorktreeSubcommand::Prune(command) => run_worktree_prune_command(command),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_worktree_prune_command(command: WorktreePruneCommand) -> anyhow::Result<()> {
|
||||
let options = CodexWorktreePruneOptions {
|
||||
codex_home: find_codex_home()?,
|
||||
source_repo_root: command.repo,
|
||||
created_before: command.created_before,
|
||||
last_used_before: command.last_used_before,
|
||||
mode: if command.dry_run {
|
||||
CodexWorktreePruneMode::DryRun
|
||||
} else {
|
||||
CodexWorktreePruneMode::Delete
|
||||
},
|
||||
};
|
||||
|
||||
let report = prune_codex_managed_worktrees(&options)?;
|
||||
for candidate in &report.pruned {
|
||||
print_candidate("pruned", candidate);
|
||||
}
|
||||
for skipped in &report.skipped {
|
||||
print_skipped_candidate(skipped);
|
||||
}
|
||||
for candidate in &report.kept {
|
||||
print_candidate("kept", candidate);
|
||||
}
|
||||
println!(
|
||||
"summary: pruned={} skipped={} kept={}",
|
||||
report.pruned.len(),
|
||||
report.skipped.len(),
|
||||
report.kept.len()
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_candidate(action: &str, candidate: &CodexWorktreePruneCandidate) {
|
||||
println!(
|
||||
"{action} {} repo={} created_at={} last_used_at={}",
|
||||
candidate.worktree_git_root.display(),
|
||||
candidate.metadata.source_repo_root.display(),
|
||||
candidate.metadata.created_at,
|
||||
candidate.metadata.last_used_at
|
||||
);
|
||||
}
|
||||
|
||||
fn print_skipped_candidate(skipped: &CodexWorktreePruneSkipped) {
|
||||
let reason = match skipped.reason {
|
||||
CodexWorktreePruneSkipReason::DirtyWorktree => "dirty-worktree",
|
||||
CodexWorktreePruneSkipReason::LocalCommits => "local-commits",
|
||||
CodexWorktreePruneSkipReason::MissingStartingRef => "missing-starting-ref",
|
||||
};
|
||||
println!(
|
||||
"skipped {} reason={} repo={} created_at={} last_used_at={}",
|
||||
skipped.candidate.worktree_git_root.display(),
|
||||
reason,
|
||||
skipped.candidate.metadata.source_repo_root.display(),
|
||||
skipped.candidate.metadata.created_at,
|
||||
skipped.candidate.metadata.last_used_at
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::WorktreeCli;
|
||||
use super::WorktreeSubcommand;
|
||||
use clap::Parser;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn parse_worktree_prune_filters() {
|
||||
let cli = WorktreeCli::parse_from([
|
||||
"codex-worktree",
|
||||
"prune",
|
||||
"--repo",
|
||||
"/tmp/repo",
|
||||
"--created-before",
|
||||
"123",
|
||||
"--last-used-before",
|
||||
"456",
|
||||
"--dry-run",
|
||||
]);
|
||||
|
||||
let WorktreeSubcommand::Prune(command) = cli.subcommand;
|
||||
assert_eq!(command.repo, Some(PathBuf::from("/tmp/repo")));
|
||||
assert_eq!(command.created_before, Some(123));
|
||||
assert_eq!(command.last_used_before, Some(456));
|
||||
assert!(command.dry_run);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user