ontoref/reflection/modules/vcs.nu
Jesús Pérez b00790f67b
Some checks failed
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
chore: fix nu errors
2026-04-08 00:11:29 +01:00

116 lines
4.2 KiB
Text

# VCS abstraction — detects jj or git from filesystem, exposes uniform operations.
#
# All ontoref modules should call these functions instead of hardcoding `git`
# commands. Detection is filesystem-based (no config, no env var): checks for
# `.jj/` (jj) and `.git/` (git) in ONTOREF_PROJECT_ROOT or ONTOREF_ROOT.
use ./env.nu
# Private: resolve project root following the same convention as other modules.
def project-root []: nothing -> string {
let pr = ($env.ONTOREF_PROJECT_ROOT? | default "")
if ($pr | is-not-empty) and ($pr != $env.ONTOREF_ROOT) { $pr } else { $env.ONTOREF_ROOT }
}
# Detect which VCS is active in the project root.
# Returns: "jj" | "git" | "none"
export def detect []: nothing -> string {
let root = (project-root)
let has_jj = ($root | path join ".jj" | path exists)
let has_git = ($root | path join ".git" | path exists)
if $has_jj { "jj" } else if $has_git { "git" } else { "none" }
}
# True when any VCS is active.
export def "is-repo" []: nothing -> bool {
(detect) != "none"
}
# Get file content at last-committed state.
#
# jj model: the working copy IS a commit (@). Reading from @- gives the parent
# commit — i.e., the state before any current working-copy edits.
# `jj file show` requires jj >= 0.40.
export def "show-committed" [file_path: string]: nothing -> string {
let root = (project-root)
match (detect) {
"jj" => {
let rel = ($file_path | path relative-to $root)
do { ^jj --no-pager --repository $root file show $rel -r "@-" } | complete
| if $in.exit_code == 0 { $in.stdout } else { "" }
},
"git" => {
let rel = ($file_path | path relative-to $root)
do { ^git -C $root show $"HEAD:($rel)" } | complete
| if $in.exit_code == 0 { $in.stdout } else { "" }
},
_ => { "" }
}
}
# Revert a file to its last-committed state.
#
# jj: `jj restore --from @-` undoes working-copy edits for the given file.
# git: `git checkout -- <file>` restores from HEAD.
export def "restore-file" [file_path: string]: nothing -> nothing {
let root = (project-root)
match (detect) {
"jj" => {
let rel = ($file_path | path relative-to $root)
do { ^jj --no-pager --repository $root restore --from "@-" -- $rel } | complete | ignore
},
"git" => {
do { ^git -C $root checkout -- $file_path } | complete | ignore
},
_ => {
error make { msg: $"vcs restore-file: no VCS at ($root)" }
}
}
}
# Get the remote origin URL.
export def "remote-url" []: nothing -> string {
let root = (project-root)
match (detect) {
"jj" => {
let r = do { ^jj --no-pager --repository $root git remote list } | complete
if $r.exit_code != 0 or ($r.stdout | str trim | is-empty) { return "" }
# Format: "<name> <url>" per line — extract URL from first remote
$r.stdout | lines | where { |l| ($l | str trim | is-not-empty) }
| first | split row "\t" | last | str trim
},
"git" => {
let r = do { ^git -C $root remote get-url origin } | complete
if $r.exit_code == 0 { $r.stdout | str trim } else { "" }
},
_ => { "" }
}
}
# Get the current branch or bookmark name.
#
# jj: returns the bookmark pointing to @, or "detached" if none.
# git: returns the branch name from rev-parse.
export def "current-ref" []: nothing -> string {
let root = (project-root)
match (detect) {
"jj" => {
let r = do {
^jj --no-pager --repository $root bookmark list --pointing-to "@" -T "name ++ \"\\n\""
} | complete
if $r.exit_code != 0 { return "detached" }
let names = ($r.stdout | lines | where { |l| ($l | str trim | is-not-empty) })
if ($names | is-empty) { "detached" } else { $names | first }
},
"git" => {
let r = do { ^git -C $root rev-parse --abbrev-ref HEAD } | complete
if $r.exit_code == 0 { $r.stdout | str trim } else { "unknown" }
},
_ => { "unknown" }
}
}
# True when the project root has a VCS remote configured.
export def "has-remote" []: nothing -> bool {
(remote-url) | is-not-empty
}