Skip to content

Instantly share code, notes, and snippets.

@xpe
Last active August 23, 2026 23:42
Show Gist options
  • Select an option

  • Save xpe/6392890431ef678d3c9a8cc08da1324f to your computer and use it in GitHub Desktop.

Select an option

Save xpe/6392890431ef678d3c9a8cc08da1324f to your computer and use it in GitHub Desktop.
Resolving `.jj/repo` to an absolute path

Resolving .jj/repo to an absolute path

Text written by Claude Opus 5. (I tested the scripts on macOS.)

Read the relative path stored in .jj/repo and resolve it against the current working directory.

Base directory: the path in .jj/repo is relative to the .jj directory, not to the CWD. All snippets below join against .jj/ for that reason.


Bash

rel=$(<.jj/repo)
abs=$(realpath ".jj/$rel")        # portable across GNU and BSD
  • $(<file) is a bash builtin read — no cat process. Like $(...), it strips trailing newlines.
  • Version: bash 5.3 (July 2025) is current; nothing here needs past 4.x.

realpath: GNU vs BSD

realpath is not in POSIX and there are two incompatible implementations. The flags are the difference, not the command.

GNU coreutils BSD (macOS, FreeBSD)
ships with Linux distros, brew install coreutils → grealpath macOS base system, *BSD
-m (no component need exist) yes no — errors out
-e (all components must exist) yes no
-s / --no-symlinks (lexical only) yes no
-q (suppress warnings) no yes
-- end-of-options yes not reliably
no flags: missing final component error accepted
no flags: missing intermediate component error error

So bare realpath runs on both, but with different behavior at the edges: on BSD a nonexistent leaf resolves fine; on GNU it fails. If the leaf is expected to exist — as .jj/repo's target normally does — the two agree and bare realpath is the portable choice.

Writing for both:

if realpath -m . >/dev/null 2>&1; then
  abs=$(realpath -m -- ".jj/$rel")   # GNU
else
  abs=$(realpath ".jj/$rel")         # BSD
fi

Or sidestep the question entirely with the POSIX cd/pwd -P form at the bottom of this document, which needs no external realpath at all.

Zsh

abs=".jj/$(<.jj/repo)"
abs=${abs:A}
  • :A — absolute + normalize + resolve symlinks, no existence requirement.
  • :a — same, lexical only (no symlink resolution).
  • :P — full physical resolution (zsh ≥ 5.3).
  • Build the full string before applying the modifier; :a/:A resolve relative paths against $PWD.
  • Version: 5.9.x is the current stable line (5.9.1, May 2026); 5.10 is in test.

Fish

set rel (cat .jj/repo)
set abs (path resolve .jj/$rel)
  • No $(<file) equivalent; cat is idiomatic. Command substitution strips trailing newlines.
  • path resolve (fish ≥ 3.5): absolute + normalize + resolve symlinks; keeps non-existent trailing components.
  • path normalize is lexical-only, but does not make a relative path absolute.
  • If the file had multiple lines, set rel (...) produces a list and .jj/$rel expands to one path per element. set rel (cat .jj/repo | head -n1) avoids that.
  • Version: fish 4.8.1 (July 2026); 4.0 (Feb 2025) was the Rust rewrite.

Nushell

let rel = (open .jj/repo | str trim)
let abs = ('.jj' | path join $rel | path expand)
  • open on an extensionless file returns a string; str trim removes the trailing newline. open --raw if you want to bypass content-type inference.
  • path expand — absolute + normalize + resolve symlinks, relative to CWD.
  • path expand --no-symlink (-n) skips symlink resolution; --strict (-s) errors if the path doesn't exist.
  • Version: 0.114.x (July 2026).

Resolution semantics side by side

lexical only + resolve symlinks requires existence
bash + GNU realpath realpath -sm realpath -m realpath -e
bash + BSD realpath — realpath — ²
zsh ${p:a} ${p:A} —
fish path normalize¹ path resolve —
nushell path expand -n path expand path expand -s

¹ does not absolutize. ² BSD has no existence flags: intermediate components must exist, the final one need not.

POSIX sh fallback

rel=$(cat .jj/repo)
abs=$(cd ".jj/$(dirname "$rel")" && pwd -P)/$(basename "$rel")

cd, pwd -P, dirname, basename are all POSIX; realpath is not (and BSD/macOS realpath lacks -m). This form resolves symlinks and fails if the directory doesn't exist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment