mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
git-utils: integrate filter guards with repository authority
This commit is contained in:
+102
-120
@@ -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>> {
|
||||
|
||||
Reference in New Issue
Block a user