OXYGENHUB

gc: tolerate historical views naming objects pruned under an earlier, shorter op-retention 21b2aec279ff

--- crates/oxy-core/src/repo/gc.rs
-                changes.extend(self.store.get_state(&st)?.heads);
-                let policy = crate::policy::Policy::decode(&self.store.get_typed(&p, ObjectType::Policy)?)?;
-            // Beyond the shallow boundary there is nothing to keep.
+        // A historical view may name objects pruned under an earlier, shorter
+        // retention; there is nothing to keep for those.
+                match self.store.get_state(&st) {
+                    Ok(s) => changes.extend(s.heads),
+                    Err(StoreError::NotFound(_)) => continue,
+                    Err(e) => return Err(e.into()),
+                }
+                let bytes = match self.store.get_typed(&p, ObjectType::Policy) {
+                    Ok(b) => b,
+                    Err(StoreError::NotFound(_)) => continue,
+                    Err(e) => return Err(e.into()),
+                };
+                let policy = crate::policy::Policy::decode(&bytes)?;
+            // Beyond the shallow boundary, or pruned under an earlier
+            // retention, there is nothing to keep.
--- crates/oxy-core/src/repo/tests.rs
-    let repo = Repo::open(root).unwrap().with_home(root.join("home"));
+    let mut repo = Repo::open(root).unwrap().with_home(root.join("home"));
+    // Restoring the default retention makes the old views roots again; the
+    // working copies they name were pruned, which gc tolerates.
+    repo.config.set("core.op-retention", "90").unwrap();
+    repo.save_config().unwrap();
+    let repo = Repo::open(root).unwrap().with_home(root.join("home"));
+    let r = repo.gc(false).unwrap();
+    assert_eq!(r.pruned.objects, 0);
+    assert_eq!(repo.log().unwrap().len(), 4);
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-09-26 23:34 UTC
Signature
✔ signed by Andy McCall
Hash
21b2aec279ff41d19c1864f5c6a48f194c64d32ff6b83be6e21ed2e574420f5c
Depends on
2863456a0f6d 982864068a59 ba160f60219e d02e545dc265
Depended on by
6f8aeb996558
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
8 atoms, 0 tree operations, 2 files

Browse the tree at this change

modified crates/oxy-core/src/repo/gc.rs +15 −3
…
67 unchanged lines
68 68
                attests.push(r);
69 69
            }
70 70
        }
71 +
        // A historical view may name objects pruned under an earlier, shorter
72 +
        // retention; there is nothing to keep for those.
71 73
        for st in states {
72 74
            if keep.insert(st) {
73 -
                changes.extend(self.store.get_state(&st)?.heads);
75 +
                match self.store.get_state(&st) {
76 +
                    Ok(s) => changes.extend(s.heads),
77 +
                    Err(StoreError::NotFound(_)) => continue,
78 +
                    Err(e) => return Err(e.into()),
79 +
                }
74 80
            }
75 81
        }
76 82
        for p in policies {
77 83
            if keep.insert(p) {
78 -
                let policy = crate::policy::Policy::decode(&self.store.get_typed(&p, ObjectType::Policy)?)?;
84 +
                let bytes = match self.store.get_typed(&p, ObjectType::Policy) {
85 +
                    Ok(b) => b,
86 +
                    Err(StoreError::NotFound(_)) => continue,
87 +
                    Err(e) => return Err(e.into()),
88 +
                };
89 +
                let policy = crate::policy::Policy::decode(&bytes)?;
79 90
                for id in &policy.identities {
80 91
                    keep.insert(id.hash());
81 92
                }
…
8 unchanged lines
90 101
            if !keep.insert(h) {
91 102
                continue;
92 103
            }
93 -
            // Beyond the shallow boundary there is nothing to keep.
104 +
            // Beyond the shallow boundary, or pruned under an earlier
105 +
            // retention, there is nothing to keep.
94 106
            let c = match self.store.get_change(&h) {
95 107
                Ok(c) => c,
96 108
                Err(StoreError::NotFound(_)) => continue,
…
32 unchanged lines
modified crates/oxy-core/src/repo/tests.rs +9 −1
…
783 unchanged lines
784 784
    // working copies go, everything reachable stays.
785 785
    repo.config.set("core.op-retention", "0").unwrap();
786 786
    repo.save_config().unwrap();
787 -
    let repo = Repo::open(root).unwrap().with_home(root.join("home"));
787 +
    let mut repo = Repo::open(root).unwrap().with_home(root.join("home"));
788 788
    let r = repo.gc(true).unwrap();
789 789
    assert!(r.pruned.objects >= 5, "{r:?}");
790 790
    let after = repo.store().list().unwrap();
…
20 unchanged lines
811 811
    let r = repo.gc(true).unwrap();
812 812
    assert!(r.pruned.objects > 0);
813 813
    assert_eq!(repo.log().unwrap().len(), 4);
814 +
    // Restoring the default retention makes the old views roots again; the
815 +
    // working copies they name were pruned, which gc tolerates.
816 +
    repo.config.set("core.op-retention", "90").unwrap();
817 +
    repo.save_config().unwrap();
818 +
    let repo = Repo::open(root).unwrap().with_home(root.join("home"));
819 +
    let r = repo.gc(false).unwrap();
820 +
    assert_eq!(r.pruned.objects, 0);
821 +
    assert_eq!(repo.log().unwrap().len(), 4);
814 822
}
815 823
816 824
#[test]
…
501 unchanged lines