# Use jscpd in Pre-Commit Hook

> Run jscpd before every commit to prevent duplicated code from entering the repository.

Run jscpd before every commit to prevent duplicated code from entering the repository.

## Using pre-commit framework

The [pre-commit](https://pre-commit.com) framework manages git hooks for you. After configuring the hook, it runs automatically on every `git commit`.

### Install pre-commit

```bash [Terminal]
# pip
pip install pre-commit

# brew
brew install pre-commit

# npm (wrapper around the Python tool)
npm install -g pre-commit
```

### Add the hook config

Add the hook config to `.pre-commit-config.yaml` in your repo.

**Option A: language: node** — pre-commit installs jscpd automatically:

```yaml [.pre-commit-config.yaml]
repos:
  - repo: local
    hooks:
      - id: jscpd
        name: jscpd - copy/paste detector
        entry: jscpd
        language: node
        additional_dependencies: ['jscpd@5']
        args: [--threshold, "5", --reporters, console,silent]
        pass_filenames: false
        always_run: true
```

**Option B: language: system** — jscpd must be pre-installed globally:

```yaml [.pre-commit-config.yaml]
repos:
  - repo: local
    hooks:
      - id: jscpd
        name: jscpd - copy/paste detector
        entry: jscpd
        language: system
        args: [--threshold, "5", --reporters, console,silent]
        pass_filenames: false
        always_run: true
```

If using Option B, install jscpd globally first: `npm install -g jscpd@5` or `cargo install jscpd`.

### Install the hook into git

```bash [Terminal]
pre-commit install
```

jscpd now runs on every `git commit`. If duplication exceeds the threshold, the commit is blocked.

To run manually without committing:

```bash [Terminal]
pre-commit run jscpd --all-files
```

## Using Husky

```bash [Terminal]
npm install -D husky
npx husky init
```

Add the hook:

```bash [Terminal]
echo 'npx jscpd@5 --threshold 5 --reporters console,silent .' > .husky/pre-commit
```

## Manual git hook

No extra tools required — just a shell script in `.git/hooks/`.

### Create the hook

Create `.git/hooks/pre-commit`:

```sh [.git/hooks/pre-commit]
#!/bin/sh
jscpd --threshold 5 --reporters console,silent .
```

Make it executable:

```bash [Terminal]
chmod +x .git/hooks/pre-commit
```

Hooks in `.git/hooks/` are not version-controlled. To share the hook with your team, store it in the repo and symlink or copy it.

### Option A: Symlink from a versioned script

Store the hook logic in the repo (e.g. `scripts/pre-commit`), then symlink:

```bash [Terminal]
ln -s ../../scripts/pre-commit .git/hooks/pre-commit
```

Each developer runs the symlink command once after cloning.

### Option B: `core.hooksPath` (Git 2.9+)

Point Git at a versioned hooks directory:

```bash [Terminal]
git config core.hooksPath .githooks
```

Create `.githooks/pre-commit`:

```sh [.githooks/pre-commit]
#!/bin/sh
jscpd --threshold 5 --reporters console,silent .
```

```bash [Terminal]
chmod +x .githooks/pre-commit
```

Commit `.githooks/` to the repo. New contributors run the `git config` command once after cloning. Add it to your onboarding docs or a `scripts/setup.sh`:

```sh [scripts/setup.sh]
#!/bin/sh
git config core.hooksPath .githooks
```

### Option C: npm `prepare` script

Add to `package.json`:

```json [package.json]
{
  "scripts": {
    "prepare": "git config core.hooksPath .githooks"
  }
}
```

`npm install` (and `npm ci`) automatically run `prepare`, so the hooks path is set with no manual steps.

### Option D: Makefile

```makefile [Makefile]
.PHONY: hooks
hooks:
    git config core.hooksPath .githooks
```

Contributors run `make hooks` after cloning.

## Tips

- Use `--reporters console,silent` to show clone details without writing report files on every commit
- Use `--threshold` to set a failure threshold — the hook exits with code 1 if exceeded
- Use `--ignore` to exclude generated files, test fixtures, or vendor directories
- For large repos, use the Rust engine (`jscpd@5` / `cpd`) — it runs 24-37x faster, keeping commit latency low
- Consider `--format` to limit detection to specific languages during the hook, with a full scan in CI
