summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--git-stack166
2 files changed, 167 insertions, 0 deletions
diff --git a/README.md b/README.md
index 6f8a9a7..9a71d4d 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ config stuff (i need to move everything into one place)
* `$XDG_CONFIG_HOME/mpv` -> `./mpv`
* `$XDG_CONFIG_HOME/alacritty` -> `./alacritty`
* `~/.vsvimrc` -> `./vs/.vsvimrc`
+* `$PATH/git-stack` -> `./git-stack`
# other config
* `~/.gitconfig`
diff --git a/git-stack b/git-stack
new file mode 100644
index 0000000..c5828f6
--- /dev/null
+++ b/git-stack
@@ -0,0 +1,166 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+app_name="git-stack"
+
+die() {
+ echo "$app_name: $*" >&2
+ exit 1
+}
+
+in_repo() {
+ git rev-parse --git-dir >/dev/null 2>&1
+}
+
+require_repo() {
+ in_repo || die "not inside a Git repository"
+}
+
+git_path() {
+ git rev-parse --git-path "$1"
+}
+
+stack_file() {
+ local p
+ p="$(git_path "$app_name/messages")"
+ mkdir -p "$(dirname "$p")"
+ touch "$p"
+ printf '%s\n' "$p"
+}
+
+install_hook() {
+ local hooks_dir hook tmp
+ hooks_dir="$(git_path hooks)"
+ hook="$hooks_dir/pre-commit"
+ mkdir -p "$hooks_dir"
+
+ if [ -f "$hook" ] && ! grep -Fq "# managed-by: $app_name" "$hook"; then
+ return 0
+ fi
+
+ tmp="$(mktemp)"
+ cat >"$tmp" <<'EOF'
+#!/usr/bin/env bash
+# managed-by: git-stack
+set -euo pipefail
+
+stack_file="$(git rev-parse --git-path git-stack/messages)"
+if [ -f "$stack_file" ] && [ -s "$stack_file" ]; then
+ echo "git-stack: commit blocked; message stack is not empty" >&2
+ echo "git-stack: pending messages:" >&2
+ nl -ba "$stack_file" >&2
+ echo "git-stack: clear with: git stack pop / git stack clear" >&2
+ exit 1
+fi
+EOF
+
+ chmod +x "$tmp"
+ mv "$tmp" "$hook"
+}
+
+ensure_ready() {
+ require_repo
+ install_hook
+}
+
+cmd_push() {
+ ensure_ready
+ [ "$#" -ge 1 ] || die 'usage: git stack push "message"'
+ local file msg
+ file="$(stack_file)"
+ msg="$*"
+ printf '%s\n' "$msg" >>"$file"
+ echo "$msg"
+}
+
+cmd_peek() {
+ ensure_ready
+ local file
+ file="$(stack_file)"
+ [ -s "$file" ] || die "stack is empty"
+ tail -n 1 "$file"
+}
+
+cmd_list() {
+ ensure_ready
+ local file
+ file="$(stack_file)"
+ [ -s "$file" ] || exit 0
+ nl -ba "$file"
+}
+
+cmd_pop() {
+ ensure_ready
+ local file last tmp
+ file="$(stack_file)"
+ [ -s "$file" ] || die "stack is empty"
+
+ last="$(tail -n 1 "$file")"
+ echo "$last"
+
+ tmp="$(mktemp)"
+ sed '$d' "$file" >"$tmp" || true
+ mv "$tmp" "$file"
+}
+
+cmd_clear() {
+ ensure_ready
+ : >"$(stack_file)"
+}
+
+cmd_install() {
+ ensure_ready
+ echo "hook installed in $(git_path hooks)/pre-commit"
+}
+
+usage() {
+ cat <<'EOF'
+Usage:
+ git stack push "message"
+ git stack peek
+ git stack list
+ git stack pop
+ git stack clear
+ git stack install
+EOF
+}
+
+main() {
+ [ "$#" -ge 1 ] || {
+ usage
+ exit 1
+ }
+
+ case "$1" in
+ push)
+ shift
+ cmd_push "$@"
+ ;;
+ peek)
+ shift
+ cmd_peek "$@"
+ ;;
+ list)
+ shift
+ cmd_list "$@"
+ ;;
+ pop)
+ shift
+ cmd_pop "$@"
+ ;;
+ clear)
+ shift
+ cmd_clear "$@"
+ ;;
+ install)
+ shift
+ cmd_install "$@"
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+ esac
+}
+
+main "$@"