OXYGENHUB

Diff from the status cache: the working-copy change rendered against its indexed base (refs #41) 77445673174b

While the status cache holds, oxy diff and diff --json render the stored
working-copy change against its base loaded from the state index, with no
snapshot; Repo::working_copy_with_base picks that or a snapshot, and
change_base's rendering is shared as render_base_for. On the Linux tree,
one file edited: 0.34 s (from 0.41 s), 250 MB (from 360 MB). A test
checks the cached diff renders exactly as a snapshot's.
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-10-01 19:48 UTC
Signature
✔ signed by Andy McCall
Hash
77445673174be3dcd5b8e13a15d5402d2bb828e549e183533f8eb36cf8d931b4
Depends on
1d360c91a289 3604179679eb 75167672ebcd 86e1824f2d0b 8ab265dc4a51 982864068a59 c6aa5bd9d1c8 fce44ae9ad29
Depended on by
74b4a3c62ea4 dd6147dcf9c2 e226a6cc2f71 10b4fd430eab 1ec3f008e96a
In bookmarks
agents-in-readme 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.3.0 v0.3.1 v0.4.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
9 atoms, 0 tree operations, 5 files

Browse the tree at this change

modified crates/oxy-core/src/repo/mod.rs +11 −1
…
1942 unchanged lines
1943 1943
    /// contents of the files the change edits. Other text files are left
1944 1944
    /// unrendered ([`Snapshot::unrendered`]).
1945 1945
    pub fn change_base(&self, change: &Change) -> Result<(State, Snapshot)> {
1946 -
        let mut base = self.load_state_history(&change.deps)?;
1946 +
        let base = self.load_state_history(&change.deps)?;
1947 +
        self.render_base_for(change, base)
1948 +
    }
1949 +
1950 +
    /// [`Repo::change_base`] with the base already loaded (the working
1951 +
    /// copy's comes from the state index).
1952 +
    pub(crate) fn render_base_for(
1953 +
        &self,
1954 +
        change: &Change,
1955 +
        mut base: State,
1956 +
    ) -> Result<(State, Snapshot)> {
1947 1957
        let edited: std::collections::HashSet<NodeId> =
1948 1958
            change.edits.iter().map(|e| e.node()).collect();
1949 1959
        let nodes: Vec<NodeId> = edited.iter().copied().collect();
…
129 unchanged lines
modified crates/oxy-core/src/repo/snapshot.rs +17 −4
…
1413 unchanged lines
1414 1414
                let (base, snapshot) = self.change_base(&c)?;
1415 1415
                (c, base, snapshot)
1416 1416
            }
1417 -
            None => {
1418 -
                let snap = self.snapshot_status("oxy diff")?;
1419 -
                (snap.wc, snap.base, snap.base_snapshot)
1420 -
            }
1417 +
            None => self.working_copy_with_base()?,
1421 1418
        };
1422 1419
        Ok(diff_files_of(&c, &base, &base_snapshot))
1420 +
    }
1421 +
1422 +
    /// The working-copy change with its base state and the base's
1423 +
    /// rendering, for showing what the working copy changes. While the
1424 +
    /// status cache holds (every file as the last snapshot left it), the
1425 +
    /// stored working-copy change is the answer and no snapshot is taken;
1426 +
    /// otherwise a snapshot makes it.
1427 +
    pub fn working_copy_with_base(&self) -> Result<(Change, State, Snapshot)> {
1428 +
        if self.status_by_manifest()?.is_some() {
1429 +
            let (_, wc) = self.working_copy()?;
1430 +
            let base = self.load_state_indexed(&wc.deps)?;
1431 +
            let (base, snapshot) = self.render_base_for(&wc, base)?;
1432 +
            return Ok((wc, base, snapshot));
1433 +
        }
1434 +
        let snap = self.snapshot_status("oxy diff")?;
1435 +
        Ok((snap.wc, snap.base, snap.base_snapshot))
1423 1436
    }
1424 1437
1425 1438
    /// The log restricted to changes touching `path` (its node, so renames
…
1191 unchanged lines
modified crates/oxy-core/src/repo/tests.rs +26 −0
…
5954 unchanged lines
5955 5955
    assert_eq!(repo.status_by_manifest().unwrap(), Some(vec![]));
5956 5956
}
5957 5957
5958 +
/// oxygen#41: while the status cache holds, the working copy's diff is
5959 +
/// rendered from the stored working-copy change, and says exactly what a
5960 +
/// snapshot's diff says.
5961 +
#[test]
5962 +
fn the_working_copy_diff_from_the_cache_matches_a_snapshot() {
5963 +
    hermetic();
5964 +
    let dir = tempfile::tempdir().unwrap();
5965 +
    let root = dir.path();
5966 +
    let repo = Repo::init(root).unwrap();
5967 +
    write(root, "a.txt", b"one\ntwo\nthree\n");
5968 +
    write(root, "gone.txt", b"bye\n");
5969 +
    repo.commit("base", &[]).unwrap();
5970 +
    write(root, "a.txt", b"one\n2\nthree\nfour\n");
5971 +
    write(root, "new.txt", b"hello\n");
5972 +
    fs::remove_file(root.join("gone.txt")).unwrap();
5973 +
    let snap = repo.snapshot_status("oxy status").unwrap();
5974 +
    let slow = render_change(&snap.wc, &snap.base, &snap.base_snapshot);
5975 +
    assert!(
5976 +
        repo.status_by_manifest().unwrap().is_some(),
5977 +
        "the cache holds"
5978 +
    );
5979 +
    let (wc, base, snapshot) = repo.working_copy_with_base().unwrap();
5980 +
    assert_eq!(render_change(&wc, &base, &snapshot), slow);
5981 +
    assert_eq!(repo.diff_files(None).unwrap().len(), 3);
5982 +
}
5983 +
5958 5984
/// oxygen#35: a history-only state keeps no inserted text. It reads a
5959 5985
/// stored insert's text blob only when it builds that file's graph, and
5960 5986
/// builds the same graph a full load does.
…
708 unchanged lines
modified docs/benchmarks.md +9 −0
…
769 unchanged lines
770 770
A later status that finds every file as the manifest says answers from
771 771
the cache. Test builds check that answer against a snapshot. Diff still
772 772
renders the base; it is next.
773 +
774 +
Then diff (same day, load average 4.2, Git 0.10 s throughout): while the
775 +
status cache holds, `oxy diff` renders the stored working-copy change
776 +
against its base loaded from the state index, rather than snapshotting.
777 +
0.34 to 0.35 s and 250 MB (from 0.41 s and 360 MB). Rendering it through
778 +
`change_base`, which reads the base's changes rather than the index, was
779 +
slower than a snapshot (0.9 s) and was not kept. What remains is the
780 +
status check's listing (about 130 ms), the index (60 ms) and rendering.
781 +
modified src/cli/workspace.rs +2 −5
…
883 unchanged lines
884 884
            print!("{}", render_change(&c, &base, &snapshot));
885 885
        }
886 886
        None => {
887 -
            let snap = repo.snapshot_status("oxy diff")?;
888 -
            print!(
889 -
                "{}",
890 -
                render_change(&snap.wc, &snap.base, &snap.base_snapshot)
891 -
            );
887 +
            let (wc, base, snapshot) = repo.working_copy_with_base()?;
888 +
            print!("{}", render_change(&wc, &base, &snapshot));
892 889
        }
893 890
    }
894 891
    Ok(0)
…
1472 unchanged lines