Skip to main content

git_worktree_manager/operations/
path_cmd.rs

1/// Internal _path command for shell function integration.
2///
3/// Used by the gw-cd shell function (see src/shell_functions.rs).
4use crate::error::{CwError, Result};
5use crate::git;
6use crate::messages;
7
8/// Resolve branch to path (outputs to stdout for shell consumption).
9pub fn worktree_path(branch: Option<&str>, list_branches: bool, interactive: bool) -> Result<()> {
10    if interactive {
11        return interactive_path_selection();
12    }
13
14    if list_branches {
15        return list_branch_names();
16    }
17
18    let branch = branch.ok_or_else(|| {
19        CwError::Git(
20            "branch argument is required (unless --list-branches or --interactive is used)"
21                .to_string(),
22        )
23    })?;
24
25    // Local mode
26    let repo = git::get_repo_root(None)?;
27    let normalized = git::normalize_branch_name(branch);
28    let path = git::find_worktree_by_branch(&repo, branch)?
29        .or(git::find_worktree_by_branch(
30            &repo,
31            &format!("refs/heads/{}", normalized),
32        )?)
33        .ok_or_else(|| CwError::Git(messages::worktree_not_found(branch)))?;
34
35    println!("{}", path.display());
36    Ok(())
37}
38
39fn list_branch_names() -> Result<()> {
40    if let Ok(repo) = git::get_repo_root(None) {
41        if let Ok(worktrees) = git::parse_worktrees(&repo) {
42            for (branch, _) in &worktrees {
43                let normalized = git::normalize_branch_name(branch);
44                if normalized != "(detached)" {
45                    println!("{}", normalized);
46                }
47            }
48        }
49    }
50    Ok(())
51}
52
53fn interactive_path_selection() -> Result<()> {
54    let mut entries: Vec<(String, String)> = Vec::new(); // (label, path)
55
56    let repo = git::get_main_repo_root(None)?;
57    let worktrees = git::parse_worktrees(&repo)?;
58    let repo_resolved = git::canonicalize_or(&repo);
59
60    for (branch, path) in &worktrees {
61        let normalized = git::normalize_branch_name(branch);
62        let path_resolved = git::canonicalize_or(path);
63        if path_resolved == repo_resolved {
64            let label = if normalized.is_empty() || normalized == "(detached)" {
65                "main (root)".to_string()
66            } else {
67                format!("{} (root)", normalized)
68            };
69            entries.insert(0, (label, path.to_string_lossy().to_string()));
70        } else if normalized != "(detached)" {
71            entries.push((normalized.to_string(), path.to_string_lossy().to_string()));
72        }
73    }
74
75    if entries.is_empty() {
76        eprintln!("No worktrees found.");
77        std::process::exit(1);
78    }
79
80    if entries.len() == 1 {
81        println!("{}", entries[0].1);
82        return Ok(());
83    }
84
85    // Try arrow-key TUI selector, fall back to numbered selection
86    match crate::tui::arrow_select(&entries, "Select worktree:", 0) {
87        Some(selected_path) => {
88            println!("{}", selected_path);
89            Ok(())
90        }
91        None => {
92            std::process::exit(1);
93        }
94    }
95}