git-utils: integrate filter guards with repository authority

This commit is contained in:
Chris Bookholt
2026-07-02 00:46:29 -07:00
parent 801018897f
commit e8b30bd7d3
19 changed files with 3197 additions and 173 deletions
+102 -120
View File
@@ -9,15 +9,21 @@ use std::process::Stdio;
use crate::git_command::GitRunner;
use crate::git_config::GitConfigEntry;
use crate::git_config::parse_effective_config;
use crate::git_config::parse_effective_config_with_origins;
use crate::git_config::read_effective_config_with_fallback as read_effective_config_unchecked;
use crate::git_config_sources::ensure_no_worktree_config_sources;
#[path = "filter_sentinel.rs"]
mod filter_sentinel;
pub(crate) use filter_sentinel::SentinelFilterProbeBudget;
pub(crate) use filter_sentinel::SentinelFilterProbeResolution;
pub(crate) use filter_sentinel::classify_sentinel_filter_probes;
pub(crate) use filter_sentinel::sentinel_filter_probe_config_args;
pub(crate) const DISABLED_HOOKS_PATH: &str = if cfg!(windows) { "NUL" } else { "/dev/null" };
pub(crate) const EXECUTABLE_FILTER_CONFIG_PATTERN: &str =
r"^filter\..*\.(clean|smudge|process|required)$";
#[derive(Debug, Clone, PartialEq, Eq)]
enum FilterAttributeValue {
pub(crate) enum FilterAttributeValue {
Driver(String),
AmbiguousSentinel(String),
}
@@ -82,13 +88,20 @@ pub(crate) fn ensure_no_selected_executable_git_filters(
filter_config: entries,
});
}
let guard = executable_filter_guard(git, cwd, entries, &executable_drivers)?;
let paths = paths
.iter()
.map(|path| path.as_bytes().to_vec())
.collect::<Vec<_>>();
let attributes =
read_filter_attributes(git, cwd, &paths, git_config_args, &executable_drivers)?;
if let Some((driver, path)) = selected_executable_filter(&entries, &attributes)? {
let attributes = read_filter_attributes(
git,
cwd,
&paths,
git_config_args,
&executable_drivers,
&guard,
)?;
if let Some((driver, path)) = selected_executable_filter(&guard.filter_config, &attributes)? {
return Err(io::Error::new(
io::ErrorKind::Unsupported,
format!(
@@ -97,7 +110,7 @@ pub(crate) fn ensure_no_selected_executable_git_filters(
),
));
}
executable_filter_guard(git, cwd, entries, &executable_drivers)
Ok(guard)
}
fn executable_filter_guard(
@@ -146,12 +159,12 @@ impl GitFilterNeutralization {
name: &str,
value: &str,
) -> io::Result<()> {
let mut command = git.command();
command
.args(["config", "--file"])
.arg(config_path)
.args(["--add", &format!("filter.{driver}.{name}"), value])
.current_dir(cwd);
let mut command = git.command_for_cwd(cwd)?;
command.args(["config", "--file"]).arg(config_path).args([
"--add",
&format!("filter.{driver}.{name}"),
value,
]);
let output = git.output(command)?;
if !output.status.success() {
return Err(io::Error::other(format!(
@@ -185,56 +198,8 @@ pub(crate) fn read_effective_config_with_fallback(
pattern: &str,
probe: &str,
) -> io::Result<BTreeMap<String, GitConfigEntry>> {
let scoped =
run_effective_config_query(git, cwd, git_config_args, pattern, /*show_scope*/ true)?;
if scoped
.status
.code()
.is_some_and(|code| code == 0 || code == 1)
{
return parse_effective_config(&scoped.stdout);
}
let legacy = run_effective_config_query(
git,
cwd,
git_config_args,
pattern,
/*show_scope*/ false,
)?;
if !legacy
.status
.code()
.is_some_and(|code| code == 0 || code == 1)
{
return Err(io::Error::other(format!(
"git {probe} config probe failed with status {}: {}",
legacy.status,
String::from_utf8_lossy(&legacy.stderr).trim()
)));
}
parse_effective_config_with_origins(&legacy.stdout)
}
fn run_effective_config_query(
git: &GitRunner,
cwd: &Path,
git_config_args: &[String],
pattern: &str,
show_scope: bool,
) -> io::Result<std::process::Output> {
let mut command = git.command();
command
.env("GIT_OPTIONAL_LOCKS", "0")
.args(git_config_args)
.args(["config", "--null"]);
if show_scope {
command.arg("--show-scope");
}
command
.args(["--show-origin", "--includes", "--get-regexp", pattern])
.current_dir(cwd);
git.output(command)
ensure_no_worktree_config_sources(git, cwd, git_config_args)?;
read_effective_config_unchecked(git, cwd, git_config_args, pattern, probe)
}
fn read_filter_attributes(
@@ -243,6 +208,7 @@ fn read_filter_attributes(
paths: &[Vec<u8>],
git_config_args: &[String],
executable_drivers: &BTreeSet<String>,
neutralization: &GitFilterNeutralization,
) -> io::Result<BTreeMap<Vec<u8>, String>> {
if paths.is_empty() {
return Ok(BTreeMap::new());
@@ -251,7 +217,7 @@ fn read_filter_attributes(
write_nul_paths(&mut input, paths)?;
input.rewind()?;
let mut command = git.command();
let mut command = git.command_for_cwd(cwd)?;
command
.env("GIT_OPTIONAL_LOCKS", "0")
.args(git_config_args)
@@ -265,7 +231,6 @@ fn read_filter_attributes(
"-z",
"filter",
])
.current_dir(cwd)
.stdin(Stdio::from(input));
let output = git.output(command)?;
if !output.status.success() {
@@ -276,7 +241,14 @@ fn read_filter_attributes(
)));
}
let attributes = parse_filter_attributes(&output.stdout, paths)?;
resolve_filter_attribute_sentinels(git, cwd, attributes, git_config_args, executable_drivers)
resolve_filter_attribute_sentinels(
git,
cwd,
attributes,
git_config_args,
executable_drivers,
neutralization,
)
}
fn resolve_filter_attribute_sentinels(
@@ -285,8 +257,10 @@ fn resolve_filter_attribute_sentinels(
attributes: BTreeMap<Vec<u8>, FilterAttributeValue>,
git_config_args: &[String],
executable_drivers: &BTreeSet<String>,
neutralization: &GitFilterNeutralization,
) -> io::Result<BTreeMap<Vec<u8>, String>> {
let mut resolved = BTreeMap::new();
let mut probe_budget = SentinelFilterProbeBudget::default();
for (path, attribute) in attributes {
match attribute {
FilterAttributeValue::Driver(driver) => {
@@ -300,6 +274,8 @@ fn resolve_filter_attribute_sentinels(
&path,
&driver,
git_config_args,
neutralization,
&mut probe_budget,
)?
{
resolved.insert(path, driver);
@@ -310,39 +286,37 @@ fn resolve_filter_attribute_sentinels(
Ok(resolved)
}
/// `git check-attr` serializes both its three special states and literal
/// driver names with the same `set`, `unset`, and `unspecified` strings. Ask
/// Git to resolve the ambiguity with every command for that driver overridden
/// to empty. A required literal driver fails while a special state succeeds.
/// Retrying with the driver optional distinguishes that expected failure from
/// an unrelated probe error. No filter process or shell is started.
/// Disambiguate Git's sentinel spellings with required/optional probes. The
/// shared guard blanks every known executable driver before either probe.
fn sentinel_spelling_selects_filter_driver(
git: &GitRunner,
cwd: &Path,
path: &[u8],
driver: &str,
git_config_args: &[String],
neutralization: &GitFilterNeutralization,
probe_budget: &mut SentinelFilterProbeBudget,
) -> io::Result<bool> {
let required = run_sentinel_selection_probe(
let probe = SentinelSelectionProbe {
git,
cwd,
path,
driver,
git_config_args,
/*required*/ true,
)?;
if required.status.success() {
neutralization,
};
let required = probe.run(/*required*/ true, probe_budget)?;
if classify_sentinel_filter_probes(required.status.success(), /*optional_succeeded*/ None)
== SentinelFilterProbeResolution::SpecialAttributeState
{
return Ok(false);
}
let optional = run_sentinel_selection_probe(
git,
cwd,
path,
driver,
git_config_args,
/*required*/ false,
)?;
if optional.status.success() {
let optional = probe.run(/*required*/ false, probe_budget)?;
if classify_sentinel_filter_probes(
required.status.success(),
/*optional_succeeded*/ Some(optional.status.success()),
) == SentinelFilterProbeResolution::LiteralDriver
{
return Ok(true);
}
Err(io::Error::other(format!(
@@ -353,39 +327,47 @@ fn sentinel_spelling_selects_filter_driver(
)))
}
fn run_sentinel_selection_probe(
git: &GitRunner,
cwd: &Path,
path: &[u8],
driver: &str,
git_config_args: &[String],
required: bool,
) -> io::Result<std::process::Output> {
let mut command = git.command();
command
.env("GIT_OPTIONAL_LOCKS", "0")
.args(git_config_args)
.args([
"-c",
&format!("core.hooksPath={DISABLED_HOOKS_PATH}"),
"-c",
"core.fsmonitor=false",
"-c",
&format!("filter.{driver}.required={required}"),
"-c",
&format!("filter.{driver}.clean="),
"-c",
&format!("filter.{driver}.smudge="),
"-c",
&format!("filter.{driver}.process="),
"hash-object",
"--stdin",
])
.arg("--path")
.arg(git_path_argument(path)?)
.current_dir(cwd)
.stdin(Stdio::null());
git.output(command)
struct SentinelSelectionProbe<'a> {
git: &'a GitRunner,
cwd: &'a Path,
path: &'a [u8],
driver: &'a str,
git_config_args: &'a [String],
neutralization: &'a GitFilterNeutralization,
}
impl SentinelSelectionProbe<'_> {
fn run(
&self,
required: bool,
probe_budget: &mut SentinelFilterProbeBudget,
) -> io::Result<std::process::Output> {
let probe_config_args = sentinel_filter_probe_config_args(
self.neutralization.git_config_args(),
self.driver,
required,
)?;
let path = git_path_argument(self.path)?;
let mut command = self.git.command_for_cwd(self.cwd)?;
command
.env("GIT_OPTIONAL_LOCKS", "0")
.args(self.git_config_args)
.args([
"-c",
&format!("core.hooksPath={DISABLED_HOOKS_PATH}"),
"-c",
"core.fsmonitor=false",
])
.args(&probe_config_args)
.args(["hash-object", "--stdin"])
.arg("--path")
.arg(path)
.stdin(Stdio::null());
probe_budget.ensure_probe_available()?;
let output = self.git.output(command)?;
probe_budget.record_completed_probe();
Ok(output)
}
}
#[cfg(unix)]
@@ -415,7 +397,7 @@ fn selected_executable_filter(
Ok(None)
}
fn executable_filter_drivers(
pub(crate) fn executable_filter_drivers(
entries: &BTreeMap<String, GitConfigEntry>,
) -> io::Result<BTreeSet<String>> {
let mut executable_drivers = BTreeSet::new();
@@ -456,7 +438,7 @@ fn write_nul_paths(input: &mut std::fs::File, paths: &[Vec<u8>]) -> io::Result<(
Ok(())
}
fn parse_filter_attributes(
pub(crate) fn parse_filter_attributes(
output: &[u8],
expected_paths: &[Vec<u8>],
) -> io::Result<BTreeMap<Vec<u8>, FilterAttributeValue>> {