pub const SHIM: &str = "#!/bin/sh\n# git-templates hook shim \u{2192} the amont binary.\n#\n# One shim serves every hook: it passes its own filename through, so the binary\n# knows which hook to run. Dispatchers (pre-commit, pre-push) and ported leaf\n# hooks use the identical file.\n# Author: https://github.com/fredericrous\n#\n# POSIX sh, not zsh: this must run wherever git does, including Git for Windows\n# (which ships sh but not zsh).\n#\n# Resolution order matters more than it looks. Git hooks do NOT inherit an\n# interactive shell\'s PATH: GUI clients (VS Code, Tower, JetBrains) launch git\n# with an environment that often lacks ~/.local/bin or ~/.cargo/bin. A shim that\n# only did `exec amont` would work in the terminal and fail in the GUI. So:\n# 1. $GIT_HOOKS_BIN \u{2014} escape hatch; also how `make test` points at target/debug\n# 2. the baked path \u{2014} an absolute path written into this shim by\n# `amont install`. The placeholder token is NOT\n# spelled out in this comment: substitution is a plain\n# global replace, so a comment containing the token gets\n# rewritten too, and the explanation turns into a machine\n# path. It did, for as long as this said otherwise.\n# ABSOLUTE, and checked for it below before it is used.\n# Git runs a hook with the working tree as the current\n# directory, so testing an UNSUBSTITUTED token with\n# `[ -x ]` asks the REPOSITORY whether it has a file by\n# that name \u{2014} and a clone shipping an executable so\n# named would then be run here, on the first commit,\n# before any check and before any trust decision. The\n# unbaked shim is a supported state (3 below), so the\n# value has to be rejected outright rather than asked\n# about on disk.\n# 3. $HOME/.local/bin \u{2014} the default install location, resolved at RUNTIME so\n# this template stays machine-agnostic. Without it, a\n# repo created by `git init` from a template dir that is\n# the git checkout itself (the usual setup here \u{2014} the\n# XDG path symlinks to the repo) never gets that token\n# substituted and would depend on PATH after all.\n# Both names are tried: Windows builds amont.exe, and\n# `[ -x .../amont ]` is false for it \u{2014} which left\n# Windows with PATH as its ONLY fallback, in exactly the\n# GUI-client case this list exists to survive. Windows\n# needs no symlink for any of this: point\n# init.templateDir straight at the checkout.\n# 4. PATH \u{2014} last resort\n# and if none resolve, FAIL LOUDLY. Never skip a check silently: a hook that\n# quietly does nothing is worse than one that is missing.\nset -e\nHOOKS_DIR=$(CDPATH= cd -- \"$(dirname -- \"$0\")\" && pwd)\nHOOK_NAME=$(basename -- \"$0\")\n\n# The baked path, and nothing relative \u{2014} see 2 above. Absolute means POSIX `/\u{2026}`\n# or a Windows drive path: `amont install` bakes `C:\\Users\\\u{2026}\\amont.exe` on\n# Git for Windows. An unbaked shim fails this and falls through to 3, which is\n# exactly what it should do.\nBAKED=\"__AMONT_BIN__\"\ncase \"$BAKED\" in\n /*|?:/*|?:\\\\*) ;;\n *) BAKED= ;;\nesac\n\nif [ -n \"$GIT_HOOKS_BIN\" ] && [ -x \"$GIT_HOOKS_BIN\" ]; then\n BIN=\"$GIT_HOOKS_BIN\"\nelif [ -n \"$BAKED\" ] && [ -x \"$BAKED\" ]; then\n BIN=\"$BAKED\"\nelif [ -x \"$HOME/.local/bin/amont\" ]; then\n BIN=\"$HOME/.local/bin/amont\"\nelif [ -x \"$HOME/.local/bin/amont.exe\" ]; then\n BIN=\"$HOME/.local/bin/amont.exe\"\nelif command -v amont > /dev/null 2>&1; then\n BIN=amont\nelse\n printf \' \\033[38;5;160m\u{2717}\\033[0m amont binary not found \u{2014} hooks cannot run.\\n\' >&2\n printf \' Looked at: $GIT_HOOKS_BIN, the baked path, ~/.local/bin, then PATH.\\n\' >&2\n printf \' Reinstall with \\033[38;5;208mmake install\\033[0m in git-templates.\\n\' >&2\n exit 1\nfi\n\nexec \"$BIN\" --hooks-dir \"$HOOKS_DIR\" \"$HOOK_NAME\" \"$@\"\n";Expand description
The one shim. All four git-invoked hooks are the same file — it passes its
own filename through — and shims_on_disk_match_the_embedded_one keeps the
repository’s templates/hooks/ honest against this copy.
The canonical text lives INSIDE this crate, and that is a packaging
constraint rather than a preference. It used to be
include_str!("../../../templates/hooks/pre-commit"), reaching up to the
repository root — which works in a checkout and cannot work in a published
crate, because cargo package tars up this directory and nothing above it.
The tarball compiled nowhere: couldn't read src/../../../templates/hooks/ pre-commit. crates.io is immutable, so that would have been a broken
release that could only be yanked, never fixed in place.
The repository’s templates/hooks/ still holds the four installable copies
— that directory IS the product for anyone pointing init.templateDir at a
clone, and it has to be real files rather than symlinks because Git for
Windows materialises those as text files containing a path.