ok/jj
1
0
Fork 0
forked from mirrors/jj

cli: rewrite base GitIgnoreFile lookup to use gitoxide instead of libgit2

Since gix::Repository::config_snapshot() borrows the repo instance, it has to
be allocated in caller's stack. That's why GitBackend::git_config() is removed.
This commit is contained in:
Yuya Nishihara 2023-10-31 09:50:10 +09:00
parent c88e69ad6f
commit 162dcd49b4
4 changed files with 26 additions and 28 deletions

1
Cargo.lock generated
View file

@ -1616,6 +1616,7 @@ dependencies = [
"esl01-renderdag", "esl01-renderdag",
"futures 0.3.29", "futures 0.3.29",
"git2", "git2",
"gix",
"hex", "hex",
"indexmap", "indexmap",
"insta", "insta",

View file

@ -41,6 +41,7 @@ dirs = { workspace = true }
esl01-renderdag = { workspace = true } esl01-renderdag = { workspace = true }
futures = { workspace = true } futures = { workspace = true }
git2 = { workspace = true } git2 = { workspace = true }
gix = { workspace = true }
hex = { workspace = true } hex = { workspace = true }
indexmap = { workspace = true } indexmap = { workspace = true }
itertools = { workspace = true } itertools = { workspace = true }

View file

@ -17,7 +17,6 @@ use std::env::{self, ArgsOs, VarError};
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::fmt::Debug; use std::fmt::Debug;
use std::io::Write as _; use std::io::Write as _;
use std::iter;
use std::ops::Deref; use std::ops::Deref;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::process::ExitCode; use std::process::ExitCode;
@ -25,6 +24,7 @@ use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::SystemTime; use std::time::SystemTime;
use std::{iter, str};
use clap::builder::{NonEmptyStringValueParser, TypedValueParser, ValueParserFactory}; use clap::builder::{NonEmptyStringValueParser, TypedValueParser, ValueParserFactory};
use clap::{Arg, ArgAction, ArgMatches, Command, FromArgMatches}; use clap::{Arg, ArgAction, ArgMatches, Command, FromArgMatches};
@ -926,16 +926,18 @@ impl WorkspaceCommandHelper {
} }
} }
pub fn git_config(&self) -> Result<git2::Config, git2::Error> { #[instrument(skip_all)]
if let Some(git_backend) = self.git_backend() { pub fn base_ignores(&self) -> Arc<GitIgnoreFile> {
git_backend.git_config() fn get_excludes_file_path(config: &gix::config::File) -> Option<PathBuf> {
// TODO: maybe use path_by_key() and interpolate(), which can process non-utf-8
// path on Unix.
if let Some(value) = config.string_by_key("core.excludesFile") {
str::from_utf8(&value).ok().map(expand_git_path)
} else { } else {
git2::Config::open_default() xdg_config_home().ok().map(|x| x.join("git").join("ignore"))
} }
} }
#[instrument(skip_all)]
pub fn base_ignores(&self) -> Arc<GitIgnoreFile> {
fn xdg_config_home() -> Result<PathBuf, VarError> { fn xdg_config_home() -> Result<PathBuf, VarError> {
if let Ok(x) = std::env::var("XDG_CONFIG_HOME") { if let Ok(x) = std::env::var("XDG_CONFIG_HOME") {
if !x.is_empty() { if !x.is_empty() {
@ -946,20 +948,17 @@ impl WorkspaceCommandHelper {
} }
let mut git_ignores = GitIgnoreFile::empty(); let mut git_ignores = GitIgnoreFile::empty();
if let Ok(excludes_file_path) = self if let Some(git_backend) = self.git_backend() {
.git_config() let git_repo = git_backend.git_repo();
.and_then(|git_config| { if let Some(excludes_file_path) = get_excludes_file_path(&git_repo.config_snapshot()) {
git_config
.get_string("core.excludesFile")
.map(expand_git_path)
})
.or_else(|_| xdg_config_home().map(|x| x.join("git").join("ignore")))
{
git_ignores = git_ignores.chain_with_file("", excludes_file_path); git_ignores = git_ignores.chain_with_file("", excludes_file_path);
} }
if let Some(git_backend) = self.git_backend() {
git_ignores = git_ignores git_ignores = git_ignores
.chain_with_file("", git_backend.git_repo_path().join("info").join("exclude")); .chain_with_file("", git_backend.git_repo_path().join("info").join("exclude"));
} else if let Ok(git_config) = gix::config::File::from_globals() {
if let Some(excludes_file_path) = get_excludes_file_path(&git_config) {
git_ignores = git_ignores.chain_with_file("", excludes_file_path);
}
} }
git_ignores git_ignores
} }
@ -1853,7 +1852,7 @@ export or their "parent" branches."#,
} }
/// Expands "~/" to "$HOME/" as Git seems to do for e.g. core.excludesFile. /// Expands "~/" to "$HOME/" as Git seems to do for e.g. core.excludesFile.
fn expand_git_path(path_str: String) -> PathBuf { fn expand_git_path(path_str: &str) -> PathBuf {
if let Some(remainder) = path_str.strip_prefix("~/") { if let Some(remainder) = path_str.strip_prefix("~/") {
if let Ok(home_dir_str) = std::env::var("HOME") { if let Ok(home_dir_str) = std::env::var("HOME") {
return PathBuf::from(home_dir_str).join(remainder); return PathBuf::from(home_dir_str).join(remainder);

View file

@ -206,19 +206,16 @@ impl GitBackend {
self.repo.lock().unwrap() self.repo.lock().unwrap()
} }
// TODO: add public API to obtain new gix::Repository from base_repo /// Returns new thread-local instance to access to the underlying Git repo.
pub fn git_repo(&self) -> gix::Repository {
self.base_repo.to_thread_local()
}
/// Creates new owned git repository instance. /// Creates new owned git repository instance.
pub fn open_git_repo(&self) -> Result<git2::Repository, git2::Error> { pub fn open_git_repo(&self) -> Result<git2::Repository, git2::Error> {
git2::Repository::open(self.git_repo_path()) git2::Repository::open(self.git_repo_path())
} }
/// Git configuration for this repository.
pub fn git_config(&self) -> Result<git2::Config, git2::Error> {
// TODO: switch to gix config type
self.open_git_repo().and_then(|repo| repo.config())
}
/// Path to the `.git` directory or the repository itself if it's bare. /// Path to the `.git` directory or the repository itself if it's bare.
pub fn git_repo_path(&self) -> &Path { pub fn git_repo_path(&self) -> &Path {
self.base_repo.path() self.base_repo.path()