diff options
author | David Knaack <davidkna@users.noreply.github.com> | 2022-08-09 04:33:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-08 22:33:00 -0400 |
commit | ac55a01d0ffe907ef7af48c9597c0bca4dbd8c69 (patch) | |
tree | 777418ce50e602d21b737ac02d76078c386665d3 /src | |
parent | f614fcdc1f2b7ad2cbfd6865a774775c032001de (diff) |
feat(git): replace `git2` with `git-repository` (#3883)
Diffstat (limited to 'src')
-rw-r--r-- | src/context.rs | 135 | ||||
-rw-r--r-- | src/modules/directory.rs | 6 | ||||
-rw-r--r-- | src/modules/git_branch.rs | 8 | ||||
-rw-r--r-- | src/modules/git_commit.rs | 81 | ||||
-rw-r--r-- | src/modules/git_state.rs | 27 | ||||
-rw-r--r-- | src/modules/git_status.rs | 1 | ||||
-rw-r--r-- | src/modules/hg_branch.rs | 2 | ||||
-rw-r--r-- | src/modules/hostname.rs | 2 |
8 files changed, 118 insertions, 144 deletions
diff --git a/src/context.rs b/src/context.rs index 404cf5aaf..bd00ce388 100644 --- a/src/context.rs +++ b/src/context.rs @@ -6,7 +6,11 @@ use crate::utils::{create_command, exec_timeout, read_file, CommandOutput}; use crate::modules; use crate::utils::{self, home_dir}; use clap::Parser; -use git2::{ErrorCode::UnbornBranch, Repository, RepositoryState}; +use git_repository::{ + self as git, + sec::{self as git_sec, trust::DefaultForLevel}, + state as git_state, Repository, ThreadSafeRepository, +}; use once_cell::sync::OnceCell; #[cfg(test)] use std::collections::HashMap; @@ -255,22 +259,51 @@ impl<'a> Context<'a> { } /// Will lazily get repo root and branch when a module requests it. - pub fn get_repo(&self) -> Result<&Repo, git2::Error> { - self.repo.get_or_try_init(|| -> Result<Repo, git2::Error> { - let repository = if env::var("GIT_DIR").is_ok() { - Repository::open_from_env() - } else { - let dirs: [PathBuf; 0] = []; - Repository::open_ext(&self.current_dir, git2::RepositoryOpenFlags::FROM_ENV, dirs) - }?; - Ok(Repo { - branch: get_current_branch(&repository), - workdir: repository.workdir().map(Path::to_path_buf), - path: Path::to_path_buf(repository.path()), - state: repository.state(), - remote: get_remote_repository_info(&repository), + pub fn get_repo(&self) -> Result<&Repo, git::discover::Error> { + self.repo + .get_or_try_init(|| -> Result<Repo, git::discover::Error> { + // custom open options + let mut git_open_opts_map = + git_sec::trust::Mapping::<git::open::Options>::default(); + + // don't use the global git configs + let config = git::permissions::Config { + system: false, + git: false, + user: false, + env: true, + includes: true, + }; + // change options for config permissions without touching anything else + git_open_opts_map.reduced = + git_open_opts_map.reduced.permissions(git::Permissions { + config, + ..git::Permissions::default_for_level(git_sec::Trust::Reduced) + }); + git_open_opts_map.full = git_open_opts_map.full.permissions(git::Permissions { + config, + ..git::Permissions::default_for_level(git_sec::Trust::Full) + }); + + let shared_repo = ThreadSafeRepository::discover_with_environment_overrides_opts( + &self.current_dir, + Default::default(), + git_open_opts_map, + )?; + + let repository = shared_repo.to_thread_local(); + let branch = get_current_branch(&repository); + let remote = get_remote_repository_info(&repository, branch.as_deref()); + let path = repository.path().to_path_buf(); + Ok(Repo { + repo: shared_repo, + branch, + workdir: repository.work_dir().map(PathBuf::from), + path, + state: repository.state(), + remote, + }) }) - }) } pub fn dir_contents(&self) -> Result<&DirContents, std::io::Error> { @@ -494,6 +527,8 @@ impl DirContents { } pub struct Repo { + pub repo: ThreadSafeRepository, + /// If `current_dir` is a git repository or is contained within one, /// this is the current branch name of that repo. pub branch: Option<String>, @@ -506,7 +541,7 @@ pub struct Repo { pub path: PathBuf, /// State - pub state: RepositoryState, + pub state: Option<git_state::InProgress>, /// Remote repository pub remote: Option<Remote>, @@ -514,8 +549,8 @@ pub struct Repo { impl Repo { /// Opens the associated git repository. - pub fn open(&self) -> Result<Repository, git2::Error> { - Repository::open(&self.path) + pub fn open(&self) -> Repository { + self.repo.to_thread_local() } } @@ -570,54 +605,26 @@ impl<'a> ScanDir<'a> { } fn get_current_branch(repository: &Repository) -> Option<String> { - let head = match repository.head() { - Ok(reference) => reference, - Err(e) => { - return if e.code() == UnbornBranch { - // HEAD should only be an unborn branch if the repository is fresh, - // in that case read directly from `.git/HEAD` - let mut head_path = repository.path().to_path_buf(); - head_path.push("HEAD"); - - // get first line, then last path segment - fs::read_to_string(&head_path) - .ok()? - .lines() - .next()? - .trim() - .split('/') - .last() - .map(std::borrow::ToOwned::to_owned) - } else { - None - }; - } - }; - - let shorthand = head.shorthand(); + let name = repository.head_name().ok()??; + let shorthand = name.shorten(); - shorthand.map(std::string::ToString::to_string) + Some(shorthand.to_string()) } -fn get_remote_repository_info(repository: &Repository) -> Option<Remote> { - if let Ok(head) = repository.head() { - if let Some(local_branch_ref) = head.name() { - let remote_ref = match repository.branch_upstream_name(local_branch_ref) { - Ok(remote_ref) => remote_ref.as_str()?.to_owned(), - Err(_) => return None, - }; - - let mut v = remote_ref.splitn(4, '/'); - let remote_name = v.nth(2)?.to_owned(); - let remote_branch = v.last()?.to_owned(); - - return Some(Remote { - branch: Some(remote_branch), - name: Some(remote_name), - }); - } - } - None +fn get_remote_repository_info( + repository: &Repository, + branch_name: Option<&str>, +) -> Option<Remote> { + let branch_name = branch_name?; + let branch = repository + .remote_ref(branch_name) + .and_then(|r| r.ok()) + .map(|r| r.shorten().to_string()); + let name = repository + .branch_remote_name(branch_name) + .map(|n| n.to_string()); + + Some(Remote { branch, name }) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/src/modules/directory.rs b/src/modules/directory.rs index 29dc94bd1..99c9b8ad7 100644 --- a/src/modules/directory.rs +++ b/src/modules/directory.rs @@ -112,12 +112,12 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { { let root = repo_path_vec[0]; let before = dir_string.replace(&contracted_path, ""); - [prefix + &before, root.to_string(), after_repo_root] + [prefix + before.as_str(), root.to_string(), after_repo_root] } else { - ["".to_string(), "".to_string(), prefix + &dir_string] + ["".to_string(), "".to_string(), prefix + dir_string.as_str()] } } - _ => ["".to_string(), "".to_string(), prefix + &dir_string], + _ => ["".to_string(), "".to_string(), prefix + dir_string.as_str()], }; let path_vec = if config.use_os_path_sep { diff --git a/src/modules/git_branch.rs b/src/modules/git_branch.rs index 8d96a8bb4..46591d625 100644 --- a/src/modules/git_branch.rs +++ b/src/modules/git_branch.rs @@ -26,12 +26,8 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { let repo = context.get_repo().ok()?; - if config.only_attached { - if let Ok(git_repo) = repo.open() { - if git_repo.head_detached().ok()? { - return None; - } - } + if config.only_attached && repo.open().head().ok()?.is_detached() { + return None; } let branch_name = repo.branch.as_ref()?; diff --git a/src/modules/git_commit.rs b/src/modules/git_commit.rs index e536ff3f8..bc2fba510 100644 --- a/src/modules/git_commit.rs +++ b/src/modules/git_commit.rs @@ -1,8 +1,8 @@ use super::{Context, Module, ModuleConfig}; -use crate::formatter::string_formatter::StringFormatterError; -use git2::Time; +use git_repository::commit::describe::SelectRef::AnnotatedTags; use crate::configs::git_commit::GitCommitConfig; +use crate::context::Repo; use crate::formatter::StringFormatter; /// Creates a module with the Git commit in the current directory @@ -13,45 +13,14 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { let config: GitCommitConfig = GitCommitConfig::try_load(module.config); let repo = context.get_repo().ok()?; - let git_repo = repo.open().ok()?; + let git_repo = repo.open(); + let git_head = git_repo.head().ok()?; - let is_detached = git_repo.head_detached().ok()?; + let is_detached = git_head.is_detached(); if config.only_detached && !is_detached { return None; }; - let git_head = git_repo.head().ok()?; - let head_commit = git_head.peel_to_commit().ok()?; - let commit_oid = head_commit.id(); - - let mut tag_name = String::new(); - if !config.tag_disabled { - // Let's get repo tags names - let tag_names = git_repo.tag_names(None).ok()?; - let tag_and_refs = tag_names.iter().flatten().flat_map(|name| { - let full_tag = format!("refs/tags/{}", name); - let tag_obj = git_repo.find_reference(&full_tag)?.peel_to_tag()?; - let sig_obj = tag_obj.tagger(); - git_repo.find_reference(&full_tag).map(|reference| { - ( - String::from(name), - // fall back to oldest + 1s time if sig_obj is unavailable - sig_obj.map_or(git2::Time::new(1, 0), |s| s.when()), - reference, - ) - }) - }); - - let mut oldest = Time::new(0, 0); - // Let's check if HEAD has some tag. If several, gets last created one... - for (name, timestamp, reference) in tag_and_refs.rev() { - if commit_oid == reference.peel_to_commit().ok()?.id() && timestamp > oldest { - tag_name = name; - oldest = timestamp; - } - } - }; - let parsed = StringFormatter::new(config.format).and_then(|formatter| { formatter .map_style(|variable| match variable { @@ -59,11 +28,12 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { _ => None, }) .map(|variable| match variable { - "hash" => Some(Ok(id_to_hex_abbrev( - commit_oid.as_bytes(), - config.commit_hash_length, + "hash" => Some(Ok(git_hash(context.get_repo().ok()?, &config)?)), + "tag" => Some(Ok(format!( + "{}{}", + config.tag_symbol, + git_tag(context.get_repo().ok()?)? ))), - "tag" => format_tag(config.tag_symbol, &tag_name), _ => None, }) .parse(None, Some(context)) @@ -80,23 +50,24 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { Some(module) } -fn format_tag(symbol: &str, tag_name: &str) -> Option<Result<String, StringFormatterError>> { - if tag_name.is_empty() { - None - } else { - Some(Ok(format!("{}{}", &symbol, &tag_name))) - } +fn git_tag(repo: &Repo) -> Option<String> { + let git_repo = repo.open(); + let head_commit = git_repo.head_commit().ok()?; + + let describe_platform = head_commit.describe().names(AnnotatedTags); + let formatter = describe_platform.try_format().ok()??; + + Some(formatter.name?.to_string()) } -/// len specifies length of hex encoded string -fn id_to_hex_abbrev(bytes: &[u8], len: usize) -> String { - bytes - .iter() - .map(|b| format!("{:02x}", b)) - .collect::<String>() - .chars() - .take(len) - .collect() +fn git_hash(repo: &Repo, config: &GitCommitConfig) -> Option<String> { + let git_repo = repo.open(); + let head_id = git_repo.head_id().ok()?; + + Some(format!( + "{}", + head_id.to_hex_with_len(config.commit_hash_length) + )) } #[cfg(test)] diff --git a/src/modules/git_state.rs b/src/modules/git_state.rs index 526e8069d..92727933f 100644 --- a/src/modules/git_state.rs +++ b/src/modules/git_state.rs @@ -1,4 +1,4 @@ -use git2::RepositoryState; +use git_repository::state::InProgress; use std::path::PathBuf; use super::{Context, Module, ModuleConfig}; @@ -54,54 +54,53 @@ fn get_state_description<'a>( repo: &'a Repo, config: &GitStateConfig<'a>, ) -> Option<StateDescription<'a>> { - match repo.state { - RepositoryState::Clean => None, - RepositoryState::Merge => Some(StateDescription { + match repo.state.as_ref()? { + InProgress::Merge => Some(StateDescription { label: config.merge, current: None, total: None, }), - RepositoryState::Revert => Some(StateDescription { + InProgress::Revert => Some(StateDescription { label: config.revert, current: None, total: None, }), - RepositoryState::RevertSequence => Some(StateDescription { + InProgress::RevertSequence => Some(StateDescription { label: config.revert, current: None, total: None, }), - RepositoryState::CherryPick => Some(StateDescription { + InProgress::CherryPick => Some(StateDescription { label: config.cherry_pick, current: None, total: None, }), - RepositoryState::CherryPickSequence => Some(StateDescription { + InProgress::CherryPickSequence => Some(StateDescription { label: config.cherry_pick, current: None, total: None, }), - RepositoryState::Bisect => Some(StateDescription { + InProgress::Bisect => Some(StateDescription { label: config.bisect, current: None, total: None, }), - RepositoryState::ApplyMailbox => Some(StateDescription { + InProgress::ApplyMailbox => Some(StateDescription { label: config.am, current: None, total: None, }), - RepositoryState::ApplyMailboxOrRebase => Some(StateDescription { + InProgress::ApplyMailboxRebase => Some(StateDescription { label: config.am_or_rebase, current: None, total: None, }), - RepositoryState::Rebase => Some(describe_rebase(repo, config.rebase)), - RepositoryState::RebaseInteractive => Some(describe_rebase(repo, config.rebase)), - RepositoryState::RebaseMerge => Some(describe_rebase(repo, config.rebase)), + InProgress::Rebase => Some(describe_rebase(repo, config.rebase)), + InProgress::RebaseInteractive => Some(describe_rebase(repo, config.rebase)), } } +// TODO: Use future gitoxide API to get the state of the rebase 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 diff --git a/src/modules/git_status.rs b/src/modules/git_status.rs index d90f8e8f7..aa3f03878 100644 --- a/src/modules/git_status.rs +++ b/src/modules/git_status.rs @@ -34,6 +34,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { //Return None if not in git repository context.get_repo().ok()?; + if let Some(git_status) = git_status_wsl(context, &config) { if git_status.is_empty() { return None; diff --git a/src/modules/hg_branch.rs b/src/modules/hg_branch.rs index ae1ac7377..aa048a579 100644 --- a/src/modules/hg_branch.rs +++ b/src/modules/hg_branch.rs @@ -41,7 +41,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> { // The truncation symbol should only be added if we truncated let truncated_and_symbol = if len < graphemes_len(&branch_name) { let truncation_symbol = get_graphemes(config.truncation_symbol, 1); - truncated_graphemes + &truncation_symbol + truncated_graphemes + truncation_symbol.as_str() } else { truncated_graphemes }; diff --git a/src/modules/hostname.rs b/src/modules/hostname.rs index 3621dc320..fdb991941 100644 --- a/src/modules/hostname.rs +++ b/src/modules/hostname.rs @@ -150,7 +150,7 @@ mod tests { .collect(); let expected = Some(format!( "{} in ", - style().paint("🌐 ".to_owned() + &hostname) + style().paint("🌐 ".to_owned() + hostname.as_str()) )); assert_eq!(expected, actual); |