> For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt.

# hooks

`rs hooks` 命令用于安装、更新和卸载仓库级 [Git hooks](https://git-scm.com/docs/githooks)。hook 脚本会在执行安装命令的项目目录中运行。

## 用法 \{#usage}

```bash
rs hooks [options]
rs hooks uninstall
```

直接运行 `rs hooks` 即可安装或更新 hooks。

## 安装 hooks \{#install-hooks}

hook 脚本默认存放在 Git 仓库根目录的 `.rstack/hooks` 中。如果当前目录不在 Git 仓库内，命令会跳过安装。

在负责管理仓库 hooks 的项目中，将 `rs hooks` 添加到 `package.json` 的 `prepare` 脚本：

```json title="package.json"
{
  "scripts": {
    "prepare": "rs hooks"
  }
}
```

执行一次该脚本，完成 hooks 安装：


```sh [npm]
npm run prepare
```

```sh [yarn]
yarn run prepare
```

```sh [pnpm]
pnpm run prepare
```

```sh [bun]
bun run prepare
```

例如，创建一个 `pre-commit` hook，并在其中运行 [`rs staged`](/zh/guide/cli/staged.md)：

```sh title=".rstack/hooks/pre-commit"
rs staged
```

安装命令可以安全地重复执行，并且不会加载 `rstack.config.*`。克隆仓库后或生成的 hook 文件缺失时，请再次运行 `rs hooks`。

:::warning 已有 Git hook 管理工具

`rs hooks` 会更新仓库的 [`core.hooksPath`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-corehooksPath)。如果检测到其他 hooks 路径或已有 Git hooks，命令会跳过安装。可运行 `rs hooks --force`，改用 Rstack 管理 hooks。

:::

## 安装选项 \{#install-options}

### `--force`

即使已经存在其他 Git hooks 配置，也可以使用 `--force`（或 `-f`）安装 Rstack hooks：

```bash
rs hooks --force
```

Rstack 会保留原有 hook 文件，并将 `core.hooksPath` 指向其生成的 hooks 目录。该配置生效期间，Git 不会执行原路径下的 hooks。

`--force` 无法替换由其他 Rstack 项目管理的 hooks。

:::tip
`rs hooks --force` 只需运行一次。`prepare` 脚本中应使用不带 `--force` 的 `rs hooks`。
:::

### `--hooks-dir`

指定 hook 脚本的自定义存放目录，路径相对于 Git 仓库根目录。

```bash
rs hooks --hooks-dir config/git-hooks

# 路径包含空格时需要使用引号
rs hooks --hooks-dir "config/git hooks"
```

使用自定义目录时，请将完整命令写入负责管理 hooks 的项目 `package.json`：

```json title="package.json"
{
  "scripts": {
    "prepare": "rs hooks --hooks-dir config/git-hooks"
  }
}
```

> 路径中不能包含 `..`，以免通过父目录路径在仓库之外创建或覆盖 hook 文件。

### `--help`

`--help`（或 `-h`）用于显示命令的用法、子命令和安装选项。

```bash
rs hooks --help
```

## 卸载 hooks \{#uninstall-hooks}

请在安装 hooks 的项目目录中运行：

```bash
rs hooks uninstall
```

Rstack 会自动定位当前生效的 hooks，并移除 `core.hooksPath` 配置和生成的 `_` 目录，但不会删除 `.rstack/hooks/pre-commit` 等项目 hook 文件。由其他项目或工具管理的 hooks 不会被删除。

如果不希望再次安装 hooks，请从 `prepare` 脚本中移除 `rs hooks`。

如果之前使用 `--force` 接管了 `.git/hooks` 中的 hooks，卸载后这些 hooks 会重新生效。

## Hook 文件 \{#hook-files}

默认目录结构如下：

```text
.rstack/
└── hooks/
    ├── pre-commit        # 仓库 hook 脚本：编辑并提交
    └── _/                # 由 rs hooks 生成；默认被 Git 忽略
        ├── .gitignore
        ├── .owner
        ├── runner
        ├── pre-commit
        ├── commit-msg
        └── ...
```

与 `_` 同级的文件是仓库 hook 脚本。`_` 目录包含生成文件，并由 Git 忽略。`rs hooks` 会将 `core.hooksPath` 指向 `.rstack/hooks/_`。

## 支持的 hooks \{#supported-hooks}

Rstack CLI 支持以下客户端 Git hooks：

- `pre-commit`
- `pre-merge-commit`
- `prepare-commit-msg`
- `commit-msg`
- `post-commit`
- `applypatch-msg`
- `pre-applypatch`
- `post-applypatch`
- `pre-rebase`
- `post-rewrite`
- `post-checkout`
- `post-merge`
- `pre-push`
- `pre-auto-gc`

在与 `_` 同级的位置创建对应的同名文件即可。

## Hook 运行时 \{#hook-runtime}

Rstack CLI 使用 POSIX `sh -e` 运行 hook 脚本。它会转发 Git 提供的参数和标准输入，并返回 hook 的退出码。运行 hook 前，Rstack 会切换到安装 hooks 的项目，并将该项目的 `node_modules/.bin` 添加到 `PATH` 开头。

### 禁用与调试 \{#disable-and-debug}

将 `RSTACK_HOOKS` 设为 `0`，可以跳过安装或 hook 执行：

```bash
RSTACK_HOOKS=0 git commit -m "Skip hooks"
```

将 `RSTACK_HOOKS` 设为 `2`，可以跟踪 Rstack CLI 的 hook 运行时，包括调用 hook 脚本和处理退出码等步骤；如需跟踪 hook 脚本内部的命令，请在脚本中添加 `set -x`：

```bash
RSTACK_HOOKS=2 git commit -m "Trace hooks"
```

### 配置 hook 运行环境 \{#configure-the-hook-environment}

运行 hook 脚本前，Rstack CLI 会加载以下可选的 POSIX shell 文件：

```text
${XDG_CONFIG_HOME:-$HOME/.config}/rstack/hooks-init.sh
```

可以在其中初始化 Node.js 版本管理器、更新 `PATH`，或为当前用户设置 `RSTACK_HOOKS=0`。

## 所有权保护 \{#ownership-safety}

每个生成的 hooks 目录都会记录所属项目。只有该项目应在 `prepare` 脚本中调用 `rs hooks`。其他项目即使使用 `--force` 调用安装命令，也会被跳过；卸载命令同样会拒绝删除其他项目的 hooks。

如需转移所有权：

1. 从原项目的 `prepare` 脚本中移除 `rs hooks`。
2. 在原项目中运行 `rs hooks uninstall`。
3. 将 `rs hooks` 添加到新项目的 `prepare` 脚本，并运行一次。

## Monorepo \{#monorepo}

在 monorepo 中，提供 Rstack CLI 的项目可能位于 `frontend/` 等子目录。在该目录中运行 `rs hooks`，hooks 仍会安装到 Git 仓库根目录：

```text
repo/.rstack/hooks/
repo/.rstack/hooks/_/
core.hooksPath=.rstack/hooks/_
```

Rstack 会将 `frontend` 记录为 hooks 所属项目。hook 脚本仍保存在仓库根目录，但会从 `frontend` 目录运行，因此可以直接使用其中的配置和依赖，无需显式执行 `cd`：

```sh title=".rstack/hooks/pre-commit"
rs staged
```

## Worktree 行为 \{#worktree-behavior}

Rstack 会保留当前生效的 `core.hooksPath` 所在的 Git 配置作用域。如果 linked worktree 使用的是 worktree 作用域配置，`rs hooks --force` 会在同一作用域中替换它，而不会写入一个被 Git 忽略的 local 配置。

`rs hooks uninstall` 也会从同一作用域中移除配置。卸载 worktree 作用域的安装只影响当前 worktree，不会改变其他 worktree 中的 hooks。local 作用域的 `core.hooksPath` 会在多个 worktree 之间共享，因此更改或移除该配置会影响所有未覆盖它的 worktree。

## 故障排查 \{#troubleshooting}

### Hook 未运行 \{#hook-does-not-run}

- 确认 hook 脚本使用[支持的名称](#supported-hooks)，并与 `_` 目录同级。
- 运行 `git config --show-scope --get core.hooksPath`，检查当前生效的配置作用域和路径。
- 重新运行 `rs hooks`，恢复生成文件及其可执行权限。
- 检查环境变量或初始化文件中是否设置了 `RSTACK_HOOKS=0`。
- 如果命令检测到其他 hooks 配置，请运行 `rs hooks --force`。
- 如果命令提示其他项目是 hooks owner，请按照[所有权保护](#ownership-safety)中的步骤转移 owner。

hook 脚本不需要可执行权限，因为 Rstack CLI 会使用 `sh` 运行它。

### 找不到命令 \{#command-not-found}

退出码为 127 时，Rstack CLI 会打印实际生效的 `PATH`。如果 GUI Git 客户端找不到 Node.js 或包管理器，请在 `hooks-init.sh` 中初始化相关环境。

### Windows 与 Yarn \{#windows-and-yarn}

在 Windows 上，hooks 会通过 [Git for Windows](https://gitforwindows.org/) 自带的 POSIX shell 运行。请在 hook 中使用 LF 换行符和 `/` 路径分隔符。

[Yarn PnP](https://yarnpkg.com/features/pnp) 不提供 `node_modules/.bin`。请通过 Yarn 脚本运行工具，例如 `yarn run test`；必要时可通过 `hooks-init.sh` 配置 Node.js 和 Yarn。
