diff --git a/src/git/path.rs b/src/git/path.rs index 5cc868c..f6b8459 100644 --- a/src/git/path.rs +++ b/src/git/path.rs @@ -2,9 +2,14 @@ use std::fmt::Debug; use camino::Utf8PathBuf; use command_error::CommandExt; +use command_error::OutputContext; +use miette::miette; use miette::Context; use miette::IntoDiagnostic; use tracing::instrument; +use utf8_command::Utf8Output; + +use crate::NormalPath; use super::Git; @@ -23,6 +28,43 @@ impl<'a> GitPath<'a> { Self(git) } + /// If in a working tree, get the repository root (`git rev-parse --show-toplevel`). If the + /// repository is bare, get the `.git` directory (`git rev-parse --git-dir`). Otherwise, error. + #[instrument(level = "trace")] + pub fn repo_root_or_git_common_dir_if_bare(&self) -> miette::Result { + if self.is_inside_work_tree()? { + self.repo_root() + } else if self.0.config().is_bare()? { + self.git_common_dir() + } else { + Err(miette!( + "Path is not in a working tree or a bare repository: {}", + NormalPath::try_display_cwd(self.0.get_directory()) + )) + } + } + + /// Check if we're inside a working tree. + #[instrument(level = "trace")] + pub fn is_inside_work_tree(&self) -> miette::Result { + self.0 + .rev_parse_command() + .arg("--is-inside-work-tree") + .output_checked_as(|context: OutputContext| { + if !context.status().success() { + Err(context.error()) + } else { + let stdout = context.output().stdout.trim(); + match stdout { + "true" => Ok(true), + "false" => Ok(false), + _ => Err(context.error_msg("Expected 'true' or 'false'")), + } + } + }) + .into_diagnostic() + } + /// `git rev-parse --show-toplevel` #[instrument(level = "trace")] pub fn repo_root(&self) -> miette::Result {