Skip to main content

lattice_vcs/
branch.rs

1use crate::{Repository, Result, VcsError};
2
3/// Branch operations — checkout, create, delete.
4///
5/// Uses the git CLI.
6pub struct Branch;
7
8impl Branch {
9    /// Check out an existing branch by name.
10    ///
11    /// Equivalent to `git checkout <name>`.
12    pub fn checkout(repo: &Repository, name: &str) -> Result<()> {
13        repo.run_git(["checkout", name])
14            .map(|_| ())
15            .map_err(|e| VcsError::Index(format!("branch checkout {}: {}", name, e)))
16    }
17
18    /// Create a new branch and optionally check it out.
19    ///
20    /// If `checkout` is `true`, equivalent to `git checkout -b <name>
21    /// [<from>]`. Otherwise equivalent to `git branch <name> [<from>]`.
22    /// `from` is the base ref to branch off of; `None` uses git's own
23    /// default (HEAD).
24    pub fn create(repo: &Repository, name: &str, checkout: bool, from: Option<&str>) -> Result<()> {
25        let mut args = if checkout {
26            vec!["checkout", "-b", name]
27        } else {
28            vec!["branch", name]
29        };
30        if let Some(base) = from {
31            args.push(base);
32        }
33        repo.run_git(args)
34            .map(|_| ())
35            .map_err(|e| VcsError::Index(format!("branch create {}: {}", name, e)))
36    }
37
38    /// Delete a branch by name.
39    ///
40    /// Uses `-D` (force-delete). Callers should confirm with the user
41    /// before calling this on unmerged branches.
42    pub fn delete(repo: &Repository, name: &str) -> Result<()> {
43        repo.run_git(["branch", "-D", name])
44            .map(|_| ())
45            .map_err(|e| VcsError::Index(format!("branch delete {}: {}", name, e)))
46    }
47
48    /// Rename a branch.
49    ///
50    /// Equivalent to `git branch -m <old> <new>`. Deliberately **not**
51    /// `-M`: the lowercase form refuses when `new` already names a
52    /// branch, and the uppercase one overwrites it. Overwriting here
53    /// destroys whatever `new` pointed at, silently, so the refusal is
54    /// the behaviour we want and the error is propagated rather than
55    /// swallowed.
56    ///
57    /// Renaming the checked-out branch carries HEAD with it (git's own
58    /// behaviour); it does not detach.
59    pub fn rename(repo: &Repository, old: &str, new: &str) -> Result<()> {
60        repo.run_git(["branch", "-m", old, new])
61            .map(|_| ())
62            .map_err(|e| VcsError::Index(format!("branch rename {} -> {}: {}", old, new, e)))
63    }
64
65    /// List all local branches.
66    ///
67    /// Equivalent to `git branch --format=%(refname:short)`.
68    pub fn list(repo: &Repository) -> Result<Vec<String>> {
69        repo.run_git_lines(["branch", "--format=%(refname:short)"])
70    }
71}