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:
parent
c88e69ad6f
commit
162dcd49b4
4 changed files with 26 additions and 28 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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> {
|
|
||||||
if let Some(git_backend) = self.git_backend() {
|
|
||||||
git_backend.git_config()
|
|
||||||
} else {
|
|
||||||
git2::Config::open_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub fn base_ignores(&self) -> Arc<GitIgnoreFile> {
|
pub fn base_ignores(&self) -> Arc<GitIgnoreFile> {
|
||||||
|
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 {
|
||||||
|
xdg_config_home().ok().map(|x| x.join("git").join("ignore"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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
|
|
||||||
.git_config()
|
|
||||||
.and_then(|git_config| {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
if let Some(git_backend) = self.git_backend() {
|
if let Some(git_backend) = self.git_backend() {
|
||||||
|
let git_repo = git_backend.git_repo();
|
||||||
|
if let Some(excludes_file_path) = get_excludes_file_path(&git_repo.config_snapshot()) {
|
||||||
|
git_ignores = git_ignores.chain_with_file("", excludes_file_path);
|
||||||
|
}
|
||||||
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);
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue