diff --git a/docs/cli/commands.json b/docs/cli/commands.json index ef44c8592..f8e518d47 100644 --- a/docs/cli/commands.json +++ b/docs/cli/commands.json @@ -825,8 +825,8 @@ { "name": "global", "usage": "--global", - "help": "Install at user level (~/.gitconfig) so every repo on this machine\ngets hk hooks. Requires Git 2.54 or newer. In repos without an\n`hk.pkl`, the installed hook is a silent no-op.", - "help_first_line": "Install at user level (~/.gitconfig) so every repo on this machine", + "help": "Recommended. Install at user level (~/.gitconfig) so every repo\non this machine gets hk hooks. Requires Git 2.54 or newer. In\nrepos without an `hk.pkl`, the installed hook is a silent no-op.", + "help_first_line": "Recommended. Install at user level (~/.gitconfig) so every repo", "short": [], "long": ["global"], "hide": false, @@ -857,7 +857,7 @@ "mounts": [], "hide": false, "help": "Sets up git hooks to run hk", - "help_long": "Sets up git hooks to run hk.\n\nOn Git 2.54+ this uses config-based hooks (`hook..command`), which keeps `.git/hooks/` untouched and composes cleanly with other hook managers. On older Git it falls back to writing script shims.\n\nWith `--global`, hooks are installed into the user's `~/.gitconfig` so every repository picks them up without a per-repo install. In a project without an `hk.pkl`, the installed hook exits silently — no-op.", + "help_long": "Sets up git hooks to run hk.\n\nThe recommended setup is `hk install --global`, which installs hooks once into the user's `~/.gitconfig` so every repository on the machine picks them up automatically. In a project without an `hk.pkl`, the installed hook exits silently — no-op — so it's safe to enable everywhere. Requires Git 2.54+.\n\nWithout `--global`, hooks are installed into the current repo only. On Git 2.54+ this uses config-based hooks (`hook..command`), which keeps `.git/hooks/` untouched and composes cleanly with other hook managers. On older Git it falls back to writing script shims.", "name": "install", "aliases": ["i"], "hidden_aliases": [], diff --git a/docs/cli/install.md b/docs/cli/install.md index 11e21680e..3e3560939 100644 --- a/docs/cli/install.md +++ b/docs/cli/install.md @@ -7,17 +7,17 @@ Sets up git hooks to run hk. -On Git 2.54+ this uses config-based hooks (`hook..command`), which keeps `.git/hooks/` untouched and composes cleanly with other hook managers. On older Git it falls back to writing script shims. +The recommended setup is `hk install --global`, which installs hooks once into the user's `~/.gitconfig` so every repository on the machine picks them up automatically. In a project without an `hk.pkl`, the installed hook exits silently — no-op — so it's safe to enable everywhere. Requires Git 2.54+. -With `--global`, hooks are installed into the user's `~/.gitconfig` so every repository picks them up without a per-repo install. In a project without an `hk.pkl`, the installed hook exits silently — no-op. +Without `--global`, hooks are installed into the current repo only. On Git 2.54+ this uses config-based hooks (`hook..command`), which keeps `.git/hooks/` untouched and composes cleanly with other hook managers. On older Git it falls back to writing script shims. ## Flags ### `--global` -Install at user level (~/.gitconfig) so every repo on this machine -gets hk hooks. Requires Git 2.54 or newer. In repos without an -`hk.pkl`, the installed hook is a silent no-op. +Recommended. Install at user level (~/.gitconfig) so every repo +on this machine gets hk hooks. Requires Git 2.54 or newer. In +repos without an `hk.pkl`, the installed hook is a silent no-op. ### `--legacy` diff --git a/docs/getting_started.md b/docs/getting_started.md index 42a4c4998..0143952bf 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -32,19 +32,73 @@ Other installation methods: - [`brew install hk`](https://formulae.brew.sh/formula/hk) - [`aqua g -i jdx/hk`](https://github.com/aquaproj/aqua-registry/blob/main/pkgs/jdx/hk/registry.yaml) +## Install Hooks (recommended: global) + +On **Git 2.54+**, the recommended way to set up hk is to install hooks **once, globally** into your `~/.gitconfig`. They then apply to every repository on your machine — and are a **silent no-op in any repo that doesn't have an `hk.pkl`**, so it's safe to enable everywhere: + +```sh +hk install --global +``` + +This writes `hook.hk-.command` entries to your global git config for the common client-side hooks (`pre-commit`, `pre-push`, `commit-msg`, `prepare-commit-msg`, `post-checkout`, `post-merge`, `post-rewrite`, `pre-rebase`, `post-commit`). After this, adding an `hk.pkl` to a project is all you need — no per-repo install step. + +To remove the global install: + +```sh +hk uninstall --global +``` + +:::tip +Prefer this to per-repo `hk install`: no need to re-run `hk install` in each clone, new repos just work, and projects without an `hk.pkl` are unaffected. +::: + +### Per-repository install (alternative) + +If you can't use Git 2.54+, or you want hk to apply only to specific repos, use per-repo install from inside a project that has an `hk.pkl`: + +```sh +hk install +``` + +This installs only the hooks defined in the project's `hk.pkl`. On Git 2.54+ it writes config-based hooks (`git config hook.hk-.command`); on older Git it falls back to [script shims](https://github.blog/open-source/git/highlights-from-git-2-54/) in `.git/hooks/`. Pass `--legacy` to force shim mode. + +:::warning +Running per-repo `hk install` on top of `hk install --global` causes hk to fire **twice per event** — Git aggregates `hook..command` entries across every scope. If you want only the local install in a repo that already has the global install active, disable the global entries in that repo with `git config --local hook.hk-.enabled false`. +::: + +### Configuring manually in `~/.gitconfig` + +If you'd rather set this up by hand instead of running `hk install --global`, add a block like the following to your `~/.gitconfig`: + +```ini +[hook "hk-pre-commit"] + command = test "${HK:-1}" = "0" || hk run pre-commit --from-hook "$@" + event = pre-commit +[hook "hk-pre-push"] + command = test "${HK:-1}" = "0" || hk run pre-push --from-hook "$@" + event = pre-push +[hook "hk-commit-msg"] + command = test "${HK:-1}" = "0" || hk run commit-msg --from-hook "$@" + event = commit-msg +``` + +The `--from-hook` flag tells hk to exit silently when the project has no `hk.pkl` or doesn't define that event. The `test "${HK:-1}" = "0" ||` prefix is an escape hatch: run `HK=0 git commit` to bypass hooks for a single command. Use `mise x -- hk` instead of `hk` in the `command` if you manage hk via mise and don't auto-activate it. + +To disable hk for a single repo without uninstalling globally, set `hook.hk-.enabled = false` in that repo's `.git/config`. + ## Project Setup -Use [`hk init`](/cli/init) to generate a `hk.pkl` file: +With hooks installed globally, enabling hk for a project is just: ```sh hk init ``` -## Global Configuration +This generates an `hk.pkl` file in the root of the repository. `git commit` will now run the linters defined in that file via the already-installed global `pre-commit` hook — no per-repo `hk install` needed. -You can create a global configuration file that will be applied to all projects. This is useful for setting up consistent linting rules across multiple repositories. By default, hk will look for this file in your home directory. +## Global `hkrc` Configuration -The global configuration file follows the same format as `hk.pkl` and can be used to define global hooks and linters. Project-specific settings in `hk.pkl` can override or extend the global configuration. +Separately from global *hooks*, you can also create a global *config* file that is merged into every project's `hk.pkl`. This is useful for setting up consistent linting rules across multiple repositories. By default, hk looks for this file at `~/.config/hk/config.pkl`. See [hkrc](/configuration#hkrc) for details. ## `hk.pkl` @@ -91,56 +145,6 @@ hooks { See [configuration](/configuration) for more information on the `hk.pkl` file. -## Usage - -Inside a git repository with a `hk.pkl` file, run [`hk install`](/cli/install) to configure git to use the hooks defined in `hk.pkl`: - -```sh -hk install -``` - -This will install the hooks for the repository like `pre-commit` and `pre-push` if they are defined in `hk.pkl`. Running `git commit` would now run the linters defined above in our example through the pre-commit hook. - -On **Git 2.54 or newer**, `hk install` writes [config-based hooks](https://github.blog/open-source/git/highlights-from-git-2-54/) (`git config hook.hk-.command`) instead of script files in `.git/hooks/`. This keeps the hooks directory untouched and composes cleanly with other hook managers. On older Git it falls back to writing script shims — no configuration needed, hk detects the installed git version automatically. Pass `--legacy` to force the shim mode. - -## Install Hooks Globally (Git 2.54+) - -With Git 2.54+, you can install hk hooks once in your **user-wide** `~/.gitconfig` and they apply to every repository on your machine: - -```sh -hk install --global -``` - -This writes `hook.hk-.command` entries to your global git config for the common client-side hooks (`pre-commit`, `pre-push`, `commit-msg`, `prepare-commit-msg`, `post-checkout`, `post-merge`, `post-rewrite`, `pre-rebase`, `post-commit`). Each invocation is a **silent no-op in repos that don't have an `hk.pkl`**, so you can safely enable it everywhere without breaking unrelated projects. - -To remove the global install: - -```sh -hk uninstall --global -``` - -Per-repository `hk install` works alongside `--global`, but note that **Git aggregates `hook..command` entries across every scope and runs them all** — so a local install on top of a global one will fire hk twice per event. To run only the local install in a repo that also has the global install active, disable the global entries in that repo with `hook.hk-.enabled = false` (see the note at the end of this section). - -### Configuring manually in `~/.gitconfig` - -If you'd rather set this up by hand, add a block like the following to your `~/.gitconfig`: - -```ini -[hook "hk-pre-commit"] - command = test "${HK:-1}" = "0" || hk run pre-commit --from-hook "$@" - event = pre-commit -[hook "hk-pre-push"] - command = test "${HK:-1}" = "0" || hk run pre-push --from-hook "$@" - event = pre-push -[hook "hk-commit-msg"] - command = test "${HK:-1}" = "0" || hk run commit-msg --from-hook "$@" - event = commit-msg -``` - -The `--from-hook` flag tells hk to exit silently when the project has no `hk.pkl` or doesn't define that event. The `test "${HK:-1}" = "0" ||` prefix is an escape hatch: run `HK=0 git commit` to bypass hooks for a single command. Use `mise x -- hk` instead of `hk` in the `command` if you manage hk via mise and don't auto-activate it. - -To disable hk for a single repo without uninstalling globally, set `hook.hk-.enabled = false` in that repo's `.git/config`. - ## Checking and Fixing Code You can check or fix code with [`hk check`](/cli/check) or [`hk fix`](/cli/fix)—by convention, "check" means files should not be modified and "fix" diff --git a/docs/package.json b/docs/package.json index 9d45432e0..2b905abb6 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,5 +1,7 @@ { "name": "docs", + "version": "0.0.0", + "private": true, "scripts": { "docs:dev": "vitepress dev", "docs:build": "vitepress build", diff --git a/hk.usage.kdl b/hk.usage.kdl index 7e9835d30..f51c3ebcc 100644 --- a/hk.usage.kdl +++ b/hk.usage.kdl @@ -143,8 +143,8 @@ cmd init help="Generates a new hk.pkl file for a project" { } cmd install help="Sets up git hooks to run hk" { alias i - long_help "Sets up git hooks to run hk.\n\nOn Git 2.54+ this uses config-based hooks (`hook..command`), which keeps `.git/hooks/` untouched and composes cleanly with other hook managers. On older Git it falls back to writing script shims.\n\nWith `--global`, hooks are installed into the user's `~/.gitconfig` so every repository picks them up without a per-repo install. In a project without an `hk.pkl`, the installed hook exits silently — no-op." - flag --global help="Install at user level (~/.gitconfig) so every repo on this machine\ngets hk hooks. Requires Git 2.54 or newer. In repos without an\n`hk.pkl`, the installed hook is a silent no-op." + long_help "Sets up git hooks to run hk.\n\nThe recommended setup is `hk install --global`, which installs hooks once into the user's `~/.gitconfig` so every repository on the machine picks them up automatically. In a project without an `hk.pkl`, the installed hook exits silently — no-op — so it's safe to enable everywhere. Requires Git 2.54+.\n\nWithout `--global`, hooks are installed into the current repo only. On Git 2.54+ this uses config-based hooks (`hook..command`), which keeps `.git/hooks/` untouched and composes cleanly with other hook managers. On older Git it falls back to writing script shims." + flag --global help="Recommended. Install at user level (~/.gitconfig) so every repo\non this machine gets hk hooks. Requires Git 2.54 or newer. In\nrepos without an `hk.pkl`, the installed hook is a silent no-op." flag --legacy help="Force using the legacy `.git/hooks/` script shims instead of Git\n2.54+ config-based hooks. Not compatible with `--global`." flag --mise help="Use `mise x` to execute hooks. With this, it won't\nbe necessary to activate mise in order to run hooks\nwith mise tools." { long_help "Use `mise x` to execute hooks. With this, it won't\nbe necessary to activate mise in order to run hooks\nwith mise tools.\n\nSet HK_MISE=1 to make this default behavior." diff --git a/src/cli/install.rs b/src/cli/install.rs index 9cdd6d26e..032ae984d 100644 --- a/src/cli/install.rs +++ b/src/cli/install.rs @@ -19,19 +19,22 @@ const DEFAULT_GLOBAL_EVENTS: &[&str] = &[ /// Sets up git hooks to run hk. /// -/// On Git 2.54+ this uses config-based hooks (`hook..command`), which -/// keeps `.git/hooks/` untouched and composes cleanly with other hook -/// managers. On older Git it falls back to writing script shims. +/// The recommended setup is `hk install --global`, which installs hooks +/// once into the user's `~/.gitconfig` so every repository on the machine +/// picks them up automatically. In a project without an `hk.pkl`, the +/// installed hook exits silently — no-op — so it's safe to enable +/// everywhere. Requires Git 2.54+. /// -/// With `--global`, hooks are installed into the user's `~/.gitconfig` so -/// every repository picks them up without a per-repo install. In a project -/// without an `hk.pkl`, the installed hook exits silently — no-op. +/// Without `--global`, hooks are installed into the current repo only. +/// On Git 2.54+ this uses config-based hooks (`hook..command`), +/// which keeps `.git/hooks/` untouched and composes cleanly with other +/// hook managers. On older Git it falls back to writing script shims. #[derive(Debug, clap::Args)] #[clap(visible_alias = "i")] pub struct Install { - /// Install at user level (~/.gitconfig) so every repo on this machine - /// gets hk hooks. Requires Git 2.54 or newer. In repos without an - /// `hk.pkl`, the installed hook is a silent no-op. + /// Recommended. Install at user level (~/.gitconfig) so every repo + /// on this machine gets hk hooks. Requires Git 2.54 or newer. In + /// repos without an `hk.pkl`, the installed hook is a silent no-op. #[clap(long, verbatim_doc_comment)] global: bool,