git_workty/commands/
pick.rs1use crate::git::GitRepo;
2use crate::status::get_all_statuses;
3use crate::ui::{print_error, UiOptions};
4use crate::worktree::list_worktrees;
5use anyhow::Result;
6use console::Term;
7use dialoguer::FuzzySelect;
8use is_terminal::IsTerminal;
9
10pub fn execute(repo: &GitRepo, _opts: &UiOptions) -> Result<()> {
11 if !std::io::stdin().is_terminal() {
12 print_error(
13 "Cannot run interactive picker in non-TTY environment",
14 Some("Use `git workty go <name>` for non-interactive selection."),
15 );
16 std::process::exit(1);
17 }
18
19 let worktrees = list_worktrees(repo)?;
20 if worktrees.is_empty() {
21 print_error("No worktrees found", None);
22 std::process::exit(1);
23 }
24
25 let statuses = get_all_statuses(repo, &worktrees);
27
28 let max_name_len = statuses
30 .iter()
31 .map(|(wt, _)| wt.name().len())
32 .max()
33 .unwrap_or(10);
34
35 let items: Vec<String> = statuses
36 .iter()
37 .map(|(wt, status)| {
38 let name = format!("{:width$}", wt.name(), width = max_name_len);
39
40 let dirty = if status.dirty_count > 0 {
42 format!("*{}", status.dirty_count)
43 } else {
44 " ".to_string()
45 };
46
47 let time = format_time_short(status.last_commit_time);
49
50 let rebase = if status.needs_rebase() {
52 format!("R{}", status.behind_main.unwrap_or(0))
53 } else {
54 " ".to_string()
55 };
56
57 format!("{} {:>3} {:>4} {}", name, dirty, time, rebase)
58 })
59 .collect();
60
61 let selection = FuzzySelect::new()
62 .with_prompt("Select worktree")
63 .items(&items)
64 .default(0)
65 .interact_on_opt(&Term::stderr())?;
66
67 match selection {
68 Some(idx) => {
69 println!("{}", statuses[idx].0.path.display());
70 Ok(())
71 }
72 None => {
73 std::process::exit(130);
74 }
75 }
76}
77
78fn format_time_short(seconds: Option<i64>) -> String {
79 match seconds {
80 Some(s) if s < 60 => "now".to_string(),
81 Some(s) if s < 3600 => format!("{}m", s / 60),
82 Some(s) if s < 86400 => format!("{}h", s / 3600),
83 Some(s) if s < 604800 => format!("{}d", s / 86400),
84 Some(s) => format!("{}w", s / 604800),
85 None => "-".to_string(),
86 }
87}