mirror of
https://github.com/openai/codex.git
synced 2026-09-13 11:47:17 +00:00
## Why Updating a file with `apply_patch` historically normalized its contents to LF, which can rewrite line endings outside the requested change. ## What changed - Add an opt-in `PreserveLineEndings` update mode and thread it through patch verification, application, and the standalone executable. Existing APIs keep the LF-normalizing mode by default. - Retain the original endings of untouched and context lines, including in mixed-ending files, and use the file's first line ending for inserted or replaced lines. - Track patch context separately from identical changed lines so preservation does not alter replacement ordering or matching. ## Testing Add coverage for CRLF, CR, mixed endings, trailing blank lines, repeated lines, EOF overlap rejection, and legacy LF normalization. GitOrigin-RevId: 085f0cbc57e0965aa62c6a2d0e80870424e762f5
88 lines
2.7 KiB
Rust
88 lines
2.7 KiB
Rust
use std::io::Read;
|
|
use std::io::Write;
|
|
|
|
pub fn main() -> ! {
|
|
let exit_code = run_main();
|
|
std::process::exit(exit_code);
|
|
}
|
|
|
|
/// We would prefer to return `std::process::ExitCode`, but its `exit_process()`
|
|
/// method is still a nightly API and we want main() to return !.
|
|
pub fn run_main() -> i32 {
|
|
// Expect either one argument (the full apply_patch payload) or read it from stdin.
|
|
let mut args = std::env::args_os();
|
|
let _argv0 = args.next();
|
|
|
|
let patch_arg = match args.next() {
|
|
Some(arg) => match arg.into_string() {
|
|
Ok(s) => s,
|
|
Err(_) => {
|
|
eprintln!("Error: apply_patch requires a UTF-8 PATCH argument.");
|
|
return 1;
|
|
}
|
|
},
|
|
None => {
|
|
// No argument provided; attempt to read the patch from stdin.
|
|
let mut buf = String::new();
|
|
match std::io::stdin().read_to_string(&mut buf) {
|
|
Ok(_) => {
|
|
if buf.is_empty() {
|
|
eprintln!("Usage: apply_patch 'PATCH'\n echo 'PATCH' | apply_patch");
|
|
return 2;
|
|
}
|
|
buf
|
|
}
|
|
Err(err) => {
|
|
eprintln!("Error: Failed to read PATCH from stdin.\n{err}");
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Refuse extra args to avoid ambiguity.
|
|
if args.next().is_some() {
|
|
eprintln!("Error: apply_patch accepts exactly one argument.");
|
|
return 2;
|
|
}
|
|
|
|
let mut stdout = std::io::stdout();
|
|
let mut stderr = std::io::stderr();
|
|
let cwd = match codex_utils_absolute_path::AbsolutePathBuf::current_dir() {
|
|
Ok(cwd) => cwd,
|
|
Err(err) => {
|
|
eprintln!("Error: Failed to determine current directory.\n{err}");
|
|
return 1;
|
|
}
|
|
};
|
|
let runtime = match tokio::runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
{
|
|
Ok(runtime) => runtime,
|
|
Err(err) => {
|
|
eprintln!("Error: Failed to initialize runtime.\n{err}");
|
|
return 1;
|
|
}
|
|
};
|
|
// TODO(anp): Discover the standalone executable cwd as PathUri directly.
|
|
let cwd = codex_utils_path_uri::PathUri::from_abs_path(&cwd);
|
|
let update_file_mode = crate::apply_patch_file_update_mode_from_env();
|
|
match runtime.block_on(crate::apply_patch_with_mode(
|
|
&patch_arg,
|
|
update_file_mode,
|
|
&cwd,
|
|
&mut stdout,
|
|
&mut stderr,
|
|
codex_exec_server::LOCAL_FS.as_ref(),
|
|
/*sandbox*/ None,
|
|
)) {
|
|
Ok(_) => {
|
|
// Flush to ensure output ordering when used in pipelines.
|
|
let _ = stdout.flush();
|
|
0
|
|
}
|
|
Err(_) => 1,
|
|
}
|
|
}
|