use anyhow::bail;
use gix::prelude::ObjectIdExt;
use unicode_width::UnicodeWidthStr;
use crate::OutputFormat;
pub fn list(repo: gix::Repository, out: &mut dyn std::io::Write, format: OutputFormat) -> anyhow::Result<()> {
if format != OutputFormat::Human {
bail!("JSON output isn't implemented yet");
}
let main_repo = repo.main_repo()?;
let mut worktrees = Vec::new();
if let Some(worktree) = main_repo.worktree() {
worktrees.push(create_worktree_info(&main_repo, gix::path::realpath(worktree.base())?)?);
}
for proxy in main_repo.worktrees()? {
let base = gix::path::realpath(proxy.base()?)?;
match proxy.into_repo() {
Ok(worktree_repo) => {
worktrees.push(create_worktree_info(&worktree_repo, base)?);
}
Err(_) => {
worktrees.push(create_inaccessible_worktree_info(&repo, base));
}
}
}
let path_width = worktrees
.iter()
.map(|worktree| UnicodeWidthStr::width(worktree.base.as_str()))
.max()
.unwrap_or(0);
for worktree in worktrees {
worktree.write(out, path_width)?;
}
Ok(())
}
struct WorktreeInfo {
base: String,
head: String,
branch: String,
}
impl WorktreeInfo {
fn write(&self, out: &mut dyn std::io::Write, path_width: usize) -> std::io::Result<()> {
writeln!(
out,
"{}{} {} [{}]",
self.base,
" ".repeat(path_width.saturating_sub(UnicodeWidthStr::width(self.base.as_str()))),
self.head,
self.branch,
)
}
}
fn create_worktree_info(repo: &gix::Repository, base: std::path::PathBuf) -> anyhow::Result<WorktreeInfo> {
let head = repo
.head_id()
.map_or_else(
|_| repo.object_hash().null().attach(repo).shorten_or_id(),
|id| id.shorten_or_id(),
)
.to_string();
let branch = repo.head_name()?.map_or_else(
|| "<detached>".to_string(),
|name| name.shorten().to_owned().to_string(),
);
Ok(WorktreeInfo {
base: base.display().to_string(),
head,
branch,
})
}
fn create_inaccessible_worktree_info(repo: &gix::Repository, base: std::path::PathBuf) -> WorktreeInfo {
WorktreeInfo {
base: base.display().to_string(),
head: repo.object_hash().null().attach(repo).shorten_or_id().to_string(),
branch: "<unknown>".to_string(),
}
}