fix(git_branch): more robust handling of .git (#3290)
This commit is contained in:
parent
1109fd6997
commit
e3a88a6ec1
|
@ -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>,
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue