git_worktree_cli/commands/
add.rs1use 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 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 let main_branch = get_main_branch(&project_root)?;
27
28 println!("{}", "Fetching latest changes from origin...".cyan());
30 git::execute_streaming(&["fetch", "origin"], Some(&git_working_dir))?;
31
32 let (local_exists, remote_exists) = git::branch_exists(&git_working_dir, branch_name)?;
34
35 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 println!(
90 "{}",
91 format!("✓ Worktree created at: {}", target_path.display()).green()
92 );
93 println!("{}", format!("✓ Branch: {}", branch_name).green());
94
95 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 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}