git_x/
new_branch.rs

1use crate::core::git::GitOperations;
2use crate::core::validation::Validate;
3use crate::{GitXError, Result};
4use std::process::Command;
5
6pub fn run(branch_name: String, from: Option<String>) -> Result<String> {
7    // Validate branch name format and safety
8    Validate::branch_name(&branch_name)?;
9
10    // Check if branch already exists
11    if branch_exists(&branch_name) {
12        return Err(GitXError::GitCommand(format!(
13            "Branch '{branch_name}' already exists"
14        )));
15    }
16
17    // Determine base branch
18    let base_branch = match from {
19        Some(ref branch) => {
20            if !branch_exists(branch) && !is_valid_ref(branch) {
21                return Err(GitXError::GitCommand(format!(
22                    "Base branch or ref '{branch}' does not exist"
23                )));
24            }
25            branch.clone()
26        }
27        None => get_current_branch_result()?,
28    };
29
30    let mut output = Vec::new();
31    output.push(branch_name.clone());
32
33    // Create the new branch
34    create_branch_result(&branch_name, &base_branch)?;
35
36    // Switch to the new branch
37    switch_to_branch_result(&branch_name)?;
38
39    output.push(branch_name.clone());
40    Ok(output.join("\n"))
41}
42
43fn branch_exists(branch_name: &str) -> bool {
44    Command::new("git")
45        .args([
46            "show-ref",
47            "--verify",
48            "--quiet",
49            &format!("refs/heads/{branch_name}"),
50        ])
51        .status()
52        .map(|status| status.success())
53        .unwrap_or(false)
54}
55
56fn is_valid_ref(ref_name: &str) -> bool {
57    Command::new("git")
58        .args(["rev-parse", "--verify", "--quiet", ref_name])
59        .status()
60        .map(|status| status.success())
61        .unwrap_or(false)
62}
63
64fn get_current_branch_result() -> Result<String> {
65    GitOperations::current_branch()
66        .map_err(|e| GitXError::GitCommand(format!("Failed to get current branch: {e}")))
67}
68
69fn create_branch_result(branch_name: &str, base_branch: &str) -> Result<()> {
70    let output = Command::new("git")
71        .args(["branch", branch_name, base_branch])
72        .output()
73        .map_err(|_| GitXError::GitCommand("Failed to execute git branch command".to_string()))?;
74
75    if !output.status.success() {
76        let stderr = String::from_utf8_lossy(&output.stderr);
77        return Err(GitXError::GitCommand(format!(
78            "Failed to create branch: {}",
79            stderr.trim()
80        )));
81    }
82
83    Ok(())
84}
85
86fn switch_to_branch_result(branch_name: &str) -> Result<()> {
87    let output = Command::new("git")
88        .args(["switch", branch_name])
89        .output()
90        .map_err(|_| GitXError::GitCommand("Failed to execute git switch command".to_string()))?;
91
92    if !output.status.success() {
93        let stderr = String::from_utf8_lossy(&output.stderr);
94        return Err(GitXError::GitCommand(format!(
95            "Failed to switch to new branch: {}",
96            stderr.trim()
97        )));
98    }
99
100    Ok(())
101}