1use crate::{cmd, constants, errors, model, path};
2
3pub fn worktree_details(
6 pathbuf: &std::path::Path,
7) -> Result<model::GitTreeDetails, errors::CommandError> {
8 let mut worktree_count = 0;
9 let cmd = ["git", "worktree", "list", "--porcelain"];
10 let path = path::abspath(pathbuf);
11 let exec = cmd::exec_in_dir(&cmd, &path);
12 let output = cmd::stdout_to_string(exec)?;
13 let worktree_token = "worktree ";
14 let branch_token = "branch refs/heads/";
15 let bare_token = "bare";
16 let mut parent_path = std::path::PathBuf::new();
17 let mut branch = String::new();
18 let mut is_current = false;
19 let mut is_bare = false;
20
21 for line in output.lines() {
22 if let Some(worktree) = line.strip_prefix(worktree_token) {
23 let worktree_path = std::path::PathBuf::from(worktree);
24 let current_path = path::abspath(&worktree_path);
25 is_current = current_path == path;
26 if worktree_count == 0 {
28 parent_path = current_path;
29 }
30 worktree_count += 1;
31 } else if is_current && line.starts_with(branch_token) {
32 branch = line[branch_token.len()..].to_string();
33 } else if is_current && line == bare_token {
34 is_bare = true;
36 }
37 }
38
39 if worktree_count < 2 {
42 return Ok(model::GitTreeDetails {
43 branch,
44 tree_type: match is_bare {
45 true => model::GitTreeType::Bare,
46 false => model::GitTreeType::Tree,
47 },
48 });
49 }
50 if path == parent_path {
51 return Ok(model::GitTreeDetails {
52 branch,
53 tree_type: model::GitTreeType::Parent,
54 });
55 }
56
57 Ok(model::GitTreeDetails {
58 branch,
59 tree_type: model::GitTreeType::Worktree(parent_path),
60 })
61}
62
63pub fn branches(path: &std::path::Path) -> Vec<String> {
65 let mut branches: Vec<String> = Vec::new();
66 let cmd = [
67 "git",
68 "for-each-ref",
69 "--format=%(refname:short)",
70 "refs/heads",
71 ];
72 let exec = cmd::exec_in_dir(&cmd, &path);
73 if let Ok(output) = cmd::stdout_to_string(exec) {
74 branches.append(
75 &mut output
76 .lines()
77 .filter(|x| !x.is_empty())
78 .map(|x| x.to_string())
79 .collect::<Vec<String>>(),
80 );
81 }
82
83 branches
84}
85
86pub(crate) fn branch(path: &std::path::Path) -> Option<String> {
88 let cmd = ["git", "symbolic-ref", "--quiet", "--short", "HEAD"];
89 let exec = cmd::exec_in_dir(&cmd, &path);
90 if let Ok(output) = cmd::stdout_to_string(exec) {
91 if !output.is_empty() {
92 return Some(output);
93 }
94 }
95 let cmd = ["git", "rev-parse", "--short", "HEAD"];
97 let exec = cmd::exec_in_dir(&cmd, &path);
98 if let Ok(output) = cmd::stdout_to_string(exec) {
99 if !output.is_empty() {
100 return Some(output);
101 }
102 }
103 None
105}
106
107pub(crate) fn current_worktree_path(
109 path: &std::path::Path,
110) -> Result<String, errors::CommandError> {
111 let cmd = ["git", "rev-parse", "--show-toplevel"];
112 let exec = cmd::exec_in_dir(&cmd, &path);
113
114 cmd::stdout_to_string(exec)
115}
116
117pub(crate) fn name_from_url_or_path(url: &str, path: &std::path::Path) -> String {
119 if !url.is_empty() {
120 if let Some(name) = url
121 .rsplit('/')
122 .next()
123 .map(|name| name.trim_end_matches(".git"))
124 {
125 return name.to_string();
126 }
127 }
128 path.file_name()
129 .map(|basename| basename.to_string_lossy().to_string())
130 .unwrap_or(string!(constants::DOT))
131}