(cwd: &Path, args: &[&str])
| 373 | } |
| 374 | |
| 375 | fn run_git(cwd: &Path, args: &[&str]) { |
| 376 | // A cwd that does not yet exist makes the spawn itself fail with |
| 377 | // ENOENT, which is indistinguishable from git-not-found; guard it so |
| 378 | // any real failure is attributable. |
| 379 | assert!( |
| 380 | cwd.is_dir(), |
| 381 | "git cwd {cwd:?} should exist before running git {args:?}" |
| 382 | ); |
| 383 | let git = git_program(); |
| 384 | // Retry a transient spawn ENOENT a few times: under load the initial |
| 385 | // fork/exec can spuriously fail even with a valid absolute program. |
| 386 | let mut last_err: Option<std::io::Error> = None; |
| 387 | let mut output = None; |
| 388 | for attempt in 0..5 { |
| 389 | match Command::new(&git).args(args).current_dir(cwd).output() { |
| 390 | Ok(out) => { |
| 391 | output = Some(out); |
| 392 | break; |
| 393 | } |
| 394 | Err(e) if e.kind() == std::io::ErrorKind::NotFound && attempt < 4 => { |
| 395 | last_err = Some(e); |
| 396 | std::thread::sleep(std::time::Duration::from_millis(20 * (attempt + 1))); |
| 397 | } |
| 398 | Err(e) => { |
| 399 | panic!("git {args:?} should run (program {git:?}): {e}"); |
| 400 | } |
| 401 | } |
| 402 | } |
| 403 | let output = output.unwrap_or_else(|| { |
| 404 | panic!("git {args:?} should run (program {git:?}) after retries: {last_err:?}") |
| 405 | }); |
| 406 | assert!( |
| 407 | output.status.success(), |
| 408 | "git {:?} failed\nstdout:\n{}\nstderr:\n{}", |
| 409 | args, |
| 410 | String::from_utf8_lossy(&output.stdout), |
| 411 | String::from_utf8_lossy(&output.stderr) |
| 412 | ); |
| 413 | } |
| 414 | |
| 415 | #[cfg(windows)] |
| 416 | fn git_test_root(path: &Path) -> std::path::PathBuf { |
no test coverage detected