Skip to content

Instantly share code, notes, and snippets.

@the-solipsist
Last active March 3, 2025 18:19
Show Gist options
  • Save the-solipsist/38fa498c0cdc9c858ed104cbb10a0338 to your computer and use it in GitHub Desktop.
Save the-solipsist/38fa498c0cdc9c858ed104cbb10a0338 to your computer and use it in GitHub Desktop.
hledger-jj converted from ysh to bash by Gemini 2.0
#!/bin/bash
# -*- sh -*-
# Improved version of hledger-jj bash script, created by Gemini 2.0
# Original version of hledger-jj ysh script, created by Simon Michael
# --- Error Handling and Setup ---
if [[ -z "$LEDGER_FILE" ]]; then
echo "Error: LEDGER_FILE environment variable is not set." >&2
exit 1
fi
FILE1="${LEDGER_FILE}"
DIR="$(dirname "$FILE1")"
if [[ ! -d "$DIR" ]]; then
echo "Error: Directory '$DIR' (derived from LEDGER_FILE) does not exist or is not a directory." >&2
exit 1
fi
# Check if hledger is installed
if ! command -v hledger >/dev/null 2>&1; then
echo "Error: hledger is not installed or not in PATH." >&2
exit 1
fi
# Check if jj is installed
if ! command -v jj >/dev/null 2>&1; then
echo "Error: jj (Jujutsu) is not installed or not in PATH." >&2
echo "Please install it: https://jj-vcs.github.io" >&2
exit 1
fi
# Get journal files and handle potential hledger errors
FILES_STR=$(hledger -f "$FILE1" files 2>/dev/null) # Capture stderr to discard hledger warnings
if [[ -z "$FILES_STR" ]]; then
echo "Error: hledger files command failed or returned no files. Check your LEDGER_FILE." >&2
exit 1
fi
IFS=$'\n' read -r -d '' -a FILES <<< "$FILES_STR"
unset IFS
HELP=$(cat <<'EOF'
-------------------------------------------------------------------------------
hledger-jj [COMMAND [OPTS]] - easy version control for hledger journals
An easy CLI for keeping your data in version control, using jj and a git repo.
Works for \$LEDGER_FILE and its subfiles only (-f is not yet supported).
A repo will be created if needed, in \$LEDGER_FILE's directory.
You can run this tool from any directory. Commands may be abbreviated.
Options are passed to jj; you may need to write -- first.
Commands:
help Show help message
status [OPTS] Show status of journal files
diff [OPTS] Show unrecorded changes in journal files
commit [MSG [OPTS]] Record changes to journal files
log [OPTS] List recorded changes to journal files
Examples:
hledger jj status
hledger jj diff
hledger jj commit "Added rent transaction"
hledger jj c "Minor fixes" -- -q 'account:Assets:Bank'
hledger jj log -n 5 --stat
EOF
)
help() {
echo -e "$HELP"
}
setup() {
ensure_journal_repo
}
ensure_journal_repo() {
# Change to journal directory and back using pushd/popd for efficiency
pushd "$DIR" > /dev/null || {
echo "Error: Could not change directory to '$DIR'." >&2
exit 1
}
trap 'popd > /dev/null' EXIT # Ensure we return to original dir even on errors
if ! jj status >/dev/null 2>&1; then
if ! jj git init --colocate; then
echo "Error: Failed to initialize jj/git repo in '$DIR'." >&2
exit 1
fi
echo "Created new colocated jj/git repo in $DIR"
fi
}
status() {
pushd "$DIR" > /dev/null || { echo "Error: Could not change directory to '$DIR'." >&2; return 1; }
trap 'popd > /dev/null' EXIT
jj status --color=always "$@" "${FILES[@]}" | grep -vE '^Untracked paths:|\?'
}
diff() {
pushd "$DIR" > /dev/null || { echo "Error: Could not change directory to '$DIR'." >&2; return 1; }
trap 'popd > /dev/null' EXIT
jj diff "$@" "${FILES[@]}"
}
commit() {
pushd "$DIR" > /dev/null || { echo "Error: Could not change directory to '$DIR'." >&2; return 1; }
trap 'popd > /dev/null' EXIT
if ! jj file track "${FILES[@]}"; then
echo "Error: Failed to track files using jj." >&2
return 1
fi
msg="${2:-$(date +'%Y-%m-%d %H:%M')}"
if ! jj commit -m "$msg" "$@" "${FILES[@]}"; then
echo "Error: jj commit command failed." >&2
return 1
fi
}
log() {
pushd "$DIR" > /dev/null || { echo "Error: Could not change directory to '$DIR'." >&2; return 1; }
trap 'popd > /dev/null' EXIT
jj log "$@" "${FILES[@]}"
}
if [ "$#" -lt 1 ]; then
help
else
command="$1"
args=("${@:2}")
case "$command" in
help | -h | --help ) help ;; # More explicit help matching
status* | s | st ) setup; status "${args[@]}" ;; # More explicit command prefixes
diff* | d ) setup; diff "${args[@]}" ;;
commit* | c ) setup; commit "${args[@]}" ;;
log* | l ) setup; log "${args[@]}" ;;
* ) echo "Unknown command: $command"; return 1 ;; # Default case for unknown commands
esac
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment