OXYGENHUB

cat: say -r CHANGE reads the change's dependency closure b0a0c9afae2d

oxy cat -r CHANGE reads the change and what it depends on, not the
bookmark it is on, so a file only later changes touched is absent or
older. --help says so, and a file missing there but on the bookmark now
gets an error naming the closure and suggesting -r BOOKMARK or --at-op.

closes #76
Author
Andy McCall @andymccall <[email protected]>
When
· 2026-10-05 18:24 UTC
Signature
✔ signed by Andy McCall
Hash
b0a0c9afae2d5c2af470c2ce8a932d5e7438b2416e7bcc929214d0c873f17938
Depends on
c6aa5bd9d1c8 ee06a6c557b7 fce44ae9ad29
Depended on by
nothing yet
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
6 atoms, 0 tree operations, 3 files

Browse the tree at this change

modified docs/guide.md +4 −2
…
411 unchanged lines
412 412
`diff`, `hunks`, `blame`, `resolve`, `op log`, `bookmark list`, `remote list`,
413 413
`fetch`, `pull`, `push`, `submodule status`, `bisect status`, `verify`,
414 414
`policy show`, `root`, `version`). `oxy cat PATH -r CHANGE` prints a file as
415 -
it was, `oxy discard PATH..` restores files to the base, and `oxy hunks`
416 -
numbers the working copy's hunks for `commit --hunks` and `discard --hunks`.
415 +
it was in that change's dependency closure (the change and what it depends
416 +
on; `-r BOOKMARK` or a tag reads the whole state), `oxy discard PATH..`
417 +
restores files to the base, and `oxy hunks` numbers the working copy's hunks
418 +
for `commit --hunks` and `discard --hunks`.
417 419
418 420
## 18. If something looks wrong
419 421
…
10 unchanged lines
modified src/cli/mod.rs +3 −1
…
130 unchanged lines
131 131
    /// Print a file as it is in a state (default: the working copy's base)
132 132
    Cat {
133 133
        path: String,
134 -
        /// A change, bookmark, or tag
134 +
        /// A bookmark or tag (its state), or a change: a change means its
135 +
        /// dependency closure, the change and what it depends on, so files
136 +
        /// only later changes touched are absent or at older versions
135 137
        #[arg(short = 'r', long)]
136 138
        change: Option<String>,
137 139
    },
…
547 unchanged lines
modified src/cli/workspace.rs +21 −1
…
415 unchanged lines
416 416
pub fn cat(path: &str, change: Option<&str>) -> Result<i32> {
417 417
    let repo = open()?;
418 418
    let heads = change.map(|c| repo.resolve_heads(c)).transpose()?;
419 -
    let bytes = repo.file_at(path, heads.as_deref())?;
419 +
    let bytes = match repo.file_at(path, heads.as_deref()) {
420 +
        Ok(b) => b,
421 +
        // -r CHANGE reads that change's dependency closure, not the
422 +
        // bookmark it is on: say so when the bookmark has the file
423 +
        // (oxygen#76).
424 +
        Err(e) => match change {
425 +
            Some(c)
426 +
                if !repo.view()?.bookmarks.contains_key(c)
427 +
                    && repo.tag_heads(c)?.is_none()
428 +
                    && repo.file_at(path, None).is_ok() =>
429 +
            {
430 +
                anyhow::bail!(
431 +
                    "{path} is not in {c}'s dependency closure (the change and what it \
432 +
                     depends on); it is on the bookmark: try -r {}, or --at-op OP for the \
433 +
                     state at an operation",
434 +
                    repo.config.bookmark
435 +
                )
436 +
            }
437 +
            _ => return Err(e.into()),
438 +
        },
439 +
    };
420 440
    use std::io::Write as _;
421 441
    std::io::stdout().write_all(&bytes)?;
422 442
    Ok(0)
…
549 unchanged lines