summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Knaack <davidkna@users.noreply.github.com>2022-08-09 04:33:00 +0200
committerGitHub <noreply@github.com>2022-08-08 22:33:00 -0400
commitac55a01d0ffe907ef7af48c9597c0bca4dbd8c69 (patch)
tree777418ce50e602d21b737ac02d76078c386665d3 /src
parentf614fcdc1f2b7ad2cbfd6865a774775c032001de (diff)
feat(git): replace `git2` with `git-repository` (#3883)
Diffstat (limited to 'src')
-rw-r--r--src/context.rs135
-rw-r--r--src/modules/directory.rs6
-rw-r--r--src/modules/git_branch.rs8
-rw-r--r--src/modules/git_commit.rs81
-rw-r--r--src/modules/git_state.rs27
-rw-r--r--src/modules/git_status.rs1
-rw-r--r--src/modules/hg_branch.rs2
-rw-r--r--src/modules/hostname.rs2
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);