mirror of
https://github.com/openai/codex.git
synced 2026-09-06 15:29:32 +00:00
git-utils: preserve sentinel filter status checks
This commit is contained in:
@@ -137,12 +137,17 @@ pub(crate) async fn selected_executable_filter_from(
|
||||
) -> Result<Option<(String, Vec<u8>)>, GitReadError> {
|
||||
let git_root = resolve_git_root_async(git, cwd).await?;
|
||||
let entries = read_filter_config_async(git, &git_root).await?;
|
||||
if !entries.values().any(|entry| !entry.value.is_empty()) {
|
||||
let executable_drivers =
|
||||
executable_filter_drivers(&entries).map_err(|_| invalid_output("filterConfig"))?;
|
||||
if executable_drivers.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let paths = read_paths_async(git, &git_root, PathSelection::Tracked).await?;
|
||||
let attributes = read_filter_attributes_async(git, &git_root, &paths).await?;
|
||||
selected_executable_filter(&entries, &attributes).map_err(|_| invalid_output("filterSelection"))
|
||||
let attributes =
|
||||
resolve_filter_attribute_sentinels_async(git, &git_root, attributes, &executable_drivers)
|
||||
.await?;
|
||||
Ok(selected_filter(&executable_drivers, &attributes))
|
||||
}
|
||||
|
||||
async fn resolve_git_root_async(git: &GitRunner, cwd: &Path) -> Result<PathBuf, GitReadError> {
|
||||
@@ -492,7 +497,7 @@ async fn read_filter_attributes_async(
|
||||
git: &GitRunner,
|
||||
cwd: &Path,
|
||||
paths: &[Vec<u8>],
|
||||
) -> Result<BTreeMap<Vec<u8>, String>, GitReadError> {
|
||||
) -> Result<BTreeMap<Vec<u8>, FilterAttributeValue>, GitReadError> {
|
||||
if paths.is_empty() {
|
||||
return Ok(BTreeMap::new());
|
||||
}
|
||||
@@ -526,6 +531,88 @@ async fn read_filter_attributes_async(
|
||||
parse_filter_attributes(&output.stdout, paths).map_err(|_| invalid_output("filterAttributes"))
|
||||
}
|
||||
|
||||
async fn resolve_filter_attribute_sentinels_async(
|
||||
git: &GitRunner,
|
||||
cwd: &Path,
|
||||
attributes: BTreeMap<Vec<u8>, FilterAttributeValue>,
|
||||
executable_drivers: &BTreeSet<String>,
|
||||
) -> Result<BTreeMap<Vec<u8>, String>, GitReadError> {
|
||||
let mut resolved = BTreeMap::new();
|
||||
for (path, attribute) in attributes {
|
||||
match attribute {
|
||||
FilterAttributeValue::Driver(driver) => {
|
||||
resolved.insert(path, driver);
|
||||
}
|
||||
FilterAttributeValue::AmbiguousSentinel(driver) => {
|
||||
if executable_drivers.contains(&driver)
|
||||
&& sentinel_spelling_selects_filter_driver_async(git, cwd, &path, &driver)
|
||||
.await?
|
||||
{
|
||||
resolved.insert(path, driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(resolved)
|
||||
}
|
||||
|
||||
async fn sentinel_spelling_selects_filter_driver_async(
|
||||
git: &GitRunner,
|
||||
cwd: &Path,
|
||||
path: &[u8],
|
||||
driver: &str,
|
||||
) -> Result<bool, GitReadError> {
|
||||
let required =
|
||||
run_sentinel_selection_probe_async(git, cwd, path, driver, /*required*/ true).await?;
|
||||
if required.status.success() {
|
||||
return Ok(false);
|
||||
}
|
||||
let optional =
|
||||
run_sentinel_selection_probe_async(git, cwd, path, driver, /*required*/ false).await?;
|
||||
if optional.status.success() {
|
||||
return Ok(true);
|
||||
}
|
||||
Err(command_failed(
|
||||
"filterAttributeSelection",
|
||||
optional.status.code(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn run_sentinel_selection_probe_async(
|
||||
git: &GitRunner,
|
||||
cwd: &Path,
|
||||
path: &[u8],
|
||||
driver: &str,
|
||||
required: bool,
|
||||
) -> Result<std::process::Output, GitReadError> {
|
||||
let path = git_path_argument(path).map_err(|_| invalid_output("filterAttributeSelection"))?;
|
||||
let mut command = git.tokio_command();
|
||||
command
|
||||
.env("GIT_OPTIONAL_LOCKS", "0")
|
||||
.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(path)
|
||||
.current_dir(cwd)
|
||||
.stdin(Stdio::null())
|
||||
.kill_on_drop(true);
|
||||
command_output(git, command, "filterAttributeSelection").await
|
||||
}
|
||||
|
||||
async fn command_output(
|
||||
git: &GitRunner,
|
||||
command: TokioCommand,
|
||||
@@ -731,7 +818,6 @@ fn selected_executable_filter_for(
|
||||
Ok(selected_filter(&executable_drivers, attributes))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn selected_filter(
|
||||
drivers: &BTreeSet<String>,
|
||||
attributes: &BTreeMap<Vec<u8>, String>,
|
||||
|
||||
@@ -342,6 +342,54 @@ async fn get_has_changes_rejects_configured_clean_filter_without_running_it() {
|
||||
assert!(!configured_filter_ran(&repo_path).await);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_has_changes_distinguishes_filter_sentinels_from_literal_driver_names() {
|
||||
for (driver, sentinel_attribute) in
|
||||
[("set", "filter"), ("unset", "-filter"), ("unspecified", "")]
|
||||
{
|
||||
let temp_dir = tempfile::tempdir().expect("create temp dir");
|
||||
let repo_path = create_test_git_repo(&temp_dir).await;
|
||||
let clean_key = format!("filter.{driver}.clean");
|
||||
run_git(
|
||||
&repo_path,
|
||||
&[
|
||||
"config",
|
||||
&clean_key,
|
||||
"git config codex.filterran true && git hash-object --stdin",
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
let sentinel = if sentinel_attribute.is_empty() {
|
||||
String::new()
|
||||
} else {
|
||||
format!("test.txt {sentinel_attribute}\n")
|
||||
};
|
||||
std::fs::write(repo_path.join(".gitattributes"), sentinel)
|
||||
.expect("write sentinel attribute");
|
||||
run_git(&repo_path, &["add", ".gitattributes"]).await;
|
||||
run_git(&repo_path, &["commit", "-m", "sentinel attribute"]).await;
|
||||
|
||||
assert_eq!(try_get_has_changes(&repo_path).await, Ok(false), "{driver}");
|
||||
assert!(!configured_filter_ran(&repo_path).await, "{driver}");
|
||||
|
||||
std::fs::write(
|
||||
repo_path.join(".gitattributes"),
|
||||
format!("test.txt filter={driver}\n"),
|
||||
)
|
||||
.expect("write literal sentinel-named driver");
|
||||
assert_eq!(
|
||||
try_get_has_changes(&repo_path).await,
|
||||
Err(GitReadError::SelectedExecutableFilter {
|
||||
driver: driver.to_string(),
|
||||
path: "test.txt".to_string(),
|
||||
}),
|
||||
"{driver}"
|
||||
);
|
||||
assert!(!configured_filter_ran(&repo_path).await, "{driver}");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn checked_has_changes_distinguishes_non_repository() {
|
||||
let temp_dir = tempfile::tempdir().expect("create temp dir");
|
||||
|
||||
Reference in New Issue
Block a user