OXYGENHUB

Release 0.4.1 579afd107b9b

The prompt with subrepos, catching up a stale bookmark, bookmark set
fast-forward, bookmark show, clearer submodule and cat messages, a Git
import that checkpoints and can be recovered, and git verify in bounded
memory. Merges built from the paths they can change are opt-in
(OXY_IMPORT_PARTIAL_MERGES=1) until verified at VS Code's depth.

closes #84
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-10-08 15:50 UTC
Signature
✔ signed by Andy McCall
Hash
579afd107b9b8616ea2157b86efa07058c2be4feab6a5df6b00ddccbfc8796bf
Depends on
58dfe0e134eb 7026bd7dc5b8 750251b21a96 79ab8528ff62 810691ca6ce6 8d80356371c5 8fd78eb52ad2 c07406bcc237 c3069537d7c5 c71a5777509a ce118287d9da ff0af1ee299d
Depended on by
d0bf2da15094 d256e826ac5d
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.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
14 atoms, 0 tree operations, 4 files

Browse the tree at this change

modified CHANGELOG.md +17 −1
1 1
# Changelog
2 2
3 -
## Unreleased
3 +
## 0.4.1 (2026-10-08)
4 4
5 +
- **The prompt agrees with status in a repository with subrepos (#75).** Straight after a commit or a merge, `oxy prompt` took a subrepo's directory for a recently changed file and showed `*` until a full `oxy status` ran. Subrepos are now compared by their pins.
6 +
7 +
- **Catching up a stale bookmark (#74).** `oxy bookmark switch` brings a bookmark that is only behind its remote up to it first, rather than checking out the stale state. `oxy pull` takes uncommitted work along when the pulled changes leave its files alone, instead of leaving the working copy behind, and `checkout --carry` no longer counts a subrepo already at the target's pin as a change.
8 +
9 +
- **`bookmark set` fast-forwards (#78).** Moving a bookmark onto work built on its newest head keeps the older changes it held that the new one doesn't depend on, as `--upto` does, and says so. A head the bookmark already holds is still refused without `--upto` or `--only`.
10 +
11 +
- **A bookmark's heads by name (#73).** `oxy bookmark show [NAME]` lists each head, newest first; `bookmark list -v` does it for every bookmark, and `bookmark list` names the newest head's subject. `bookmark(NAME)` in a revset is a local or remote bookmark's heads and nothing else.
12 +
13 +
- **Clearer messages.** `submodule status` and `update` name `.oxymodules` entries that were never added, with the command that pins them, and `update` exits 1 (#72). `oxy cat -r CHANGE`'s help and error say that a change means its dependency closure, and suggest `-r BOOKMARK` or `--at-op` (#76).
14 +
15 +
- **A long Git import can stop and carry on (#77, #79).** An import moves its bookmark to the commits done so far every 2 000 commits, so `oxy git fetch` resumes it. `oxy git recover` rescues an import that stopped before its first checkpoint, or ran on an older oxy: it indexes the objects already written (each checked, a torn last one cut off) and points the bookmark at them. It recovered 139 086 commits of VS Code in under two minutes. `oxy git fetch ../path` with a relative path works.
16 +
17 +
- **`oxy git verify` in bounded memory (#81).** It held the ancestry of every commit and ran out of memory on VS Code; it now holds only the commits whose children are still to come, and checks 120 000 commits in under 1 GB. On an import not finished yet it stops at the last imported commit, counts `--last` from there, and says how many are still to come.
18 +
19 +
- **Measuring an import (#80).** `OXY_IMPORT_STOP_AFTER=N` ends an import cleanly after N commits with its timings; the trace gives each commit's time and which path each merge took. Merges built from only the paths they can change are in, behind `OXY_IMPORT_PARTIAL_MERGES=1` until verified at depth: on VS Code a small merge goes from about 18 s to under a second. An import stopping early no longer dies of SIGPIPE.
20 +
5 21
- **Cloud stores take credentials handed over (#68).** `oxy_strata::Credentials` (an S3 access key, a Google service account key, an Azure service principal) can be given when a store is opened, and a program can register a lookup for the stores it opens by URL. With them, nothing from the environment or the providers' files is used, which lets a hub keep each repository's credentials itself. Their debug form never shows a secret.
6 22
7 23
- **A repository kept on a store (#68).** `oxy init --store URL` keeps a repository's objects and operation log on `dir://`, S3, GCS or Azure, behind a local cache of `storage.cache` bytes (2 GiB by default) that can be deleted at any time. Checked against the real services: a commit takes 0.5 to 0.9 seconds, and reopening with no cache about half a second.
…
112 unchanged lines
modified Cargo.lock +4 −4
…
1469 unchanged lines
1470 1470
1471 1471
[[package]]
1472 1472
name = "oxy"
1473 -
version = "0.4.0"
1473 +
version = "0.4.1"
1474 1474
dependencies = [
1475 1475
 "anyhow",
1476 1476
 "clap",
…
11 unchanged lines
1488 1488
1489 1489
[[package]]
1490 1490
name = "oxy-core"
1491 -
version = "0.4.0"
1491 +
version = "0.4.1"
1492 1492
dependencies = [
1493 1493
 "blake3",
1494 1494
 "ed25519-dalek",
…
17 unchanged lines
1512 1512
1513 1513
[[package]]
1514 1514
name = "oxy-server"
1515 -
version = "0.4.0"
1515 +
version = "0.4.1"
1516 1516
dependencies = [
1517 1517
 "axum",
1518 1518
 "axum-server",
…
6 unchanged lines
1525 1525
1526 1526
[[package]]
1527 1527
name = "oxy-strata"
1528 -
version = "0.4.0"
1528 +
version = "0.4.1"
1529 1529
dependencies = [
1530 1530
 "async-trait",
1531 1531
 "bytes",
…
1419 unchanged lines
modified Cargo.toml +1 −1
…
2 unchanged lines
3 3
resolver = "2"
4 4
5 5
[workspace.package]
6 -
version = "0.4.0"
6 +
version = "0.4.1"
7 7
edition = "2021"
8 8
license = "MIT OR Apache-2.0"
9 9
…
37 unchanged lines
modified crates/oxy-core/src/repo/git.rs +8 −4
…
937 unchanged lines
938 938
        let stop_after: Option<usize> = std::env::var("OXY_IMPORT_STOP_AFTER")
939 939
            .ok()
940 940
            .and_then(|v| v.parse().ok());
941 -
        let whole_merges = std::env::var_os("OXY_IMPORT_WHOLE_MERGES").filter(|v| v != "0");
941 +
        // Merges built from the paths they can change (oxygen#80) only with
942 +
        // OXY_IMPORT_PARTIAL_MERGES=1 until verified at VS Code's depth;
943 +
        // otherwise from the whole tree, as before.
944 +
        let partial_merges =
945 +
            std::env::var_os("OXY_IMPORT_PARTIAL_MERGES").is_some_and(|v| v != "0");
942 946
        let total = commits.len();
943 947
        for (index, gc) in commits.iter().enumerate() {
944 948
            if known.imported.contains_key(&gc.sha) || known.exported.contains_key(&gc.sha) {
…
69 unchanged lines
1014 1018
            let mut first_parent_view: Option<crate::tree::TreeView> = None;
1015 1019
            if gc.parents.len() > 1 {
1016 1020
                report.merges += 1;
1017 -
                if prev_tree.is_some() && whole_merges.is_none() {
1021 +
                if prev_tree.is_some() && partial_merges {
1018 1022
                    first_parent_view = Some(base.tree().view());
1019 1023
                }
1020 1024
                let mut args: Vec<String> = gc.parents[1..].to_vec();
…
65 unchanged lines
1086 1090
            // paths in the first parent's tree and in the merged state. With
1087 1091
            // them added it is built like an ordinary commit, instead of
1088 1092
            // from its whole tree (18 s for any merge at 118k commits of
1089 -
            // VS Code, oxygen#80). OXY_IMPORT_WHOLE_MERGES=1 restores the
1090 -
            // whole-tree path, for comparing.
1093 +
            // VS Code, oxygen#80). For now only with
1094 +
            // OXY_IMPORT_PARTIAL_MERGES=1; otherwise the whole-tree path.
1091 1095
            let mut changed = changed;
1092 1096
            // A tree conflict in the merged state (both sides placed a file
1093 1097
            // at one path) is resolved by the whole-tree path.
…
918 unchanged lines