1use std::path::Path;
5
6use crate::error::Result;
7use crate::git::cli::GitCli;
8use crate::git::porcelain::{RawWorktree, parse_worktree_list};
9
10pub(crate) fn enumerate(git: &dyn GitCli, dir: &Path) -> Result<Vec<RawWorktree>> {
14 let output = git.run(dir, &["worktree", "list", "--porcelain"])?;
15 let mut worktrees = parse_worktree_list(&output);
16 for wt in &mut worktrees {
17 wt.is_missing = !wt.is_bare && !wt.path.exists();
20 }
21 Ok(worktrees)
22}
23
24#[cfg(test)]
25mod tests {
26 use super::*;
27 use crate::git::cli::RealGit;
28 use crate::testutil::TestRepo;
29
30 #[test]
31 fn enumerates_main_and_linked() {
32 let repo = TestRepo::init();
33 repo.add_worktree("feature/x", "../wt-x");
34 repo.add_worktree("feature/y", "../wt-y");
35 let wts = enumerate(&RealGit, repo.root()).unwrap();
36 assert_eq!(wts.len(), 3);
37 assert!(wts[0].is_main);
38 let branches: Vec<_> = wts.iter().filter_map(|w| w.branch.clone()).collect();
39 assert!(branches.contains(&"feature/x".to_string()));
40 assert!(branches.contains(&"feature/y".to_string()));
41 assert!(wts.iter().all(|w| !w.is_missing));
42 }
43
44 #[test]
45 fn detects_missing_worktree() {
46 let repo = TestRepo::init();
47 repo.add_worktree("gone", "../wt-gone");
48 let linked = repo.root().parent().unwrap().join("wt-gone");
49 std::fs::remove_dir_all(&linked).unwrap();
50 let wts = enumerate(&RealGit, repo.root()).unwrap();
51 let missing = wts
52 .iter()
53 .find(|w| w.branch.as_deref() == Some("gone"))
54 .unwrap();
55 assert!(missing.is_missing);
56 }
57
58 #[test]
59 fn single_worktree_repo() {
60 let repo = TestRepo::init();
61 let wts = enumerate(&RealGit, repo.root()).unwrap();
62 assert_eq!(wts.len(), 1);
63 assert!(wts[0].is_main);
64 assert_eq!(wts[0].branch.as_deref(), Some("main"));
65 }
66}