fix(git_branch): more robust handling of .git (#3290)

This commit is contained in:
arcnmx 2021-12-03 12:15:31 -08:00 committed by GitHub
parent 1109fd6997
commit e3a88a6ec1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 37 additions and 41 deletions

View File

@ -274,7 +274,8 @@ impl<'a> Context<'a> {
}?; }?;
Ok(Repo { Ok(Repo {
branch: get_current_branch(&repository), 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(), state: repository.state(),
remote: get_remote_repository_info(&repository), 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, /// If `current_dir` is a git repository or is contained within one,
/// this is the path to the root of that repo. /// this is the path to the root of that repo.
pub root: Option<PathBuf>, pub workdir: Option<PathBuf>,
/// The path of the repository's `.git` directory.
pub path: PathBuf,
/// State /// State
pub state: RepositoryState, pub state: RepositoryState,
@ -442,6 +446,13 @@ pub struct Repo {
pub remote: Option<Remote>, pub remote: Option<Remote>,
} }
impl Repo {
/// Opens the associated git repository.
pub fn open(&self) -> Result<Repository, git2::Error> {
Repository::open(&self.path)
}
}
/// Remote repository /// Remote repository
pub struct Remote { pub struct Remote {
pub branch: Option<String>, pub branch: Option<String>,

View File

@ -53,7 +53,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
// Otherwise use the logical path, automatically contracting // Otherwise use the logical path, automatically contracting
let repo = context.get_repo().ok(); let repo = context.get_repo().ok();
let dir_string = if config.truncate_to_repo { 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) .filter(|&root| root != &home_dir)
.and_then(|root| contract_repo_path(display_dir, root)) .and_then(|root| contract_repo_path(display_dir, root))
} else { } else {
@ -99,7 +99,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
String::from("") 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() => { Some(repo_root) if config.repo_root_style.is_some() => {
let contracted_path = contract_repo_path(display_dir, repo_root)?; let contracted_path = contract_repo_path(display_dir, repo_root)?;
let repo_path_vec: Vec<&str> = contracted_path.split('/').collect(); let repo_path_vec: Vec<&str> = contracted_path.split('/').collect();

View File

@ -54,7 +54,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
.map(|variable| match variable { .map(|variable| match variable {
"version" => { "version" => {
let version = if enable_heuristic { 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( estimate_dotnet_version(
context, context,
&dotnet_files, &dotnet_files,

View File

@ -1,7 +1,6 @@
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use git2::Repository;
use crate::configs::git_branch::GitBranchConfig; use crate::configs::git_branch::GitBranchConfig;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
@ -27,11 +26,11 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let repo = context.get_repo().ok()?; let repo = context.get_repo().ok()?;
if let Some(repo_root) = repo.root.as_ref() { if config.only_attached {
let git_repo = Repository::open(repo_root).ok()?; if let Ok(git_repo) = repo.open() {
let is_detached = git_repo.head_detached().ok()?; if git_repo.head_detached().ok()? {
if config.only_attached && is_detached { return None;
return None; }
} }
} }

View File

@ -1,6 +1,5 @@
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use crate::formatter::string_formatter::StringFormatterError; use crate::formatter::string_formatter::StringFormatterError;
use git2::Repository;
use git2::Time; use git2::Time;
use crate::configs::git_commit::GitCommitConfig; use crate::configs::git_commit::GitCommitConfig;
@ -14,8 +13,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let config: GitCommitConfig = GitCommitConfig::try_load(module.config); let config: GitCommitConfig = GitCommitConfig::try_load(module.config);
let repo = context.get_repo().ok()?; let repo = context.get_repo().ok()?;
let repo_root = repo.root.as_ref()?; let git_repo = repo.open().ok()?;
let git_repo = Repository::open(repo_root).ok()?;
let is_detached = git_repo.head_detached().ok()?; let is_detached = git_repo.head_detached().ok()?;
if config.only_detached && !is_detached { if config.only_detached && !is_detached {

View File

@ -21,13 +21,15 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
}; };
let repo = context.get_repo().ok()?; let repo = context.get_repo().ok()?;
let repo_root = repo.root.as_ref()?; let repo_root = repo.workdir.as_ref()?;
let diff = context let diff = context
.exec_cmd( .exec_cmd(
"git", "git",
&[ &[
OsStr::new("-C"), OsStr::new("--git-dir"),
repo.path.as_os_str(),
OsStr::new("--work-tree"),
repo_root.as_os_str(), repo_root.as_os_str(),
OsStr::new("--no-optional-locks"), OsStr::new("--no-optional-locks"),
OsStr::new("diff"), OsStr::new("diff"),

View File

@ -1,8 +1,9 @@
use git2::RepositoryState; use git2::RepositoryState;
use std::path::{Path, PathBuf}; use std::path::PathBuf;
use super::{Context, Module, RootModuleConfig}; use super::{Context, Module, RootModuleConfig};
use crate::configs::git_state::GitStateConfig; use crate::configs::git_state::GitStateConfig;
use crate::context::Repo;
use crate::formatter::StringFormatter; use crate::formatter::StringFormatter;
/// Creates a module with the state of the git repository at the current directory /// 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<Module<'a>> {
let config: GitStateConfig = GitStateConfig::try_load(module.config); let config: GitStateConfig = GitStateConfig::try_load(module.config);
let repo = context.get_repo().ok()?; 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| { let parsed = StringFormatter::new(config.format).and_then(|formatter| {
formatter formatter
@ -52,11 +51,10 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
/// ///
/// During a git operation it will show: REBASING, BISECTING, MERGING, etc. /// During a git operation it will show: REBASING, BISECTING, MERGING, etc.
fn get_state_description<'a>( fn get_state_description<'a>(
state: RepositoryState, repo: &'a Repo,
root: &'a Path,
config: &GitStateConfig<'a>, config: &GitStateConfig<'a>,
) -> Option<StateDescription<'a>> { ) -> Option<StateDescription<'a>> {
match state { match repo.state {
RepositoryState::Clean => None, RepositoryState::Clean => None,
RepositoryState::Merge => Some(StateDescription { RepositoryState::Merge => Some(StateDescription {
label: config.merge, label: config.merge,
@ -98,13 +96,13 @@ fn get_state_description<'a>(
current: None, current: None,
total: None, total: None,
}), }),
RepositoryState::Rebase => Some(describe_rebase(root, config.rebase)), RepositoryState::Rebase => Some(describe_rebase(repo, config.rebase)),
RepositoryState::RebaseInteractive => Some(describe_rebase(root, config.rebase)), RepositoryState::RebaseInteractive => Some(describe_rebase(repo, config.rebase)),
RepositoryState::RebaseMerge => Some(describe_rebase(root, 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 * 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 * 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 * 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 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() path.exists()
}; };
let file_to_usize = |relative_path: &str| { 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 contents = crate::utils::read_file(path).ok()?;
let quantity = contents.trim().parse::<usize>().ok()?; let quantity = contents.trim().parse::<usize>().ok()?;
Some(quantity) Some(quantity)