OXYGENHUB

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