git_worktree_cli/commands/
add.rs

1use colored::Colorize;
2use std::path::{Path, PathBuf};
3
4use crate::config::GitWorktreeConfig;
5use crate::core::project::{find_existing_worktree, find_project_root};
6use crate::error::{Error, Result};
7use crate::git;
8use crate::hooks;
9
10pub fn run(branch_name: &str) -> Result<()> {
11    if branch_name.is_empty() {
12        return Err(Error::msg(
13            "Error: Branch name is required\nUsage: gwt add <branch-name>",
14        ));
15    }
16
17    // Determine git root and target path
18    let (git_working_dir, target_path, project_root) = determine_paths(branch_name)?;
19
20    println!(
21        "{}",
22        format!("Preparing worktree (new branch '{}')", branch_name).cyan()
23    );
24
25    // Get main branch from config
26    let main_branch = get_main_branch(&project_root)?;
27
28    // Fetch latest changes from origin to ensure we have the latest remote state
29    println!("{}", "Fetching latest changes from origin...".cyan());
30    git::execute_streaming(&["fetch", "origin"], Some(&git_working_dir))?;
31
32    // Check if branch exists locally or remotely
33    let (local_exists, remote_exists) = git::branch_exists(&git_working_dir, branch_name)?;
34
35    // Create worktree based on branch existence
36    if local_exists {
37        println!(
38            "{}",
39            format!(
40                "Branch '{}' exists locally, checking out existing branch...",
41                branch_name
42            )
43            .yellow()
44        );
45        git::execute_streaming(
46            &["worktree", "add", target_path.to_str().unwrap(), branch_name],
47            Some(&git_working_dir),
48        )?;
49    } else if remote_exists {
50        println!(
51            "{}",
52            format!(
53                "Branch '{}' exists remotely, checking out remote branch...",
54                branch_name
55            )
56            .yellow()
57        );
58        git::execute_streaming(
59            &[
60                "worktree",
61                "add",
62                target_path.to_str().unwrap(),
63                "-b",
64                branch_name,
65                &format!("origin/{}", branch_name),
66            ],
67            Some(&git_working_dir),
68        )?;
69    } else {
70        println!(
71            "{}",
72            format!("Creating new branch '{}' from 'origin/{}'...", branch_name, main_branch).cyan()
73        );
74        git::execute_streaming(
75            &[
76                "worktree",
77                "add",
78                "--no-track",
79                target_path.to_str().unwrap(),
80                "-b",
81                branch_name,
82                &format!("origin/{}", main_branch),
83            ],
84            Some(&git_working_dir),
85        )?;
86    }
87
88    // Success messages
89    println!(
90        "{}",
91        format!("✓ Worktree created at: {}", target_path.display()).green()
92    );
93    println!("{}", format!("✓ Branch: {}", branch_name).green());
94
95    // Execute post-add hooks
96    hooks::execute_hooks(
97        "postAdd",
98        &target_path,
99        &[
100            ("branchName", branch_name),
101            ("worktreePath", target_path.to_str().unwrap()),
102        ],
103    )?;
104
105    Ok(())
106}
107
108fn determine_paths(branch_name: &str) -> Result<(PathBuf, PathBuf, PathBuf)> {
109    let project_root = find_project_root()?;
110    let target_path = project_root.join(branch_name);
111    let git_working_dir = find_existing_worktree(&project_root)?;
112
113    Ok((git_working_dir, target_path, project_root))
114}
115
116fn get_main_branch(project_root: &Path) -> Result<String> {
117    let config_path = project_root.join("git-worktree-config.jsonc");
118    if config_path.exists() {
119        let config = GitWorktreeConfig::load(&config_path)?;
120        Ok(config.main_branch)
121    } else {
122        // Fallback to detecting from git if no config
123        if let Some(git_root) = git::get_git_root()? {
124            Ok(git::get_default_branch(&git_root)?)
125        } else {
126            Ok("main".to_string())
127        }
128    }
129}