OXYGENHUB

git verify: --max-differences, and what differs by kind 8b7986dd3cb0

Verify stopped after 50 differences, so one carried through history (a
tree conflict an older import left) used them up in the first commits
and hid every later one: on VS Code the 400 commits being checked were
never reached. --max-differences N sets the limit (0 checks every
commit), and the end of the report counts the differences by kind.

refs #80
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-10-09 02:58 UTC
Signature
✔ signed by Andy McCall
Hash
8b7986dd3cb0152e7e433b24701abaea443209e81be3effd7cdcb6457431b5ce
Depends on
62794ecc7f74 79ab8528ff62 7eac74abc586 ee6aa3e81bc8 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.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
16 atoms, 0 tree operations, 3 files

Browse the tree at this change

modified crates/oxy-core/src/repo/git.rs +6 −3
…
1377 unchanged lines
1378 1378
    /// for byte, with the same executable bits, symlink targets and
1379 1379
    /// submodule pins. Reads only; stops listing after 50 differences.
1380 1380
    pub fn verify_git_import(&self, git_dir: &Path, branch: &str) -> Result<GitVerifyReport> {
1381 -
        self.verify_git_import_last(git_dir, branch, None)
1381 +
        self.verify_git_import_last(git_dir, branch, None, 50)
1382 1382
    }
1383 1383
1384 1384
    /// [`Repo::verify_git_import`] of the last `last` commits only (all
1385 1385
    /// when `None`): earlier ones are only followed, not rebuilt, for
1386 1386
    /// looking again at where an import went wrong.
1387 +
    /// It stops after `max` differences (0: never), so one that repeats
1388 +
    /// through history does not hide the commits after it.
1387 1389
    pub fn verify_git_import_last(
1388 1390
        &self,
1389 1391
        git_dir: &Path,
1390 1392
        branch: &str,
1391 1393
        last: Option<usize>,
1394 +
        max: usize,
1392 1395
    ) -> Result<GitVerifyReport> {
1393 1396
        let commits = list_commits(git_dir, branch)?;
1394 1397
        let imported = self.imported_commits()?;
…
62 unchanged lines
1457 1460
            if children.get(c.sha.as_str()).is_some_and(|n| *n > 0) {
1458 1461
                closure.insert(c.sha.clone(), set.clone());
1459 1462
            }
1460 -
            if rep.mismatches.len() >= 50 {
1463 +
            if max > 0 && rep.mismatches.len() >= max {
1461 1464
                break;
1462 1465
            }
1463 1466
            if last.is_some_and(|n| rep.commits + n <= commits.len()) {
…
186 unchanged lines
1650 1653
                    }
1651 1654
                }
1652 1655
            }
1653 -
            if rep.mismatches.len() >= 50 {
1656 +
            if max > 0 && rep.mismatches.len() >= max {
1654 1657
                break;
1655 1658
            }
1656 1659
        }
…
28 unchanged lines
modified src/cli/mod.rs +5 −1
…
809 unchanged lines
810 810
        /// Rebuild only the last N commits (the others are followed, not checked)
811 811
        #[arg(long, value_name = "N")]
812 812
        last: Option<usize>,
813 +
        /// Stop after N differences (0: check every commit whatever it finds)
814 +
        #[arg(long, value_name = "N", default_value_t = 50)]
815 +
        max_differences: usize,
813 816
    },
814 817
    /// Import a Git branch into a new Oxygen repository at DEST
815 818
    Import {
…
338 unchanged lines
1154 1157
                    git_dir,
1155 1158
                    branch,
1156 1159
                    last,
1160 +
                    max_differences,
1157 1161
                },
1158 -
        } => sync::git_verify(&git_dir, &branch, last),
1162 +
        } => sync::git_verify(&git_dir, &branch, last, max_differences),
1159 1163
        Cmd::Policy { cmd } => match cmd {
1160 1164
            PolicyCmd::Init => identity::policy_init(),
1161 1165
            PolicyCmd::Show { json } => identity::policy_show(json),
…
67 unchanged lines
modified src/cli/sync.rs +26 −4
…
501 unchanged lines
502 502
}
503 503
504 504
/// `oxy git verify`: exit 1 if any commit's tree is not reproduced.
505 -
pub fn git_verify(git_dir: &std::path::Path, branch: &str, last: Option<usize>) -> Result<i32> {
505 +
pub fn git_verify(
506 +
    git_dir: &std::path::Path,
507 +
    branch: &str,
508 +
    last: Option<usize>,
509 +
    max: usize,
510 +
) -> Result<i32> {
506 511
    let repo = open()?;
507 512
    let started = std::time::Instant::now();
508 513
    let sp = ui::spinner(&format!(
509 514
        "Rebuilding every commit of {} {branch}…",
510 515
        git_dir.display()
511 516
    ));
512 -
    let r = repo.verify_git_import_last(git_dir, branch, last)?;
517 +
    let r = repo.verify_git_import_last(git_dir, branch, last, max)?;
513 518
    sp.clear();
514 519
    for (_, what) in &r.mismatches {
515 520
        println!("{} {what}", ui::err(ui::glyph("✖", "x")));
…
20 unchanged lines
536 541
        );
537 542
        Ok(0)
538 543
    } else {
544 +
        // What differs, by kind, the commit taken off: one difference
545 +
        // carried through history reads as one line.
546 +
        let mut kinds: std::collections::BTreeMap<String, usize> = Default::default();
547 +
        for (_, what) in &r.mismatches {
548 +
            let kind = what.split_once(": ").map_or(what.as_str(), |(_, k)| k);
549 +
            *kinds.entry(kind.to_string()).or_default() += 1;
550 +
        }
551 +
        let mut kinds: Vec<_> = kinds.into_iter().collect();
552 +
        kinds.sort_by_key(|k| std::cmp::Reverse(k.1));
553 +
        for (kind, n) in kinds.iter().take(10) {
554 +
            println!("  {} {}", ui::dim(format!("{n:>6} ×")), kind);
555 +
        }
539 556
        println!(
540 -
            "{} differences in {} commits checked",
557 +
            "{} differences in {} commits checked{}",
541 558
            r.mismatches.len(),
542 -
            r.commits
559 +
            r.commits,
560 +
            if max > 0 && r.mismatches.len() >= max {
561 +
                format!(" (stopped at {max}; --max-differences 0 checks every commit)")
562 +
            } else {
563 +
                String::new()
564 +
            }
543 565
        );
544 566
        Ok(1)
545 567
    }
…
217 unchanged lines