git verify of an unfinished import; no SIGPIPE when an import stops early f0406c3b40ed
git verify listed every commit of the branch, so on an import still going it stopped after 50 'no imported change' and never reached the commits it was asked to check. It now ends at the last imported commit, counts --last back from there, and says how many come after it. An import stopping before it had read every diff (OXY_IMPORT_STOP_AFTER, or an error) killed git diff-tree while the thread feeding it was still writing; with SIGPIPE at its default (oxy restores it for | head) that write killed oxy with signal 13. The feeder thread now blocks SIGPIPE, so its write fails and it ends. refs #81
- Author
- Andy McCall @andymccall <[email protected]>
- When
- · 2026-10-08 14:02 UTC
- Signature
- ✔ signed by Andy McCall
- Hash
f0406c3b40ed1bbaa0b43a820106cf033f1c636d9805b06882d4bb0f2a8406d2- Depends on
- 2306a4d24779 62794ecc7f74 79ab8528ff62 7eac74abc586 fce44ae9ad29
- Depended on by
- nothing yet
- In bookmarks
- bare-hashes carried-not-removed carry-identical doctor-trust identical-sides keyless-tags main merge-remote-bookmark only-help-103 paths-for-resolutions-and-amend push-notes show-stat subrepo-pin-state switch-says
- In tags
- v0.10.0 v0.11.0 v0.4.1 v0.4.2 v0.4.3 v0.4.4 v0.4.5 v0.4.6 v0.5.0 v0.5.1 v0.6.0 v0.7.0 v0.8.0 v0.9.0
- Edits
- 6 atoms, 0 tree operations, 2 files
Browse the tree at this change
modified crates/oxy-core/src/repo/git.rs +27 −2
| … | 112 unchanged lines |
||
| 113 | 113 | let stdout = std::io::BufReader::new(child.stdout.take().unwrap()); |
|
| 114 | 114 | // A thread writes, so git never waits on a full pipe for us to read. |
|
| 115 | 115 | let feeder = std::thread::spawn(move || { |
|
| 116 | + | // An import that stops before reading every diff kills git |
|
| 117 | + | // while this thread may still be writing. oxy restores |
|
| 118 | + | // SIGPIPE's default (to end quietly under `| head`), so that |
|
| 119 | + | // write would kill the whole process (signal 13, oxygen#81); |
|
| 120 | + | // blocked here, it fails with EPIPE and the thread ends. |
|
| 121 | + | #[cfg(unix)] |
|
| 122 | + | unsafe { |
|
| 123 | + | let mut set: libc::sigset_t = std::mem::zeroed(); |
|
| 124 | + | libc::sigemptyset(&mut set); |
|
| 125 | + | libc::sigaddset(&mut set, libc::SIGPIPE); |
|
| 126 | + | libc::pthread_sigmask(libc::SIG_BLOCK, &set, std::ptr::null_mut()); |
|
| 127 | + | } |
|
| 116 | 128 | let mut w = std::io::BufWriter::new(&mut stdin); |
|
| 117 | 129 | for (c, p) in pairs { |
|
| 118 | 130 | if writeln!(w, "{c} {p}").is_err() { |
|
| … | 1250 unchanged lines |
||
| 1369 | 1381 | pub files_compared: usize, |
|
| 1370 | 1382 | /// `(commit, what differs)`; empty when every commit's tree is reproduced. |
|
| 1371 | 1383 | pub mismatches: Vec<(String, String)>, |
|
| 1384 | + | /// Commits of the branch after the last imported one: an import not |
|
| 1385 | + | /// finished yet. Not checked. |
|
| 1386 | + | pub not_yet: usize, |
|
| 1372 | 1387 | } |
|
| 1373 | 1388 | ||
| 1374 | 1389 | impl Repo { |
|
| … | 15 unchanged lines |
||
| 1390 | 1405 | branch: &str, |
|
| 1391 | 1406 | last: Option<usize>, |
|
| 1392 | 1407 | ) -> Result<GitVerifyReport> { |
|
| 1393 | - | let commits = list_commits(git_dir, branch)?; |
|
| 1408 | + | let mut rep = GitVerifyReport::default(); |
|
| 1409 | + | let mut commits = list_commits(git_dir, branch)?; |
|
| 1394 | 1410 | let imported = self.imported_commits()?; |
|
| 1411 | + | // An import still going (or stopped) has reached part of the |
|
| 1412 | + | // branch: what follows its last imported commit is not checked, |
|
| 1413 | + | // only counted, and --last counts back from there (oxygen#81). |
|
| 1414 | + | if let Some(end) = commits.iter().rposition(|c| imported.contains_key(&c.sha)) { |
|
| 1415 | + | let rest = commits.len() - end - 1; |
|
| 1416 | + | if rest > 0 { |
|
| 1417 | + | commits.truncate(end + 1); |
|
| 1418 | + | } |
|
| 1419 | + | rep.not_yet = rest; |
|
| 1420 | + | } |
|
| 1395 | 1421 | let mut cat = CatFile::start(git_dir)?; |
|
| 1396 | - | let mut rep = GitVerifyReport::default(); |
|
| 1397 | 1422 | // Changes of each commit and all its ancestors, built up in topological order. |
|
| 1398 | 1423 | let mut closure: HashMap<String, Arc<BTreeSet<ChangeHash>>> = HashMap::new(); |
|
| 1399 | 1424 | // Graphs (by key) and blobs already found equal to a Git blob. |
|
| … | 207 unchanged lines |
modified src/cli/sync.rs +10 −0
| … | 513 unchanged lines |
||
| 514 | 514 | for (_, what) in &r.mismatches { |
|
| 515 | 515 | println!("{} {what}", ui::err(ui::glyph("✖", "x"))); |
|
| 516 | 516 | } |
|
| 517 | + | if r.not_yet > 0 { |
|
| 518 | + | println!( |
|
| 519 | + | " {} {}", |
|
| 520 | + | ui::note(), |
|
| 521 | + | ui::dim(format!( |
|
| 522 | + | "{} not imported yet, after the last imported commit: not checked", |
|
| 523 | + | plural(r.not_yet, "commit") |
|
| 524 | + | )) |
|
| 525 | + | ); |
|
| 526 | + | } |
|
| 517 | 527 | if r.mismatches.is_empty() { |
|
| 518 | 528 | println!( |
|
| 519 | 529 | "{}", |
|
| … | 243 unchanged lines |