OXYGENHUB

oxy cat builds only the file it prints 19303b4a06fb

file_at rendered every file of the state to return one. It now loads
the state's history and tree, builds that one file's graph and renders
it; the working copy's base, and a state named by its heads (-r main on
main), come from the workspace's state index.

VS Code at 19,978 commits: cat package.json -r main 26 s to 0.10 s, at
a change from 2016 0.66 s to 0.16 s, the base 0.10 s; output unchanged
for text and binary files. The full 166,000-change import: -r main
1.9 s and 1.3 GB, where it ran for over 9 minutes at 9.3 GB.

closes #92
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-10-09 21:49 UTC
Signature
✔ signed by Andy McCall
Hash
19303b4a06fbf4b02fded411fc361ffe549c29b7ef535092bc45ce77d4056456
Depends on
170bb436e1d5 75167672ebcd 8fd78eb52ad2 982864068a59 c6aa5bd9d1c8
Depended on by
c2e1d09047db 7505194116e7
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.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
4 atoms, 0 tree operations, 2 files

Browse the tree at this change

modified CHANGELOG.md +2 −0
…
1 unchanged lines
2 2
3 3
## Unreleased
4 4
5 +
- **Reading one file reads one file (#92).** `oxy cat` rendered every file of the state to print one. It now builds only that file, from the workspace's state index when it reads the working copy's base or the state it is on. On VS Code at 20 000 commits, `cat package.json -r main` went from 26 s to 0.10 s and at a change from 2016 from 0.66 s to 0.16 s; on the full 166 000-change history it takes 1.9 s and 1.3 GB, where it ran for minutes and used 9 GB.
6 +
5 7
- **A file's history in a fraction of a second (#91).** `oxy log --path` rendered every file of the tree twice just to find the path, and both it and the `touches()` revset decoded every change of the history, its text included, to see what each one touched. They now read the path from the tree alone and keep the nodes each change touches in a cache beside the store (`.oxy/index/touched`, rebuilt if missing). On VS Code at 20 000 commits, `log --path package.json` went from 53.6 s to 0.16 s (Git: 0.15 s) and `touches("package.json")` from 3.4 s to 0.16 s, with the same output.
6 8
7 9
## 0.4.2 (2026-10-09)
…
146 unchanged lines
modified crates/oxy-core/src/repo/snapshot.rs +26 −6
…
1408 unchanged lines
1409 1409
impl Repo {
1410 1410
    /// The content of `path` in a state: `heads`, or the working copy's base.
1411 1411
    pub fn file_at(&self, path: &str, heads: Option<&[ChangeHash]>) -> Result<Vec<u8>> {
1412 -
        let snapshot = match heads {
1413 -
            Some(h) => self.load_state(h)?.materialize(),
1414 -
            None => {
1415 -
                let (_, wc) = self.working_copy()?;
1416 -
                self.load_state(&wc.deps)?.materialize()
1417 -
            }
1412 +
        // The state's history and tree, then the one file's graph: nothing
1413 +
        // else is built or rendered (oxygen#92).
1414 +
        // The working copy's base, and a state named by its heads (`-r
1415 +
        // main` on main), come from the workspace's state index.
1416 +
        let base = if self.is_bare() {
1417 +
            None
1418 +
        } else {
1419 +
            Some(self.working_copy()?.1.deps)
1420 +
        };
1421 +
        let same = |a: &[ChangeHash], b: &[ChangeHash]| {
1422 +
            let (mut a, mut b) = (a.to_vec(), b.to_vec());
1423 +
            a.sort();
1424 +
            b.sort();
1425 +
            a == b
1426 +
        };
1427 +
        let mut state = match (heads, base) {
1428 +
            (Some(h), Some(b)) if same(h, &b) => self.load_state_indexed(&b)?,
1429 +
            (Some(h), _) => self.load_state_history(h)?,
1430 +
            (None, Some(b)) => self.load_state_indexed(&b)?,
1431 +
            (None, None) => return fail("this is a bare repository: it has no working copy"),
1432 +
        };
1433 +
        let view = state.tree().view();
1434 +
        let Some(node) = view.entries.get(path).map(|e| e.node) else {
1435 +
            return fail(format!("{path} is not in that state"));
1418 1436
        };
1437 +
        self.load_graphs_of(&mut state, &[node])?;
1438 +
        let snapshot = state.materialize_paths(view, [path]);
1419 1439
        let Some(f) = snapshot.files.get(path) else {
1420 1440
            return fail(format!("{path} is not in that state"));
1421 1441
        };
…
1581 unchanged lines