From e3a88a6ec1bf09ad87a5a56e389da6c9c4f96f2a Mon Sep 17 00:00:00 2001 From: arcnmx Date: Fri, 3 Dec 2021 12:15:31 -0800 Subject: [PATCH] fix(git_branch): more robust handling of .git (#3290) --- src/context.rs | 15 +++++++++++++-- src/modules/directory.rs | 4 ++-- src/modules/dotnet.rs | 2 +- src/modules/git_branch.rs | 11 +++++------ src/modules/git_commit.rs | 4 +--- src/modules/git_metrics.rs | 6 ++++-- src/modules/git_state.rs | 36 +++++++++++------------------------- 7 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/context.rs b/src/context.rs index e155799b..e0e8ce25 100644 --- a/src/context.rs +++ b/src/context.rs @@ -274,7 +274,8 @@ impl<'a> Context<'a> { }?; Ok(Repo { branch: get_current_branch(&repository), - root: repository.workdir().map(Path::to_path_buf), + workdir: repository.workdir().map(Path::to_path_buf), + path: Path::to_path_buf(repository.path()), state: repository.state(), remote: get_remote_repository_info(&repository), }) @@ -433,7 +434,10 @@ pub struct Repo { /// If `current_dir` is a git repository or is contained within one, /// this is the path to the root of that repo. - pub root: Option, + pub workdir: Option, + + /// The path of the repository's `.git` directory. + pub path: PathBuf, /// State pub state: RepositoryState, @@ -442,6 +446,13 @@ pub struct Repo { pub remote: Option, } +impl Repo { + /// Opens the associated git repository. + pub fn open(&self) -> Result { + Repository::open(&self.path) + } +} + /// Remote repository pub struct Remote { pub branch: Option, diff --git a/src/modules/directory.rs b/src/modules/directory.rs index 7c2e733d..d140f545 100644 --- a/src/modules/directory.rs +++ b/src/modules/directory.rs @@ -53,7 +53,7 @@ pub fn module<'a>(context: &'a Context) -> Option> { // Otherwise use the logical path, automatically contracting let repo = context.get_repo().ok(); let dir_string = if config.truncate_to_repo { - repo.and_then(|r| r.root.as_ref()) + repo.and_then(|r| r.workdir.as_ref()) .filter(|&root| root != &home_dir) .and_then(|root| contract_repo_path(display_dir, root)) } else { @@ -99,7 +99,7 @@ pub fn module<'a>(context: &'a Context) -> Option> { String::from("") }; - let path_vec = match &repo.and_then(|r| r.root.as_ref()) { + let path_vec = match &repo.and_then(|r| r.workdir.as_ref()) { Some(repo_root) if config.repo_root_style.is_some() => { let contracted_path = contract_repo_path(display_dir, repo_root)?; let repo_path_vec: Vec<&str> = contracted_path.split('/').collect(); diff --git a/src/modules/dotnet.rs b/src/modules/dotnet.rs index 1dc2c773..2483ad24 100644 --- a/src/modules/dotnet.rs +++ b/src/modules/dotnet.rs @@ -54,7 +54,7 @@ pub fn module<'a>(context: &'a Context) -> Option> { .map(|variable| match variable { "version" => { let version = if enable_heuristic { - let repo_root = context.get_repo().ok().and_then(|r| r.root.as_deref()); + let repo_root = context.get_repo().ok().and_then(|r| r.workdir.as_deref()); estimate_dotnet_version( context, &dotnet_files, diff --git a/src/modules/git_branch.rs b/src/modules/git_branch.rs index 424098a0..9962cb26 100644 --- a/src/modules/git_branch.rs +++ b/src/modules/git_branch.rs @@ -1,7 +1,6 @@ use unicode_segmentation::UnicodeSegmentation; use super::{Context, Module, RootModuleConfig}; -use git2::Repository; use crate::configs::git_branch::GitBranchConfig; use crate::formatter::StringFormatter; @@ -27,11 +26,11 @@ pub fn module<'a>(context: &'a Context) -> Option> { let repo = context.get_repo().ok()?; - if let Some(repo_root) = repo.root.as_ref() { - let git_repo = Repository::open(repo_root).ok()?; - let is_detached = git_repo.head_detached().ok()?; - if config.only_attached && is_detached { - return None; + if config.only_attached { + if let Ok(git_repo) = repo.open() { + if git_repo.head_detached().ok()? { + return None; + } } } diff --git a/src/modules/git_commit.rs b/src/modules/git_commit.rs index ed6d9f84..3c140c95 100644 --- a/src/modules/git_commit.rs +++ b/src/modules/git_commit.rs @@ -1,6 +1,5 @@ use super::{Context, Module, RootModuleConfig}; use crate::formatter::string_formatter::StringFormatterError; -use git2::Repository; use git2::Time; use crate::configs::git_commit::GitCommitConfig; @@ -14,8 +13,7 @@ pub fn module<'a>(context: &'a Context) -> Option> { let config: GitCommitConfig = GitCommitConfig::try_load(module.config); let repo = context.get_repo().ok()?; - let repo_root = repo.root.as_ref()?; - let git_repo = Repository::open(repo_root).ok()?; + let git_repo = repo.open().ok()?; let is_detached = git_repo.head_detached().ok()?; if config.only_detached && !is_detached { diff --git a/src/modules/git_metrics.rs b/src/modules/git_metrics.rs index 19bf5693..78e94e61 100644 --- a/src/modules/git_metrics.rs +++ b/src/modules/git_metrics.rs @@ -21,13 +21,15 @@ pub fn module<'a>(context: &'a Context) -> Option> { }; let repo = context.get_repo().ok()?; - let repo_root = repo.root.as_ref()?; + let repo_root = repo.workdir.as_ref()?; let diff = context .exec_cmd( "git", &[ - OsStr::new("-C"), + OsStr::new("--git-dir"), + repo.path.as_os_str(), + OsStr::new("--work-tree"), repo_root.as_os_str(), OsStr::new("--no-optional-locks"), OsStr::new("diff"), diff --git a/src/modules/git_state.rs b/src/modules/git_state.rs index 34dac024..689a4765 100644 --- a/src/modules/git_state.rs +++ b/src/modules/git_state.rs @@ -1,8 +1,9 @@ use git2::RepositoryState; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use super::{Context, Module, RootModuleConfig}; use crate::configs::git_state::GitStateConfig; +use crate::context::Repo; use crate::formatter::StringFormatter; /// Creates a module with the state of the git repository at the current directory @@ -14,10 +15,8 @@ pub fn module<'a>(context: &'a Context) -> Option> { let config: GitStateConfig = GitStateConfig::try_load(module.config); let repo = context.get_repo().ok()?; - let repo_root = repo.root.as_ref()?; - let repo_state = repo.state; - let state_description = get_state_description(repo_state, repo_root, &config)?; + let state_description = get_state_description(repo, &config)?; let parsed = StringFormatter::new(config.format).and_then(|formatter| { formatter @@ -52,11 +51,10 @@ pub fn module<'a>(context: &'a Context) -> Option> { /// /// During a git operation it will show: REBASING, BISECTING, MERGING, etc. fn get_state_description<'a>( - state: RepositoryState, - root: &'a Path, + repo: &'a Repo, config: &GitStateConfig<'a>, ) -> Option> { - match state { + match repo.state { RepositoryState::Clean => None, RepositoryState::Merge => Some(StateDescription { label: config.merge, @@ -98,13 +96,13 @@ fn get_state_description<'a>( current: None, total: None, }), - RepositoryState::Rebase => Some(describe_rebase(root, config.rebase)), - RepositoryState::RebaseInteractive => Some(describe_rebase(root, config.rebase)), - RepositoryState::RebaseMerge => Some(describe_rebase(root, config.rebase)), + RepositoryState::Rebase => Some(describe_rebase(repo, config.rebase)), + RepositoryState::RebaseInteractive => Some(describe_rebase(repo, config.rebase)), + RepositoryState::RebaseMerge => Some(describe_rebase(repo, config.rebase)), } } -fn describe_rebase<'a>(root: &'a Path, rebase_config: &'a str) -> StateDescription<'a> { +fn describe_rebase<'a>(repo: &'a Repo, rebase_config: &'a str) -> StateDescription<'a> { /* * Sadly, libgit2 seems to have some issues with reading the state of * interactive rebases. So, instead, we'll poke a few of the .git files @@ -113,25 +111,13 @@ fn describe_rebase<'a>(root: &'a Path, rebase_config: &'a str) -> StateDescripti * The following is based heavily on: https://github.com/magicmonty/bash-git-prompt */ - let dot_git = root.join(".git"); - let dot_git = if let Ok(conf) = std::fs::read_to_string(&dot_git) { - let gitdir_re = regex::Regex::new(r"(?m)^gitdir: (.*)$").unwrap(); - if let Some(caps) = gitdir_re.captures(&conf) { - root.join(caps.get(1).unwrap().as_str()) - } else { - dot_git - } - } else { - dot_git - }; - let has_path = |relative_path: &str| { - let path = dot_git.join(PathBuf::from(relative_path)); + let path = repo.path.join(PathBuf::from(relative_path)); path.exists() }; let file_to_usize = |relative_path: &str| { - let path = dot_git.join(PathBuf::from(relative_path)); + let path = repo.path.join(PathBuf::from(relative_path)); let contents = crate::utils::read_file(path).ok()?; let quantity = contents.trim().parse::().ok()?; Some(quantity)