OXYGENHUB

Load history only where no file graph is needed 57eaac9107df

Bookmark create, set and merge, pull, tags, identities, bisect, doctor
and most history rewrites loaded every file graph of a state only to
ask which changes it holds, its heads or its tree. They now load its
history alone, which on the Linux tree is the difference between a
fraction of a second and running out of memory. A history-only state
panics rather than answer a graph it has not built, so a caller that
does need graphs cannot quietly get a wrong one.

refs #35
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-09-29 22:08 UTC
Signature
✔ signed by Andy McCall
Hash
57eaac9107df33c2461b70d04bf3333a8607fda241e779719978c704cede25d6
Depends on
05e71ed6cdf1 132b884b9a8d 23b217d75848 3e5772ccf9da 614541c3cebd 728da0f92161 75167672ebcd 7b9809e67c3d 957fee30826e 982864068a59 b837d2b55853 c07e0e7d2d85 c863c7a7a2d1
Depended on by
c2e1d09047db 14edbaaf5291
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.1.0 v0.10.0 v0.11.0 v0.2.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
29 atoms, 0 tree operations, 7 files

Browse the tree at this change

modified crates/oxy-core/src/repo/bookmarks.rs +4 −4
…
84 unchanged lines
85 85
            return fail(format!("bookmark {name} already exists"));
86 86
        }
87 87
        let heads = match heads {
88 -
            Some(h) => self.load_state(h)?.maximal(h),
88 +
            Some(h) => self.load_state_history(h)?.maximal(h),
89 89
            None => {
90 90
                let (_, wc) = self.working_copy()?;
91 -
                self.load_state(&wc.deps)?.maximal(&wc.deps)
91 +
                self.load_state_history(&wc.deps)?.maximal(&wc.deps)
92 92
            }
93 93
        };
94 94
        let mut view = op.view.clone();
…
13 unchanged lines
108 108
                "no bookmark {name}; create it with `oxy bookmark create`"
109 109
            ));
110 110
        }
111 -
        let heads = self.load_state(heads)?.maximal(heads);
111 +
        let heads = self.load_state_history(heads)?.maximal(heads);
112 112
        let mut view = op.view.clone();
113 113
        view.bookmarks.insert(
114 114
            name.to_string(),
…
79 unchanged lines
194 194
        let local = self.heads_of(&op.view, &current)?;
195 195
        let mut all = local.clone();
196 196
        all.extend(other);
197 -
        let mut heads = self.load_state(&all)?.maximal(&all);
197 +
        let mut heads = self.load_state_history(&all)?.maximal(&all);
198 198
        heads.sort();
199 199
        let mut sorted_local = local.clone();
200 200
        sorted_local.sort();
…
29 unchanged lines
modified crates/oxy-core/src/repo/doctor.rs +1 −1
…
189 unchanged lines
190 190
        let Ok(heads) = self.bookmark_heads() else {
191 191
            return;
192 192
        };
193 -
        let Ok(state) = self.load_state(&heads) else {
193 +
        let Ok(state) = self.load_state_history(&heads) else {
194 194
            return;
195 195
        };
196 196
        let view = state.tree().view();
…
128 unchanged lines
modified crates/oxy-core/src/repo/history.rs +11 −11
…
51 unchanged lines
52 52
            out
53 53
        };
54 54
        let candidates = replace(&heads);
55 -
        let st = self.load_state(&candidates)?;
55 +
        let st = self.load_state_history(&candidates)?;
56 56
        let new_heads = st.maximal(&candidates);
57 57
58 58
        let (_, wc) = self.working_copy()?;
59 -
        let base = self.load_state(&wc.deps)?;
59 +
        let base = self.load_state_history(&wc.deps)?;
60 60
        let wc_base = base.maximal(&wc.deps);
61 61
        let wc_base = if wc_base.contains(old) {
62 62
            let c = replace(&wc_base);
63 -
            let st = self.load_state(&c)?;
63 +
            let st = self.load_state_history(&c)?;
64 64
            st.maximal(&c)
65 65
        } else {
66 66
            wc_base
…
40 unchanged lines
107 107
        let (wc_hash, _) = self.working_copy()?;
108 108
        let mut all = heads.clone();
109 109
        all.push(wc_hash);
110 -
        let state = self.load_state(&all)?;
110 +
        let state = self.load_state_history(&all)?;
111 111
        if !state.closure(&wc_hash).contains(old)
112 112
            && !heads.contains(old)
113 113
            && !state.heads().contains(old)
…
43 unchanged lines
157 157
            out
158 158
        };
159 159
        let candidates = remap(&heads);
160 -
        let st = self.load_state(&candidates)?;
160 +
        let st = self.load_state_history(&candidates)?;
161 161
        let new_heads = st.maximal(&candidates);
162 162
        let new_wc = mapping.get(&wc_hash).copied().unwrap_or(wc_hash);
163 163
…
171 unchanged lines
335 335
            return fail("that is the working copy; commit it instead");
336 336
        }
337 337
        // Every reference must live in the target's closure (or be the target).
338 -
        let closure = self.load_state(&[*target])?;
338 +
        let closure = self.load_state_history(&[*target])?;
339 339
        let ok_change = |h: &ChangeHash| *h == *target || closure.contains(h);
340 340
        let ok_node = |n: &NodeId| n.is_root() || closure.tree().introducer(n).is_some();
341 341
        let too_new = |what: &str| {
…
325 unchanged lines
667 667
        let bookmark = self.config.bookmark.clone();
668 668
        let mut heads = self.heads_of(&op.view, &bookmark)?;
669 669
        heads.push(h);
670 -
        let st = self.load_state(&heads)?;
670 +
        let st = self.load_state_history(&heads)?;
671 671
        let heads = st.maximal(&heads);
672 672
        let mut view = op.view.clone();
673 673
        view.bookmarks.insert(
…
3 unchanged lines
677 677
        let (_, wc) = self.working_copy()?;
678 678
        let mut base = wc.deps.clone();
679 679
        base.push(h);
680 -
        let wc_base = self.load_state(&base)?.maximal(&base);
680 +
        let wc_base = self.load_state_history(&base)?.maximal(&base);
681 681
        let fresh = self.new_working_copy(wc_base, &wc.message);
682 682
        view.workspaces
683 683
            .insert(self.workspace, self.store.put_change(&fresh)?);
…
47 unchanged lines
731 731
        let (wc_hash, _) = self.working_copy()?;
732 732
        let mut all = heads.clone();
733 733
        all.push(wc_hash);
734 -
        let state = self.load_state(&all)?;
734 +
        let state = self.load_state_history(&all)?;
735 735
        for (h, meta) in state.changes() {
736 736
            if !meta.deps.contains(hash) {
737 737
                continue;
…
95 unchanged lines
833 833
        if dst.kind == ChangeKind::WorkingCopy {
834 834
            return fail("cannot squash into the working copy");
835 835
        }
836 -
        let closure = self.load_state(&[*into])?;
836 +
        let closure = self.load_state_history(&[*into])?;
837 837
        let outside: Vec<String> = src
838 838
            .deps
839 839
            .iter()
…
206 unchanged lines
1046 1046
        let (wc_hash, _) = self.working_copy()?;
1047 1047
        let mut all = self.bookmark_heads()?;
1048 1048
        all.push(wc_hash);
1049 -
        let state = self.load_state(&all)?;
1049 +
        let state = self.load_state_history(&all)?;
1050 1050
        let src_now = state
1051 1051
            .changes()
1052 1052
            .find(|(_, m)| m.change_id == src.change_id)
…
143 unchanged lines
modified crates/oxy-core/src/repo/identity.rs +1 −1
…
452 unchanged lines
453 453
            return fail("no identity: run oxy identity create");
454 454
        };
455 455
        let heads = self.bookmark_heads()?;
456 -
        let state = self.load_state(&heads)?;
456 +
        let state = self.load_state_history(&heads)?;
457 457
        let mut out = Vec::new();
458 458
        for (h, meta) in state.changes() {
459 459
            if (meta.author == key || meta.author.is_zero())
…
27 unchanged lines
modified crates/oxy-core/src/repo/inspect.rs +4 −4
…
182 unchanged lines
183 183
            return fail("a bisect is in progress; finish it or run `oxy bisect reset`");
184 184
        }
185 185
        let (_, wc) = self.working_copy()?;
186 -
        let start = self.load_state(&wc.deps)?.maximal(&wc.deps);
186 +
        let start = self.load_state_history(&wc.deps)?.maximal(&wc.deps);
187 187
        self.bisect_save(&BisectState {
188 188
            start,
189 189
            ..Default::default()
…
47 unchanged lines
237 237
    fn bisect_heads(&self, st: &BisectState, k: usize) -> Vec<ChangeHash> {
238 238
        let mut all = st.good.clone();
239 239
        all.extend(st.candidates[..k].iter().copied());
240 -
        self.load_state(&all)
240 +
        self.load_state_history(&all)
241 241
            .map(|s| s.maximal(&all))
242 242
            .unwrap_or(all)
243 243
    }
…
65 unchanged lines
309 309
                    bad: st.bad.len(),
310 310
                });
311 311
            }
312 -
            let good_heads = self.load_state(&st.good)?.maximal(&st.good);
313 -
            let bad_heads = self.load_state(&st.bad)?.maximal(&st.bad);
312 +
            let good_heads = self.load_state_history(&st.good)?.maximal(&st.good);
313 +
            let bad_heads = self.load_state_history(&st.bad)?.maximal(&st.bad);
314 314
            st.good = good_heads.clone();
315 315
            st.bad = bad_heads.clone();
316 316
            st.candidates = self.negotiate(&good_heads, &bad_heads)?;
…
45 unchanged lines
modified crates/oxy-core/src/repo/sync.rs +1 −1
…
1188 unchanged lines
1189 1189
        let remote_heads = self.store.get_state(&remote_state)?.heads;
1190 1190
        let mut all = local.clone();
1191 1191
        all.extend(remote_heads);
1192 -
        let st = self.load_state(&all)?;
1192 +
        let st = self.load_state_history(&all)?;
1193 1193
        let mut heads = st.maximal(&all);
1194 1194
        heads.sort();
1195 1195
        let mut sorted_local = local.clone();
…
307 unchanged lines
modified crates/oxy-core/src/repo/tags.rs +1 −1
…
51 unchanged lines
52 52
        if op.view.tags.contains_key(name) {
53 53
            return fail(format!("tag {name:?} already exists; tags are immutable"));
54 54
        }
55 -
        let st = self.load_state(heads)?;
55 +
        let st = self.load_state_history(heads)?;
56 56
        let heads = st.maximal(heads);
57 57
        if heads.is_empty() {
58 58
            return fail("a tag needs at least one change");
…
183 unchanged lines