Skip to main content

gitoxide_core/repository/
worktree.rs

1use anyhow::bail;
2use gix::prelude::ObjectIdExt;
3use unicode_width::UnicodeWidthStr;
4
5use crate::OutputFormat;
6
7pub fn list(repo: gix::Repository, out: &mut dyn std::io::Write, format: OutputFormat) -> anyhow::Result<()> {
8    if format != OutputFormat::Human {
9        bail!("JSON output isn't implemented yet");
10    }
11    let main_repo = repo.main_repo()?;
12    let mut worktrees = Vec::new();
13
14    if let Some(worktree) = main_repo.worktree() {
15        worktrees.push(create_worktree_info(&main_repo, gix::path::realpath(worktree.base())?)?);
16    }
17
18    for proxy in main_repo.worktrees()? {
19        let base = gix::path::realpath(proxy.base()?)?;
20
21        match proxy.into_repo() {
22            Ok(worktree_repo) => {
23                worktrees.push(create_worktree_info(&worktree_repo, base)?);
24            }
25            Err(_) => {
26                worktrees.push(create_inaccessible_worktree_info(&repo, base));
27            }
28        }
29    }
30
31    let path_width = worktrees
32        .iter()
33        .map(|worktree| UnicodeWidthStr::width(worktree.base.as_str()))
34        .max()
35        .unwrap_or(0);
36
37    for worktree in worktrees {
38        worktree.write(out, path_width)?;
39    }
40
41    Ok(())
42}
43
44struct WorktreeInfo {
45    base: String,
46    head: String,
47    branch: String,
48}
49
50impl WorktreeInfo {
51    fn write(&self, out: &mut dyn std::io::Write, path_width: usize) -> std::io::Result<()> {
52        writeln!(
53            out,
54            "{}{} {} [{}]",
55            self.base,
56            " ".repeat(path_width.saturating_sub(UnicodeWidthStr::width(self.base.as_str()))),
57            self.head,
58            self.branch,
59        )
60    }
61}
62
63fn create_worktree_info(repo: &gix::Repository, base: std::path::PathBuf) -> anyhow::Result<WorktreeInfo> {
64    let head = repo
65        .head_id()
66        .map_or_else(
67            |_| repo.object_hash().null().attach(repo).shorten_or_id(),
68            |id| id.shorten_or_id(),
69        )
70        .to_string();
71
72    let branch = repo.head_name()?.map_or_else(
73        || "<detached>".to_string(),
74        |name| name.shorten().to_owned().to_string(),
75    );
76
77    Ok(WorktreeInfo {
78        base: base.display().to_string(),
79        head,
80        branch,
81    })
82}
83
84fn create_inaccessible_worktree_info(repo: &gix::Repository, base: std::path::PathBuf) -> WorktreeInfo {
85    WorktreeInfo {
86        base: base.display().to_string(),
87        head: repo.object_hash().null().attach(repo).shorten_or_id().to_string(),
88        branch: "<unknown>".to_string(),
89    }
90}