Use jscpd in Pre-Commit Hook
Run jscpd before every commit to prevent duplicated code from entering the repository.
Using pre-commit framework
The pre-commit framework manages git hooks for you. After configuring the hook, it runs automatically on every git commit.
Install pre-commit
# 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:
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:
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
pre-commit install
jscpd now runs on every git commit. If duplication exceeds the threshold, the commit is blocked.
To run manually without committing:
pre-commit run jscpd --all-files
Using Husky
npm install -D husky
npx husky init
Add the hook:
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:
#!/bin/sh
jscpd --threshold 5 --reporters console,silent .
Make it executable:
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:
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:
git config core.hooksPath .githooks
Create .githooks/pre-commit:
#!/bin/sh
jscpd --threshold 5 --reporters console,silent .
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:
#!/bin/sh
git config core.hooksPath .githooks
Option C: npm prepare script
Add to 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
.PHONY: hooks
hooks:
git config core.hooksPath .githooks
Contributors run make hooks after cloning.
Tips
- Use
--reporters console,silentto show clone details without writing report files on every commit - Use
--thresholdto set a failure threshold — the hook exits with code 1 if exceeded - Use
--ignoreto 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
--formatto limit detection to specific languages during the hook, with a full scan in CI