Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: typicode/husky
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v9.0.11
Choose a base ref
...
head repository: typicode/husky
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v9.1.0
Choose a head ref

Commits on Feb 13, 2024

  1. Copy the full SHA
    1bbb922 View commit details

Commits on Feb 14, 2024

  1. fix #1366 Update how-to.md (#1384)

    Fix #1366 by adding documentation to `how-to.md`
    gcko authored Feb 14, 2024
    Copy the full SHA
    f031ca8 View commit details

Commits on Feb 22, 2024

  1. create tea.yaml

    typicode authored Feb 22, 2024
    Copy the full SHA
    6444322 View commit details

Commits on Mar 5, 2024

  1. chore: update deploy.yml

    typicode authored Mar 5, 2024
    Copy the full SHA
    ad6f0b3 View commit details

Commits on Mar 9, 2024

  1. docs: update index.md

    typicode authored Mar 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    93f1282 View commit details
  2. docs: typo

    typicode authored Mar 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9b51a79 View commit details
  3. docs: update README.md

    typicode authored Mar 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    4c54086 View commit details
  4. docs: bash (#1404)

    typicode authored Mar 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    e37252c View commit details
  5. fix: deploy.yml

    typicode authored Mar 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    d5ec3f5 View commit details
  6. fix: deploy.yml

    typicode authored Mar 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f1a2d56 View commit details

Commits on Mar 10, 2024

  1. Update README.md

    typicode authored Mar 10, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ee9ab00 View commit details
  2. Update index.md

    typicode authored Mar 10, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cbcbce8 View commit details
  3. Update index.md

    typicode authored Mar 10, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    cce18cc View commit details
  4. Update README.md

    typicode authored Mar 10, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    3ea4e4a View commit details

Commits on Mar 12, 2024

  1. fix: typo (#1406)

    godky authored Mar 12, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9f6e212 View commit details

Commits on May 9, 2024

  1. Update README.md

    typicode authored May 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c062cf7 View commit details

Commits on May 19, 2024

  1. Copy the full SHA
    96979e2 View commit details
  2. Copy the full SHA
    0f1b140 View commit details
  3. Copy the full SHA
    da40de3 View commit details

Commits on Jul 17, 2024

  1. v9.1.0 (#1467)

    typicode authored Jul 17, 2024
    Copy the full SHA
    e0994a4 View commit details
  2. 9.1.0

    typicode committed Jul 17, 2024
    Copy the full SHA
    9cef99b View commit details
29 changes: 29 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -6,6 +6,26 @@
Husky improves your commits and more 🐶 _woof!_

👋 _Hey! Using React, Vue or Astro? Check my new project [MistCSS](https://github.com/typicode/mistcss) to write 50% less code._

## Features

- Just `2 kB` (📦 _gzipped_) with no dependencies
- Extremely fast (runs in `~1ms`)
- Uses new Git feature (`core.hooksPath`)
- Supports:
- macOS, Linux, Windows
- Git GUIs, Node version managers, custom hooks directory, nested projects, monorepos
- [All 13 client-side Git hooks](https://git-scm.com/docs/githooks)

And more:
- Branch-specific hooks
- Use POSIX shell to script advanced cases
- Adheres to Git's native hook organization
- Aligns with [npm](https://docs.npmjs.com/cli/v10/using-npm/scripts#best-practices) best practices using `prepare` script
- Opt-in/opt-out options
- User-friendly error messages

## Changelog

[Check out the v9 changelog](https://github.com/typicode/husky/releases/tag/v9.0.1) to discover all the new and improved features!
@@ -20,6 +40,15 @@ https://typicode.github.io/husky

Support this project by becoming a sponsor [here](https://github.com/sponsors/typicode) 💖

### Special Sponsor

<p align="center">
<a href="https://app.tea.xyz/sign-up?r=8L2HWfJB6hs">
<img src="https://github.com/typicode/husky/assets/5502029/1b95c571-0157-48bc-a147-0d8d2fbc1d8a" /><br/>
Get rewards for your open-source contributions
</a>
</p>

### GitHub

<p align="center">
73 changes: 52 additions & 21 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,36 +1,67 @@
name: Deploy
# Sample workflow for building and deploying a VitePress site to GitHub Pages
#
name: Deploy VitePress site to Pages

on:
workflow_dispatch: {}
# Runs on pushes targeting the `main` branch. Change this to `master` if you're
# using the `master` branch as the default branch.
push:
branches:
- main
branches: [main]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: pages
cancel-in-progress: false

jobs:
deploy:
# Build job
build:
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
defaults:
run:
working-directory: ./docs
steps:
- uses: actions/checkout@v3
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v3
fetch-depth: 0 # Not needed if lastUpdated is not enabled
# - uses: pnpm/action-setup@v2 # Uncomment this if you're using pnpm
# - uses: oven-sh/setup-bun@v1 # Uncomment this if you're using Bun
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: Build
cache: npm # or pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: npm ci # or pnpm install / yarn install / bun install
- name: Build with VitePress
run: npx vitepress build
- uses: actions/configure-pages@v2
- uses: actions/upload-pages-artifact@v1
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: docs/.vitepress/dist
- name: Deploy

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
uses: actions/deploy-pages@v4
6 changes: 3 additions & 3 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
@@ -11,12 +11,12 @@ jobs:
test:
strategy:
matrix:
node-version: [18, 20]
node-version: [18, 20, 22]
os: [ubuntu-latest, macOS-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm --version
4 changes: 2 additions & 2 deletions .github/workflows/npm_publish.yml
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 20
- run: ./test.sh
@@ -27,7 +27,7 @@ jobs:
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org'
6 changes: 3 additions & 3 deletions bin.mjs → bin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node
import f, { writeFileSync as w } from 'fs'
import i from './index.mjs'
import i from './index.js'

let p, a, n, s, o, d

@@ -15,11 +15,11 @@ if (a == 'init') {
w(n, JSON.stringify(o, 0, /\t/.test(s) ? '\t' : 2) + '\n')
p.stdout.write(i())
try { f.mkdirSync('.husky') } catch {}
w('.husky/pre-commit', p.env.npm_config_user_agent.split('/')[0] + ' test\n')
w('.husky/pre-commit', p.env.npm_config_user_agent?.split('/')[0] ?? 'npm' + ' test\n')
p.exit()
}

d = c => console.error(`${c} command is deprecated`)
d = c => console.error(`${c} command is DEPRECATED`)
if (['add', 'set', 'uninstall'].includes(a)) { d(a); p.exit(1) }
if (a == 'install') d(a)

53 changes: 48 additions & 5 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -12,17 +12,60 @@ export default defineConfig({
// outline: [2, 3],
socialLinks: [
{ icon: 'github', link: 'https://github.com/typicode/husky' },
{ icon: 'twitter', link: 'https://x.com/typicode' }
],
carbonAds: {
code: 'CWYDP53L',
placement: 'typicodegithubio',
},
// carbonAds: {
// code: 'CWYDP53L',
// placement: 'typicodegithubio',
// },
sidebar: [
{ text: 'Introduction', link: '/' },
{ text: 'Get Started', link: '/get-started' },
{ text: 'How To', link: '/how-to' },
{ text: 'Troubleshoot', link: '/troubleshoot' },
{ text: 'Migrate from v4', link: '/migrate-from-v4' },
],
],
nav: [
{ text: 'Sponsor', link: 'https://github.com/sponsors/typicode' }
]
},
locales: {
root: {
label: 'English',
lang: 'en-US'
},
zh: {
label: '简体中文',
lang: 'zh-hans',
description: '使 Git hooks 变得简单',
link: '/zh/',
themeConfig: {
sidebar: [
{ text: '简介', link: '/zh/' },
{ text: '快速开始', link: '/zh/get-started' },
{ text: '如何使用', link: '/zh/how-to' },
{ text: '故障排查', link: '/zh/troubleshoot' },
{ text: '从 v4 迁移', link: '/zh/migrate-from-v4' },
],
docFooter: {
prev: '上一页',
next: '下一页'
},
outline: {
label: '页面导航'
},
nav: [
{
text: 'v9.0.1',
items: [
{
text: '更新日志',
link: 'https://github.com/typicode/husky/releases/tag/v9.0.1'
}
]
}
]
}
}
}
})
19 changes: 18 additions & 1 deletion docs/get-started.md
Original file line number Diff line number Diff line change
@@ -59,5 +59,22 @@ git commit -m "Keep calm and commit"
# test script will run every time you commit
```

_For manual setup and more information, see the [How To](how-to) section._
## A few words...

### Scripting

While most of the time, you'll just run a few `npm run` or `npx` commands in your hooks, you can also script them using POSIX shell for custom workflows.

For example, here's how you can lint your staged files on each commit with only two lines of shell code and no external dependency:

```shell
# .husky/pre-commit
prettier $(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g') --write --ignore-unknown
git update-index --again
```

_This is a basic but workinkg example, check [lint-staged](https://github.com/lint-staged/lint-staged) if you need more._

### Disabling hooks

Husky doesn't force Git hooks. It can be globally disabled (`HUSKY=0`) or be opt-in if wanted. See the [How To](how-to) section for manual setup and more information.
41 changes: 41 additions & 0 deletions docs/how-to.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# How To

## Adding a New Hook

Adding a hook is as simple as creating a file. This can be accomplished using your favorite editor, a script or a basic echo command. For example, on Linux/macOS:
```shell
echo "npm test" > .husky/pre-commit
```

## Startup files

Husky allows you to execute local commands before running hooks. It reads commands from these files:
@@ -131,6 +138,40 @@ cd frontend
npm test
```

## Non-shell hooks

In order to run scripts that require the use of a scripting language, use the following pattern for each applicable hook:

(Example using hook `pre-commit` and NodeJS)
1. Create an entrypoint for the hook:
```shell
.husky/pre-commit
```
2. In the file add the following
```shell
node .husky/pre-commit.js
```
3. in `.husky/pre-commit.js`
```javascript
// Your NodeJS code
// ...
```

## Bash

Hook scripts need to be POSIX compliant to ensure best compatibility as not everyone has `bash` (e.g. Windows users).

That being said, if your team doesn't use Windows, you can use Bash this way:

```shell
# .husky/pre-commit
bash << EOF
# Put your bash script inside
# ...
EOF
```

## Node Version Managers and GUIs

If you're using Git hooks in GUIs with Node installed via a version manager (like `nvm`, `n`, `fnm`, `asdf`, `volta`, etc...), you might face a `command not found` error due to `PATH` environment variable issues.
44 changes: 29 additions & 15 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -10,10 +10,38 @@ Get started [here](/get-started.md).

[Check out the v9 changelog](https://github.com/typicode/husky/releases/tag/v9.0.1) to discover all the new and improved features! 🚀

## Features

- Just `2 kB` (📦 _gzipped_) with no dependencies
- Extremely fast (runs in `~1ms`)
- Uses new Git feature (`core.hooksPath`)
- Supports:
- macOS, Linux, Windows
- Git GUIs, Node version managers, custom hooks directory, nested projects, monorepos
- [All 13 client-side Git hooks](https://git-scm.com/docs/githooks)

And more:
- Branch-specific hooks
- Use POSIX shell to script advanced cases
- Adheres to Git's native hook organization
- Aligns with [npm](https://docs.npmjs.com/cli/v10/using-npm/scripts#best-practices) best practices using `prepare` script
- Opt-in/opt-out options
- Can be globally disabled
- User-friendly error messages

## Sponsors

Support this project by becoming a sponsor [here](https://github.com/sponsors/typicode) 💖

### Special Sponsor

<p align="center">
<a href="https://app.tea.xyz/sign-up?r=8L2HWfJB6hs">
<img src="https://github.com/typicode/husky/assets/5502029/1b95c571-0157-48bc-a147-0d8d2fbc1d8a" /><br/>
Get rewards for your open-source contributions
</a>
</p>

### GitHub

<p align="center">
@@ -31,23 +59,9 @@ Support this project by becoming a sponsor [here](https://github.com/sponsors/ty
<a href="https://opencollective.com/husky/tiers/company/4/website"><img src="https://opencollective.com/husky/tiers/company/4/avatar.svg?avatarHeight=120"></a>
<a href="https://opencollective.com/husky/tiers/company/5/website"><img src="https://opencollective.com/husky/tiers/company/5/avatar.svg?avatarHeight=120"></a>

## Features

- Just `2 kB` (📦 _gzipped_) with no dependencies
- Uses new Git feature (`core.hooksPath`)
- Adheres to Git's native hook organization
- Aligns with [npm](https://docs.npmjs.com/cli/v10/using-npm/scripts#best-practices) best practices using `prepare` script
- Clear user messages
- Opt-in/opt-out options
- Branch-specific hooks
- Supports:
- macOS, Linux, Windows
- Git GUIs, Node version managers, custom hooks directory, nested projects, monorepos
- [All 13 client-side Git hooks](https://git-scm.com/docs/githooks)

## Used by

Husky is used in [**over 1.3M projects**](https://github.com/typicode/husky/network/dependents?package_id=UGFja2FnZS0xODQzNTgwNg%3D%3D) on GitHub, including:
Husky is used in [**over 1.5M projects**](https://github.com/typicode/husky/network/dependents?package_id=UGFja2FnZS0xODQzNTgwNg%3D%3D) on GitHub, including:

- [vercel/next.js](https://github.com/vercel/next.js)
- [vercel/hyper](https://github.com/vercel/hyper)
15 changes: 4 additions & 11 deletions docs/migrate-from-v4.md
Original file line number Diff line number Diff line change
@@ -34,11 +34,8 @@ If you were calling locally installed binaries, **you need to run them via your
}
```

```shell [.husky/commit-msg (v9)]
# ...
npx --no jest
# or
yarn jest
```shell [.husky/pre-commit (v9)]
jest
```

:::
@@ -56,10 +53,7 @@ yarn jest
```

```shell [.husky/commit-msg (v9)]
# ...
npx --no -- commitlint --edit $1
# or
yarn commitlint --edit $1
commitlint --edit $1
```

:::
@@ -68,5 +62,4 @@ Other environment variables changes:

- `HUSKY_SKIP_HOOKS` is replaced by `HUSKY`.
- `HUSKY_SKIP_INSTALL` is replaced by `HUSKY`.
- `HUSKY_GIT_PARAMS` is removed. Instead Git parameters should be used directly in scripts (e.g. `$1`).
- `PATH` for locally installed tools is not automatically set anymore. You'll need to use your package manager to run them.
- `HUSKY_GIT_PARAMS` is removed. Instead Git parameters should be used directly in scripts (e.g. `$1`).
2,336 changes: 571 additions & 1,765 deletions docs/package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions docs/package.json
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
"build": "sponsorkit"
},
"devDependencies": {
"sponsorkit": "^0.9.1",
"vitepress": "^1.0.0-rc.40"
"sponsorkit": "^0.9.3",
"vitepress": "^1.0.1"
}
}
}
462 changes: 250 additions & 212 deletions docs/sponsorkit/sponsors.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 63 additions & 0 deletions docs/zh/get-started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# 快速开始

## 安装

::: code-group

```shell [npm]
npm install --save-dev husky
```

```shell [pnpm]
pnpm add --save-dev husky
```

```shell [yarn]
yarn add --dev husky
# 如果你的项目不是私有的,那么只需要安装 pinst
yarn add --dev pinst
```

```shell [bun]
bun add --dev husky
```

:::

## `husky init` <Badge type="tip" text="推荐" />

`init` 命令简化了项目中的 husky 设置。它会在 `.husky/` 中创建 `pre-commit` 脚本,并更新 `package.json` 中的 `prepare` 脚本。随后可根据你的工作流进行修改。

::: code-group

```shell [npm]
npx husky init
```

```shell [pnpm]
pnpm exec husky init
```

```shell [yarn]
# 由于特殊的注意事项和与其他包管理器的差异,
# 请参考“如何使用”章节。
```

```shell [bun]
bunx husky init
```

:::


## 试一试

恭喜你!你已经成功地用一个命令设置了你的第一个 Git 钩子 🎉。让我们测试一下:

```shell
git commit -m "Keep calm and commit"
# 测试脚本会在每次提交时运行
```

_有关手动设置和更多信息,请参见 [如何使用](how-to.md) 章节_

326 changes: 326 additions & 0 deletions docs/zh/how-to.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
# 如何使用

## 添加新 Hook

添加 hook 与创建文件一样简单。可以通过你喜欢的编辑器、脚本或 echo 命令来实现。例如,在 Linux/macOS 中:
```shell
echo "npm test" > .husky/pre-commit
```

## 启动文件

Husky 允许你在运行钩子之前执行本地命令。它从这些文件中读取命令:

- `$XDG_CONFIG_HOME/husky/init.sh`
- `~/.config/husky/init.sh`
- `~/.huskyrc` (已弃用)

Windows 系统:`C:\Users\yourusername\.config\husky\init.sh`

## 跳过 Git 钩子

### 对于单个命令

大多数 Git 命令都包含一个 `-n/--no-verify` 选项来用于跳过钩子:

```sh
git commit -m "..." -n # 跳过 Git 钩子
```

对于没有使用此标识的命令,使用 HUSKY=0 来临时禁用钩子:

```shell
HUSKY=0 git ... # 临时禁用所有 Git 钩子
git ... # 钩子会再次运行
```

### 对于多个命令

在一个较长的时间里禁用钩子(例如,在变基或者合并期间):

```shell
export HUSKY=0 # 禁用所有 Git 钩子
git ...
git ...
unset HUSKY # 重新启用钩子
```

### 对于 GUI 或全局

要在 GUI 客户端或全局禁用 Git 钩子,请修改 Husky 配置:

```sh
# ~/.config/husky/init.sh
export HUSKY=0 # Husky 不会安装,也不会再你的机器上运行钩子
```

## CI 服务器和 Docker

要避免在 CI 服务器或 Docker 中安装 Git 钩子,请使用 `HUSKY=0`。例如,在 GitHub Actions 中:

```yml
# https://docs.github.com/en/actions/learn-github-actions/variables
env:
HUSKY: 0
```
如果只安装 `dependencies`(不是 `devDependencies`),`"prepare": "husky"` 脚本可能会失败,因为 Husky 不会被安装。

你有多种解决方案。

修改 `prepare` 脚本使其永远不会失败:

```json
// package.json
"prepare": "husky || true"
```

你仍然会在输出中看到一个 `command not found` 的错误消息,这可能会让你很感到困惑。为了让它消失,创建 `.husky/install.mjs`:

<!-- Since husky may not be installed, it must be imported dynamically after prod/CI check -->
```js
// 在生产环境或 CI 环境中跳过 Husky 的安装
if (process.env.NODE_ENV === 'production' || process.env.CI === 'true') {
process.exit(0)
}
const husky = (await import('husky')).default
console.log(husky())
```

然后,在 `prepare` 脚本中使用它:

```json
"prepare": "node .husky/install.mjs"
```

## 测试钩子

要测试一个钩子,将 `exit 1` 添加到钩子脚本以中止 Git 命令:

```shell
# .husky/pre-commit
# 你的 WIP 脚本
# ...
exit 1
```

```shell
git commit -m "testing pre-commit code"
# 提交不会被创建
```

## 项目不在 Git 根目录

出于安全考虑,Husky 不会安装在父目录(`../`)中。但是,你可以在 `prepare` 脚本中更改目录。

考虑一下这个项目结构:

```
.
├── .git/
├── backend/ # 没有 package.json
└── frontend/ # package.json 中带有 husky
```
像这样设置你的 prepare 脚本:
```json
"prepare": "cd .. && husky frontend/.husky"
```

在你的 hook 脚本中,将目录切换回相关的子目录:

```shell
# frontend/.husky/pre-commit
cd frontend
npm test
```

## 非 shell 脚本钩子

为了运行需要使用脚本语言的脚本,对每个适用的钩子使用以下模式:

(使用钩子 `pre-commit` 和 NodeJS 的示例)
1. 为钩子创建一个入口:
```shell
.husky/pre-commit
```
2. 在文件中添加以下内容:
```shell
node .husky/pre-commit.js
```
3. 在 `.husky/pre-commit.js` 文件中:
```javascript
// 你的 NodeJS 代码
// ...
```

## Bash

钩子脚本需要与 POSIX 兼容,以确保最佳兼容性,因为并非每个人都有 bash (例如 Windows 用户)。

也就是说,如果你的团队不使用 Windows,你可以这样使用 Bash:

```shell
# .husky/pre-commit
bash << EOF
# Put your bash script inside
# ...
EOF
```

## Node 版本管理器和 GUI

如果您在 GUI 中使用 Git 钩子,并通过版本管理器(比如 `nvm``n``fnm``asdf``volta` 等等)安装 Node,由于 `PATH` 环境变量问题,你可能会遇到 `command not found` 报错。

### 了解 `PATH` 和版本管理器

`PATH` 是一个包含目录列表的环境变量,你的 shell 在这些目录中检索命令,如果没找到这个命令,你就会得到一个 `command not found` 报错。

在 shell 中运行 `echo $PATH` 来查看其内容。

版本管理器的工作方式如下:
1. 将初始化代码添加到 shell 启动文件(`.zshrc``.bashrc` 等),它会在每次打开终端时运行。
2. 将 Node 版本下载到主文件夹下的目录中。

例如,如果你有两个 Node 版本:

```shell
~/version-manager/Node-X/node
~/version-manager/Node-Y/node
```

打开终端将初始化版本管理器,它将选择一个版本(比如 `Node-Y`)并预先设置其到 `PATH` 的路径:

```shell
echo $PATH
# 输出
~/version-manager/Node-Y/:...
```

现在,Node 指向 `Node-Y`。切换到 `Node-X` 时会相应地改变 `PATH`

```shell
echo $PATH
# 输出
~/version-manager/Node-X/:...
```

出现这个问题是因为在终端之外启动的 GUI 没有初始化版本管理器,导致 `PATH` 没有 Node 安装路径。因此,来自 GUI 的 Git 钩子常常会失败。

### 解决方案

husky 在每个钩子之前都会执行 `~/.config/husky/init.sh`。将版本管理器初始化代码复制到这里,以确保在 GUI 中运行。

`nvm` 示例:

```shell
# ~/.config/husky/init.sh
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # 加载 nvm
```

或者,如果你的 shell 启动文件快速且轻量,也可以直接使用:

```shell
# ~/.config/husky/init.sh
. ~/.zshrc
```

## 手动设置

Git 需要配置,husky 需要在 `.husky/` 中设置文件。

在仓库中运行一次 `husky` 命令。理想情况下,将其设置在 `package.json``prepare` 脚本中,以便每次安装后自动执行<Badge type="tip" text="推荐" />

::: code-group

```json [npm]
{
"scripts": {
"prepare": "husky" // [!code hl]
}
}
```

```json [pnpm]
{
"scripts": {
"prepare": "husky" // [!code hl]
}
}
```

```json [yarn]
{
"scripts": {
// Yarn 不支持 prepare 脚本
"postinstall": "husky",
// 如果发布到 npmjs.com,需要加上这个
"prepack": "pinst --disable",
"postpack": "pinst --enable"
}
}
```

```json [bun]
{
"scripts": {
"prepare": "husky" // [!code hl]
}
}
```

:::

运行一次 `prepare`

::: code-group

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

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

```sh [yarn]
# Yarn 不支持 `prepare`
yarn run postinstall
```

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

:::

`.husky/` 目录中创建一个 `pre-commit` 文件:

::: code-group

```shell [npm]
# .husky/pre-commit
npm test
```

```shell [pnpm]
# .husky/pre-commit
pnpm test
```

```shell [yarn]
# .husky/pre-commit
yarn test
```

```sh [bun]
# .husky/pre-commit
bun test
```

:::
81 changes: 81 additions & 0 deletions docs/zh/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
![npm](https://img.shields.io/npm/dm/husky)

> 使现代的原生 Git 钩子变得简单
Husky 能使你的提交变得更好 🐶 _汪!_

在提交或推送时,自动化 **检查提交信息****检查代码****运行测试**

[这里](./get-started.md) 快速开始。

[查看 v9 更新日志](https://github.com/typicode/husky/releases/tag/v9.0.1)去发现所有新特性! 🚀

## 特性

- 仅有 `2 kB`(📦 _gzip 压缩后_),没有任何依赖。
- 非常快(运行速度约 `~1ms`
- 使用新 Git 特性(`core.hooksPath`
- 支持:
- macOS、Linux、Windows
- Git GUI、Node 版本管理器、自定义钩子目录、嵌套项目、Monorepos
- [所有 13 个客户端 Git 钩子](https://git-scm.com/docs/githooks)

更多:
- Branch-specific 钩子
- 使用 POSIX shell 为高级案例编写脚本
- 遵循 Git 的原生钩子组织结构
- 使用 `prepare` 脚本与 [npm](https://docs.npmjs.com/cli/v10/using-npm/scripts#best-practices) 最佳实践保持一致
- Opt-in/opt-out 选项
- 用户友好的报错信息

## 赞助者

支持这个项目来成为一个赞助者 [点击此处](https://github.com/sponsors/typicode) 💖

### 特别赞助

<p align="center">
<a href="https://app.tea.xyz/sign-up?r=8L2HWfJB6hs">
<img src="https://github.com/typicode/husky/assets/5502029/1b95c571-0157-48bc-a147-0d8d2fbc1d8a" /><br/>
Get rewards for your open-source contributions
</a>
</p>

### GitHub

<p align="center">
<a href="../sponsorkit/sponsors.svg">
<img src='../sponsorkit/sponsors.svg'/>
</a>
</p>

### Open Collective

<a href="https://opencollective.com/husky/tiers/company/0/website"><img src="https://opencollective.com/husky/tiers/company/0/avatar.svg?avatarHeight=120"></a>
<a href="https://opencollective.com/husky/tiers/company/1/website"><img src="https://opencollective.com/husky/tiers/company/1/avatar.svg?avatarHeight=120"></a>
<a href="https://opencollective.com/husky/tiers/company/2/website"><img src="https://opencollective.com/husky/tiers/company/2/avatar.svg?avatarHeight=120"></a>
<a href="https://opencollective.com/husky/tiers/company/3/website"><img src="https://opencollective.com/husky/tiers/company/3/avatar.svg?avatarHeight=120"></a>
<a href="https://opencollective.com/husky/tiers/company/4/website"><img src="https://opencollective.com/husky/tiers/company/4/avatar.svg?avatarHeight=120"></a>
<a href="https://opencollective.com/husky/tiers/company/5/website"><img src="https://opencollective.com/husky/tiers/company/5/avatar.svg?avatarHeight=120"></a>

## 谁在使用

Husky 在 GitHub 上用于[超过 130 万个项目](https://github.com/typicode/husky/network/dependents?package_id=UGFja2FnZS0xODQzNTgwNg%3D%3D),包括:

- [vercel/next.js](https://github.com/vercel/next.js)
- [vercel/hyper](https://github.com/vercel/hyper)
- [webpack/webpack](https://github.com/webpack/webpack)
- [angular/angular](https://github.com/angular/angular)
- [facebook/docusaurus](https://github.com/facebook/docusaurus)
- [microsoft/vscode](https://github.com/microsoft/vscode)
- [11ty/eleventy](https://github.com/11ty/eleventy)
- [stylelint/stylelint](https://github.com/stylelint/stylelint)
- [colinhacks/zod](https://github.com/colinhacks/zod)
- [rollup/rollup](https://github.com/rollup/rollup)
- [tinyhttp/tinyhttp](https://github.com/tinyhttp/tinyhttp)
- ...

## 相关文章

- [为什么 Husky 放弃了传统的 JS 配置](https://blog.typicode.com/posts/husky-git-hooks-javascript-config/)
- [为什么 Husky 不再自动安装了](https://blog.typicode.com/posts/husky-git-hooks-autoinstall/)
72 changes: 72 additions & 0 deletions docs/zh/migrate-from-v4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# 从 v4 迁移

如果你使用 `npm``yarn` 调用 `package.json` 脚本,**你可以简单地将命令**从配置文件复制到相应的钩子:

Husky v4

```json
// package.json
{
"hooks": {
"pre-commit": "npm test && npm run foo" // [!code hl]
}
}
```

Husky v9

```shell
# .husky/pre-commit
# 提示,你现在可以在多行中输入命令
npm test # [!code hl]
npm run foo # [!code hl]
```

如果你想调用本地安装的二进制文件,**现在你需要通过包管理器运行它们**

::: code-group

```js [.huskyrc.json (v4)]
{
"hooks": {
"pre-commit": "jest"
}
}
```

```shell [.husky/pre-commit (v9)]
# ...
npx --no jest
# 或者
yarn jest
```

:::

`HUSKY_GIT_PARAMS` 环境变量现在替换成原生参数 `$1``$2`

::: code-group

```js [.huskyrc.json (v4)]
{
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
```

```shell [.husky/commit-msg (v9)]
# ...
npx --no -- commitlint --edit $1
# 或者
yarn commitlint --edit $1
```

:::

其他环境变量的变化:

- `HUSKY_SKIP_HOOKS` 替换成 `HUSKY`.
- `HUSKY_SKIP_INSTALL` 替换成 `HUSKY`.
- `HUSKY_GIT_PARAMS` 被移除。取而代之的是 Git 参数应该直接在脚本中使用(例如 `$1`)。
- 本地安装工具的 `PATH` 不再自动设置,你需要使用包管理器来运行它们。
41 changes: 41 additions & 0 deletions docs/zh/troubleshoot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 故障排查

## 找不到命令(Command not found)

请参阅 [如何使用](./how-to) 获取解决方案。

## 钩子未运行

1. 验证文件名是否正确。例如,`precommit``pre-commit.sh` 都是无效的名称。有效名称请参考 Git 钩子[文档](https://git-scm.com/docs/githooks)
2. 运行 `git config core.hooksPath` 并确保它指向 `.husky/_`(或者你的自定义目录)。
3. 确认你的 Git 版本高于 `2.9`

## 卸载后 `.git/hooks/` 无法正常使用

如果卸载 `husky` 后 hooks 在 `.git/hooks/` 中无法正常使用,请执行命令 `git config --unset core.hooksPath`

## 在 Windows 上使用 Yarn

在 Windows 上使用 Git Bash 时,Git 钩子可能会失败(`stdin is not a tty`)。对于 Windows 用户,通过以下解决方案来实现:

1. 创建 `.husky/common.sh`:

```shell
command_exists () {
command -v "$1" >/dev/null 2>&1
}

# Windows 10、Git Bash 和 Yarn 的解决方案
if command_exists winpty && test -t 1; then
exec < /dev/tty
fi
```

1. 在运行 Yarn 命令的地方使用它:

```shell
# .husky/pre-commit
. "$(dirname -- "$0")/common.sh"

yarn ...
```
31 changes: 19 additions & 12 deletions husky
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
#!/usr/bin/env sh
# shellcheck disable=SC1090
[ "$HUSKY" = "2" ] && set -x
h="${0##*/}"
s="${0%/*/*}/$h"
n=$(basename "$0")
s=$(dirname "$(dirname "$0")")/$n

[ ! -f "$s" ] && exit 0

for f in "${XDG_CONFIG_HOME:-$HOME/.config}/husky/init.sh" "$HOME/.huskyrc"; do
# shellcheck disable=SC1090
[ -f "$f" ] && . "$f"
done
if [ -f "$HOME/.huskyrc" ]; then
echo "husky - '~/.huskyrc' is DEPRECATED, please move your code to ~/.config/husky/init.sh"
fi
i="${XDG_CONFIG_HOME:-$HOME/.config}/husky/init.sh"
[ -f "$i" ] && . "$i"

[ "${HUSKY-}" = "0" ] && exit 0

sh -e "$s" "$@"
c=$?

[ $c != 0 ] && echo "husky - $h script failed (code $c)"
[ $c = 127 ] && echo "husky - command not found in PATH=$PATH"
exit $c
c=0
h() {
[ $c = 0 ] && return
[ $c != 0 ] && echo "husky - $n script failed (code $c)"
[ $c = 127 ] && echo "husky - command not found in PATH=$PATH"
exit 1
}
trap 'c=$?; h' EXIT
set -e
PATH=node_modules/.bin:$PATH
. "$s"
File renamed without changes.
2 changes: 1 addition & 1 deletion index.mjs → index.js
Original file line number Diff line number Diff line change
@@ -18,6 +18,6 @@ export default (d = '.husky') => {
w(_('.gitignore'), '*')
f.copyFileSync(new URL('husky', import.meta.url), _('h'))
l.forEach(h => w(_(h), `#!/usr/bin/env sh\n. "\${0%/*}/h"`, { mode: 0o755 }))
w(_('husky.sh'), '')
w(_('husky.sh'), 'echo "husky - `#!/usr/bin/env sh` and `. "$(dirname -- "$0")/_/husky.sh"` lines in hooks are DEPRECATED and won\'t be supported in v10. You can remove these two lines for even simpler scripts"')
return ''
}
4 changes: 2 additions & 2 deletions package-lock.json
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "husky",
"version": "9.0.11",
"version": "9.1.0",
"type": "module",
"description": "Modern native Git hooks",
"keywords": [
"git",
@@ -15,9 +16,9 @@
"license": "MIT",
"author": "typicode",
"bin": {
"husky": "bin.mjs"
"husky": "bin.js"
},
"exports": "./index.mjs",
"exports": "./index.js",
"engines": {
"node": ">=18"
}
9 changes: 5 additions & 4 deletions test.sh
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@ sh test/3_from-sub-dir.sh
sh test/4_not-git-dir.sh
sh test/5_git_command_not_found.sh
sh test/6_command_not_found.sh
sh test/7_set_u.sh
sh test/8_husky_0.sh
sh test/9_init.sh
sh test/10_time.sh
sh test/7_node_modules_path.sh
sh test/8_set_u.sh
sh test/9_husky_0.sh
sh test/10_init.sh
sh test/11_time.sh
File renamed without changes.
2 changes: 1 addition & 1 deletion test/10_time.sh → test/11_time.sh
Original file line number Diff line number Diff line change
@@ -6,5 +6,5 @@ install
npx --no-install husky

git add package.json
echo "echo pre-commit" >.husky/pre-commit
echo "echo pre-commit" > .husky/pre-commit
time git commit -m foo
11 changes: 11 additions & 0 deletions test/7_node_modules_path.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/sh
. test/functions.sh
setup
install

npx --no-install husky

# Test pre-commit
git add package.json
echo 'echo "$PATH" | grep -q "node_modules/.bin"' > .husky/pre-commit
expect 0 "git commit -m foo"
File renamed without changes.
File renamed without changes.