[{"data":1,"prerenderedAt":1156},["ShallowReactive",2],{"navigation_docs":3,"-guides-pre-commit":239,"-guides-pre-commit-surround":1151},[4,29,94,114,184],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"Start",false,"\u002Fstart","1.start",[10,14,19,24],{"title":11,"path":7,"stem":12,"icon":13},"What jscpd finds","1.start\u002Findex","i-lucide-house",{"title":15,"path":16,"stem":17,"icon":18},"Installation","\u002Fstart\u002Finstallation","1.start\u002F2.installation","i-lucide-download",{"title":20,"path":21,"stem":22,"icon":23},"Quickstart","\u002Fstart\u002Fquickstart","1.start\u002F3.quickstart","i-lucide-rocket",{"title":25,"path":26,"stem":27,"icon":28},"Configuration basics","\u002Fstart\u002Fconfiguration","1.start\u002F4.configuration","i-lucide-settings-2",{"title":30,"icon":6,"path":31,"stem":32,"children":33,"page":6},"Guides","\u002Fguides","2.guides",[34,39,44,49,54,59,64,69,74,79,84,89],{"title":35,"path":36,"stem":37,"icon":38},"Use jscpd in CI","\u002Fguides\u002Fci","2.guides\u002F01.ci","i-lucide-github",{"title":40,"path":41,"stem":42,"icon":43},"Pre-commit hooks","\u002Fguides\u002Fpre-commit","2.guides\u002F02.pre-commit","i-lucide-git-commit",{"title":45,"path":46,"stem":47,"icon":48},"Fail only on new duplication","\u002Fguides\u002Fbaseline","2.guides\u002F03.baseline","i-lucide-git-compare",{"title":50,"path":51,"stem":52,"icon":53},"Monorepos","\u002Fguides\u002Fmonorepos","2.guides\u002F04.monorepos","i-lucide-folder-tree",{"title":55,"path":56,"stem":57,"icon":58},"Cross-format detection","\u002Fguides\u002Fcross-format","2.guides\u002F05.cross-format","i-lucide-layers",{"title":60,"path":61,"stem":62,"icon":63},"Agents","\u002Fguides\u002Fagents","2.guides\u002F06.agents","i-lucide-cpu",{"title":65,"path":66,"stem":67,"icon":68},"Editors","\u002Fguides\u002Feditors","2.guides\u002F07.editors","i-lucide-code",{"title":70,"path":71,"stem":72,"icon":73},"Duplication over git history","\u002Fguides\u002Fhistory","2.guides\u002F08.history","i-lucide-chart-no-axes-combined",{"title":75,"path":76,"stem":77,"icon":78},"Dashboard and health score","\u002Fguides\u002Fdashboard","2.guides\u002F09.dashboard","i-lucide-layout-dashboard",{"title":80,"path":81,"stem":82,"icon":83},"Dead code","\u002Fguides\u002Fdead-code","2.guides\u002F10.dead-code","i-lucide-ghost",{"title":85,"path":86,"stem":87,"icon":88},"Comparing two codebases","\u002Fguides\u002Fcompare","2.guides\u002F11.compare","i-lucide-git-compare-arrows",{"title":90,"path":91,"stem":92,"icon":93},"Semantic clones","\u002Fguides\u002Fsemantic-clones","2.guides\u002F12.semantic-clones","i-lucide-brain-circuit",{"title":95,"icon":6,"path":96,"stem":97,"children":98,"page":6},"Concepts","\u002Fconcepts","3.concepts",[99,104,109],{"title":100,"path":101,"stem":102,"icon":103},"Types of code clones","\u002Fconcepts\u002Fclone-types","3.concepts\u002F1.clone-types","i-lucide-copy",{"title":105,"path":106,"stem":107,"icon":108},"How detection works","\u002Fconcepts\u002Fhow-detection-works","3.concepts\u002F2.how-detection-works","i-lucide-scan-search",{"title":110,"path":111,"stem":112,"icon":113},"The health score","\u002Fconcepts\u002Fhealth-score","3.concepts\u002F3.health-score","i-lucide-heart-pulse",{"title":115,"icon":6,"path":116,"stem":117,"children":118,"page":6},"Reference","\u002Freference","4.reference",[119,124,129,134,171,176,180],{"title":120,"path":121,"stem":122,"icon":123},"CLI options","\u002Freference\u002Fcli","4.reference\u002F1.cli","i-lucide-terminal",{"title":125,"path":126,"stem":127,"icon":128},"The config file","\u002Freference\u002Fconfig-file","4.reference\u002F2.config-file","i-lucide-settings",{"title":130,"path":131,"stem":132,"icon":133},"Exit codes","\u002Freference\u002Fexit-codes","4.reference\u002F3.exit-codes","i-lucide-log-out",{"title":135,"icon":6,"path":136,"stem":137,"children":138},"Reporters","\u002Freference\u002Freporters","4.reference\u002F4.reporters\u002Findex",[139,141,146,151,156,161,166],{"title":135,"path":136,"stem":137,"icon":140},"i-lucide-file-chart-column",{"title":142,"path":143,"stem":144,"icon":145},"HTML reporter","\u002Freference\u002Freporters\u002Fhtml","4.reference\u002F4.reporters\u002F2.html","i-lucide-file-code",{"title":147,"path":148,"stem":149,"icon":150},"JSON reporter","\u002Freference\u002Freporters\u002Fjson","4.reference\u002F4.reporters\u002F3.json","i-lucide-braces",{"title":152,"path":153,"stem":154,"icon":155},"Badge reporter","\u002Freference\u002Freporters\u002Fbadge","4.reference\u002F4.reporters\u002F4.badge","i-lucide-award",{"title":157,"path":158,"stem":159,"icon":160},"SARIF reporter","\u002Freference\u002Freporters\u002Fsarif","4.reference\u002F4.reporters\u002F5.sarif","i-lucide-shield-check",{"title":162,"path":163,"stem":164,"icon":165},"CodeClimate reporter","\u002Freference\u002Freporters\u002Fcodeclimate","4.reference\u002F4.reporters\u002F6.codeclimate","i-lucide-flag-triangle-right",{"title":167,"path":168,"stem":169,"icon":170},"OpenMetrics reporter","\u002Freference\u002Freporters\u002Fopenmetrics","4.reference\u002F4.reporters\u002F7.openmetrics","i-lucide-activity",{"title":172,"path":173,"stem":174,"icon":175},"Supported formats","\u002Freference\u002Fsupported-formats","4.reference\u002F5.supported-formats","i-lucide-languages",{"title":177,"path":178,"stem":179,"icon":38},"GitHub Action","\u002Freference\u002Fgithub-action","4.reference\u002F6.github-action",{"title":181,"path":182,"stem":183,"icon":68},"Rust crates","\u002Freference\u002Frust-crates","4.reference\u002F7.rust-crates",{"title":185,"icon":6,"path":186,"stem":187,"children":188,"page":6},"Project","\u002Fproject","5.project",[189,194,199,204,229,234],{"title":190,"path":191,"stem":192,"icon":193},"Changelog","\u002Fproject\u002Fchangelog","5.project\u002F1.changelog","i-lucide-scroll-text",{"title":195,"path":196,"stem":197,"icon":198},"Migrating from v4 to v5","\u002Fproject\u002Fmigration","5.project\u002F2.migration","i-lucide-arrow-right-left",{"title":200,"path":201,"stem":202,"icon":203},"jscpd v4 (TypeScript)","\u002Fproject\u002Fv4","5.project\u002F3.v4","i-simple-icons-typescript",{"title":205,"icon":6,"path":206,"stem":207,"children":208},"Benchmarks","\u002Fproject\u002Fbenchmarks","5.project\u002F4.benchmarks\u002Findex",[209,211,216,220,225],{"title":205,"path":206,"stem":207,"icon":210},"i-lucide-gauge",{"title":212,"path":213,"stem":214,"icon":215},"Detection Speed","\u002Fproject\u002Fbenchmarks\u002Fdetection-speed","5.project\u002F4.benchmarks\u002F2.detection-speed","i-lucide-zap",{"title":217,"path":218,"stem":219,"icon":58},"Multi-Format Detection","\u002Fproject\u002Fbenchmarks\u002Fcross-format","5.project\u002F4.benchmarks\u002F3.cross-format",{"title":221,"path":222,"stem":223,"icon":224},"AI Token Efficiency","\u002Fproject\u002Fbenchmarks\u002Fai-token-efficiency","5.project\u002F4.benchmarks\u002F4.ai-token-efficiency","i-lucide-sparkles",{"title":226,"path":227,"stem":228,"icon":93},"Embedding Models","\u002Fproject\u002Fbenchmarks\u002Fembedding-models","5.project\u002F4.benchmarks\u002F5.embedding-models",{"title":230,"path":231,"stem":232,"icon":233},"Research","\u002Fproject\u002Fresearch","5.project\u002F5.research","i-lucide-graduation-cap",{"title":235,"path":236,"stem":237,"icon":238},"Articles","\u002Fproject\u002Farticles","5.project\u002F6.articles","i-lucide-newspaper",{"id":240,"title":40,"body":241,"description":1142,"extension":1143,"links":1144,"meta":1145,"navigation":1146,"path":41,"seo":1147,"stem":42,"__hash__":1150},"docs\u002F2.guides\u002F02.pre-commit.md",{"type":242,"value":243,"toc":1119},"minimark",[244,265,270,274,346,371,388,412,420,431,435,456,471,495,524,528,551,567,586,622,626,630,749,781,785,835,845,849,918,946,949,976,980,984,993,999,1013,1017,1027,1031,1053,1059,1072,1076,1086,1090,1115],[245,246,247,248,252,253,260,261,264],"p",{},"A hook runs jscpd on the developer's machine at ",[249,250,251],"code",{},"git commit",", before the change reaches a branch anyone else sees. It is the earliest gate and the cheapest one: no runner, no checkout, a scan that takes milliseconds, and the commit goes through or it does not. This page sets the hook up with the ",[254,255,259],"a",{"href":256,"rel":257},"https:\u002F\u002Fpre-commit.com",[258],"nofollow","pre-commit framework",", with Husky and with a plain git hook, then shows how to keep it from blocking on duplication that was already there. The shared gate for pull requests is on the ",[254,262,263],{"href":36},"CI page",".",[266,267,269],"h2",{"id":268},"minimal-setup","Minimal setup",[271,272,259],"h3",{"id":273},"pre-commit-framework",[275,276,282],"pre",{"className":277,"code":278,"filename":279,"language":280,"meta":281,"style":281},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","repos:\n  - repo: https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\n    rev: v5.4.0\n    hooks:\n      - id: jscpd\n",".pre-commit-config.yaml","yaml","",[249,283,284,297,313,324,332],{"__ignoreMap":281},[285,286,289,293],"span",{"class":287,"line":288},"line",1,[285,290,292],{"class":291},"swJcz","repos",[285,294,296],{"class":295},"sMK4o",":\n",[285,298,300,303,306,309],{"class":287,"line":299},2,[285,301,302],{"class":295},"  -",[285,304,305],{"class":291}," repo",[285,307,308],{"class":295},":",[285,310,312],{"class":311},"sfazB"," https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\n",[285,314,316,319,321],{"class":287,"line":315},3,[285,317,318],{"class":291},"    rev",[285,320,308],{"class":295},[285,322,323],{"class":311}," v5.4.0\n",[285,325,327,330],{"class":287,"line":326},4,[285,328,329],{"class":291},"    hooks",[285,331,296],{"class":295},[285,333,335,338,341,343],{"class":287,"line":334},5,[285,336,337],{"class":295},"      -",[285,339,340],{"class":291}," id",[285,342,308],{"class":295},[285,344,345],{"class":311}," jscpd\n",[245,347,348,349,352,353,356,357,362,363,366,367,370],{},"The hook is defined in the jscpd repository: on every commit it runs ",[249,350,351],{},"jscpd --exit-code 1"," on the staged text files and blocks the commit when two of them hold a clone. Since jscpd 5.2.1 it is a ",[249,354,355],{},"language: python"," hook that installs the ",[254,358,361],{"href":359,"rel":360},"https:\u002F\u002Fpypi.org\u002Fproject\u002Fjscpd\u002F",[258],"PyPI wheel"," of the pinned release into pre-commit's own environment, so the machine needs no Node.js; ",[249,364,365],{},"pre-commit autoupdate"," moves ",[249,368,369],{},"rev"," to the latest tag.",[275,372,377],{"className":373,"code":374,"filename":375,"language":376,"meta":281,"style":281},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pre-commit install\n","Terminal","bash",[249,378,379],{"__ignoreMap":281},[285,380,381,385],{"class":287,"line":288},[285,382,384],{"class":383},"sBMFI","pre-commit",[285,386,387],{"class":311}," install\n",[275,389,391],{"className":373,"code":390,"filename":375,"language":376,"meta":281,"style":281},"pre-commit run jscpd --files src\u002Forders.js src\u002Freports\u002Forders.js\n",[249,392,393],{"__ignoreMap":281},[285,394,395,397,400,403,406,409],{"class":287,"line":288},[285,396,384],{"class":383},[285,398,399],{"class":311}," run",[285,401,402],{"class":311}," jscpd",[285,404,405],{"class":311}," --files",[285,407,408],{"class":311}," src\u002Forders.js",[285,410,411],{"class":311}," src\u002Freports\u002Forders.js\n",[275,413,418],{"className":414,"code":416,"language":417,"meta":281},[415],"language-text","Check for duplicated code................................................Failed\n- hook id: jscpd\n- exit code: 1\n\nClone found (javascript)\n - orders.js [1:1 - 12:2] (12 lines, 124 tokens)\n   reports\u002Forders.js [1:1 - 12:2]\nFound 1 clones.\n","text",[249,419,416],{"__ignoreMap":281},[245,421,422,423,430],{},"The two files are the exact pair of ",[254,424,427],{"href":425,"rel":426},"https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\u002Ftree\u002Fmaster\u002Ffixtures\u002Fmcp-demo",[258],[249,428,429],{},"fixtures\u002Fmcp-demo","; a staged file whose twin is not staged passes, because the hook sees the staged files only.",[271,432,434],{"id":433},"husky","Husky",[275,436,438],{"className":373,"code":437,"filename":375,"language":376,"meta":281,"style":281},"npm install -D husky jscpd\n",[249,439,440],{"__ignoreMap":281},[285,441,442,445,448,451,454],{"class":287,"line":288},[285,443,444],{"class":383},"npm",[285,446,447],{"class":311}," install",[285,449,450],{"class":311}," -D",[285,452,453],{"class":311}," husky",[285,455,345],{"class":311},[275,457,459],{"className":373,"code":458,"filename":375,"language":376,"meta":281,"style":281},"npx husky init\n",[249,460,461],{"__ignoreMap":281},[285,462,463,466,468],{"class":287,"line":288},[285,464,465],{"class":383},"npx",[285,467,453],{"class":311},[285,469,470],{"class":311}," init\n",[275,472,477],{"className":473,"code":474,"filename":475,"language":476,"meta":281,"style":281},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npx jscpd . --threshold 5\n",".husky\u002Fpre-commit","sh",[249,478,479],{"__ignoreMap":281},[285,480,481,483,485,488,491],{"class":287,"line":288},[285,482,465],{"class":383},[285,484,402],{"class":311},[285,486,487],{"class":311}," .",[285,489,490],{"class":311}," --threshold",[285,492,494],{"class":493},"sbssI"," 5\n",[245,496,497,500,501,503,504,507,508,511,512,515,516,519,520,523],{},[249,498,499],{},"husky init"," creates ",[249,502,475],{}," with ",[249,505,506],{},"npm test"," in it and adds a ",[249,509,510],{},"prepare"," script, so every ",[249,513,514],{},"npm install"," wires the hook; replace the file's content with the jscpd command. With jscpd in ",[249,517,518],{},"devDependencies"," the hook runs the version from the lockfile; ",[249,521,522],{},"npx jscpd@5"," works without the install and downloads the package on its first run.",[271,525,527],{"id":526},"a-plain-git-hook","A plain git hook",[275,529,532],{"className":473,"code":530,"filename":531,"language":476,"meta":281,"style":281},"#!\u002Fbin\u002Fsh\njscpd . --threshold 5\n",".githooks\u002Fpre-commit",[249,533,534,540],{"__ignoreMap":281},[285,535,536],{"class":287,"line":288},[285,537,539],{"class":538},"sHwdD","#!\u002Fbin\u002Fsh\n",[285,541,542,545,547,549],{"class":287,"line":299},[285,543,544],{"class":383},"jscpd",[285,546,487],{"class":311},[285,548,490],{"class":311},[285,550,494],{"class":493},[275,552,554],{"className":373,"code":553,"filename":375,"language":376,"meta":281,"style":281},"chmod +x .githooks\u002Fpre-commit\n",[249,555,556],{"__ignoreMap":281},[285,557,558,561,564],{"class":287,"line":288},[285,559,560],{"class":383},"chmod",[285,562,563],{"class":311}," +x",[285,565,566],{"class":311}," .githooks\u002Fpre-commit\n",[275,568,570],{"className":373,"code":569,"filename":375,"language":376,"meta":281,"style":281},"git config core.hooksPath .githooks\n",[249,571,572],{"__ignoreMap":281},[285,573,574,577,580,583],{"class":287,"line":288},[285,575,576],{"class":383},"git",[285,578,579],{"class":311}," config",[285,581,582],{"class":311}," core.hooksPath",[285,584,585],{"class":311}," .githooks\n",[245,587,588,589,592,593,596,597,599,600,603,604,607,608,610,611,614,615,618,619,264],{},"Hooks under ",[249,590,591],{},".git\u002Fhooks"," are not versioned, so the script lives in the repository and git is pointed at the directory; each clone of the repository runs the ",[249,594,595],{},"git config"," command once, from a ",[249,598,510],{}," script in ",[249,601,602],{},"package.json",", a ",[249,605,606],{},"make hooks"," target or the onboarding notes. This hook runs whatever ",[249,609,544],{}," is on the PATH: ",[249,612,613],{},"npm install -g jscpd@5",", ",[249,616,617],{},"pip install jscpd"," or the installer from the ",[254,620,621],{"href":16},"installation page",[266,623,625],{"id":624},"make-it-useful","Make it useful",[271,627,629],{"id":628},"scan-the-whole-tree-and-let-legacy-clones-through","Scan the whole tree and let legacy clones through",[275,631,633],{"className":277,"code":632,"filename":279,"language":280,"meta":281,"style":281},"repos:\n  - repo: https:\u002F\u002Fgithub.com\u002Fkucherenko\u002Fjscpd\n    rev: v5.4.0\n    hooks:\n      - id: jscpd\n        pass_filenames: false\n        always_run: true\n        args: [\"src\", \"--baseline\", \".jscpd-baseline.json\", \"--fail-on-new-clones\"]\n",[249,634,635,641,651,659,665,675,687,698],{"__ignoreMap":281},[285,636,637,639],{"class":287,"line":288},[285,638,292],{"class":291},[285,640,296],{"class":295},[285,642,643,645,647,649],{"class":287,"line":299},[285,644,302],{"class":295},[285,646,305],{"class":291},[285,648,308],{"class":295},[285,650,312],{"class":311},[285,652,653,655,657],{"class":287,"line":315},[285,654,318],{"class":291},[285,656,308],{"class":295},[285,658,323],{"class":311},[285,660,661,663],{"class":287,"line":326},[285,662,329],{"class":291},[285,664,296],{"class":295},[285,666,667,669,671,673],{"class":287,"line":334},[285,668,337],{"class":295},[285,670,340],{"class":291},[285,672,308],{"class":295},[285,674,345],{"class":311},[285,676,678,681,683],{"class":287,"line":677},6,[285,679,680],{"class":291},"        pass_filenames",[285,682,308],{"class":295},[285,684,686],{"class":685},"sfNiH"," false\n",[285,688,690,693,695],{"class":287,"line":689},7,[285,691,692],{"class":291},"        always_run",[285,694,308],{"class":295},[285,696,697],{"class":685}," true\n",[285,699,701,704,706,709,712,715,717,720,723,726,728,730,732,735,737,739,741,744,746],{"class":287,"line":700},8,[285,702,703],{"class":291},"        args",[285,705,308],{"class":295},[285,707,708],{"class":295}," [",[285,710,711],{"class":295},"\"",[285,713,714],{"class":311},"src",[285,716,711],{"class":295},[285,718,719],{"class":295},",",[285,721,722],{"class":295}," \"",[285,724,725],{"class":311},"--baseline",[285,727,711],{"class":295},[285,729,719],{"class":295},[285,731,722],{"class":295},[285,733,734],{"class":311},".jscpd-baseline.json",[285,736,711],{"class":295},[285,738,719],{"class":295},[285,740,722],{"class":295},[285,742,743],{"class":311},"--fail-on-new-clones",[285,745,711],{"class":295},[285,747,748],{"class":295},"]\n",[245,750,751,754,755,758,759,761,762,764,765,768,769,772,773,776,777,780],{},[249,752,753],{},"pass_filenames: false"," stops pre-commit from handing the staged files to jscpd and ",[249,756,757],{},"always_run: true"," runs the hook even when no staged file is code, so jscpd scans ",[249,760,714],{}," as a whole and sees a clone between a staged file and one nobody touched. The baseline lets the clones recorded in ",[249,763,734],{}," through and fails the commit on a new one, with a ",[249,766,767],{},"[NEW]"," marker on the pair; ",[249,770,771],{},"args"," replaces the hook's own ",[249,774,775],{},"--exit-code 1",", so the gate is what you put there. The ",[254,778,779],{"href":46},"baseline guide"," shows how to record and refresh the file.",[271,782,784],{"id":783},"gate-on-a-percentage","Gate on a percentage",[275,786,788],{"className":277,"code":787,"filename":279,"language":280,"meta":281,"style":281},"      - id: jscpd\n        require_serial: true\n        args: [\"--threshold\", \"5\"]\n",[249,789,790,800,809],{"__ignoreMap":281},[285,791,792,794,796,798],{"class":287,"line":288},[285,793,337],{"class":295},[285,795,340],{"class":291},[285,797,308],{"class":295},[285,799,345],{"class":311},[285,801,802,805,807],{"class":287,"line":299},[285,803,804],{"class":291},"        require_serial",[285,806,308],{"class":295},[285,808,697],{"class":685},[285,810,811,813,815,817,819,822,824,826,828,831,833],{"class":287,"line":315},[285,812,703],{"class":291},[285,814,308],{"class":295},[285,816,708],{"class":295},[285,818,711],{"class":295},[285,820,821],{"class":311},"--threshold",[285,823,711],{"class":295},[285,825,719],{"class":295},[285,827,722],{"class":295},[285,829,830],{"class":311},"5",[285,832,711],{"class":295},[285,834,748],{"class":295},[245,836,837,838,840,841,844],{},"With ",[249,839,771],{}," set this way, the staged files still follow at the end of the command line, and the hook fails when their duplication is above 5 percent (strictly greater), which lets a small clone among large files through. ",[249,842,843],{},"require_serial: true"," keeps all staged files in one jscpd process; without it pre-commit splits them over several processes, and a pair can be split with them.",[271,846,848],{"id":847},"scope-the-hook","Scope the hook",[275,850,852],{"className":277,"code":851,"filename":279,"language":280,"meta":281,"style":281},"      - id: jscpd\n        files: \\.(ts|tsx)$\n        args: [\"--exit-code\", \"1\", \"--ignore\", \"**\u002F__snapshots__\u002F**\"]\n",[249,853,854,864,874],{"__ignoreMap":281},[285,855,856,858,860,862],{"class":287,"line":288},[285,857,337],{"class":295},[285,859,340],{"class":291},[285,861,308],{"class":295},[285,863,345],{"class":311},[285,865,866,869,871],{"class":287,"line":299},[285,867,868],{"class":291},"        files",[285,870,308],{"class":295},[285,872,873],{"class":311}," \\.(ts|tsx)$\n",[285,875,876,878,880,882,884,887,889,891,893,896,898,900,902,905,907,909,911,914,916],{"class":287,"line":315},[285,877,703],{"class":291},[285,879,308],{"class":295},[285,881,708],{"class":295},[285,883,711],{"class":295},[285,885,886],{"class":311},"--exit-code",[285,888,711],{"class":295},[285,890,719],{"class":295},[285,892,722],{"class":295},[285,894,895],{"class":311},"1",[285,897,711],{"class":295},[285,899,719],{"class":295},[285,901,722],{"class":295},[285,903,904],{"class":311},"--ignore",[285,906,711],{"class":295},[285,908,719],{"class":295},[285,910,722],{"class":295},[285,912,913],{"class":311},"**\u002F__snapshots__\u002F**",[285,915,711],{"class":295},[285,917,748],{"class":295},[245,919,920,921,924,925,928,929,932,933,924,936,938,939,941,942,945],{},"pre-commit's ",[249,922,923],{},"files"," and ",[249,926,927],{},"exclude"," patterns decide which staged files reach the hook (it declares ",[249,930,931],{},"types: [text]",", so every text file does by default), and jscpd's ",[249,934,935],{},"--format",[249,937,904],{}," filter again inside the run; a file outside the ",[249,940,935],{}," list is left out even when pre-commit passes it. The format names are on the ",[254,943,944],{"href":173},"supported formats"," page.",[266,947,115],{"id":948},"reference",[950,951,952,961,966],"ul",{},[953,954,955,957,958,960],"li",{},[254,956,120],{"href":121}," for every flag the ",[249,959,771],{}," list can take.",[953,962,963,965],{},[254,964,130],{"href":131}," for what makes the hook fail.",[953,967,968,971,972,975],{},[254,969,970],{"href":126},"Configuration file",", since a ",[249,973,974],{},".jscpd.json"," in the repository root applies to the hook too: git runs hooks from the root of the working tree.",[266,977,979],{"id":978},"troubleshooting","Troubleshooting",[271,981,983],{"id":982},"the-hook-passes-although-two-staged-files-are-clones","The hook passes although two staged files are clones",[245,985,986,987,989,990,992],{},"pre-commit spreads the staged files over several jscpd processes, one per CPU core, so the two files of a pair can land in different batches and no process sees both; the output then holds two statistics tables. Set ",[249,988,843],{}," on the hook to keep the files in one process, or scan the tree with ",[249,991,753],{}," as above.",[271,994,996],{"id":995},"error-invalid-value-srcordersjs-for-exit-code-exit_code",[249,997,998],{},"error: invalid value 'src\u002Forders.js' for '--exit-code [\u003CEXIT_CODE>]'",[245,1000,1001,924,1003,1005,1006,1008,1009,1012],{},[249,1002,886],{},[249,1004,743],{}," take an optional number, so a file name right after them is read as that number and jscpd exits 2. Always give the number in a hook, ",[249,1007,775],{}," or ",[249,1010,1011],{},"--fail-on-new-clones 0",", because pre-commit appends the file names at the end of the command line.",[271,1014,1016],{"id":1015},"the-commit-is-blocked-by-duplication-that-was-already-there","The commit is blocked by duplication that was already there",[245,1018,1019,1021,1022,1024,1025,264],{},[249,1020,775],{}," fails on any clone among the files it sees, old or new. Record the accepted state in a baseline and gate on new clones only, as above, or switch the ",[249,1023,771],{}," to ",[249,1026,821],{},[271,1028,1030],{"id":1029},"the-table-lists-bash-and-perl-files-the-project-does-not-have","The table lists bash and perl files the project does not have",[245,1032,1033,1034,1036,1037,1040,1041,1043,1044,1046,1047,1049,1050,264],{},"A scan of the repository root (",[249,1035,264],{},") walks ",[249,1038,1039],{},".git"," as well, and the sample hooks in ",[249,1042,591],{}," count as shell and Perl sources. Pass the source directories (",[249,1045,714],{},") instead of ",[249,1048,264],{},", or add ",[249,1051,1052],{},"--ignore \"**\u002F.git\u002F**\"",[271,1054,1056,1057],{"id":1055},"nodejs-errors-with-an-older-rev","Node.js errors with an older ",[249,1058,369],{},[245,1060,1061,1062,1065,1066,1068,1069,1071],{},"Up to v5.2.0 the hook was ",[249,1063,1064],{},"language: node"," and needed Node.js; from v5.2.1 it installs the PyPI wheel. Move ",[249,1067,369],{}," forward. The Husky and plain hooks run whatever ",[249,1070,544],{}," the PATH resolves, so install it where the hook runs.",[271,1073,1075],{"id":1074},"skipping-the-hook-once","Skipping the hook once",[245,1077,1078,1081,1082,1085],{},[249,1079,1080],{},"SKIP=jscpd git commit"," skips this hook in the pre-commit framework; ",[249,1083,1084],{},"git commit --no-verify"," skips every hook of any kind.",[266,1087,1089],{"id":1088},"related","Related",[950,1091,1092,1097,1102,1107],{},[953,1093,1094,1096],{},[254,1095,35],{"href":36}," for the shared gate that a skipped hook cannot bypass.",[953,1098,1099,1101],{},[254,1100,45],{"href":46}," for the baseline the whole-tree hook relies on.",[953,1103,1104,1106],{},[254,1105,130],{"href":131}," for the codes a hook turns into a blocked commit.",[953,1108,1109,1111,1112,1114],{},[254,1110,15],{"href":16}," for every way to put ",[249,1113,544],{}," on the PATH.",[1116,1117,1118],"style",{},"html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":281,"searchDepth":299,"depth":299,"links":1120},[1121,1126,1131,1132,1141],{"id":268,"depth":299,"text":269,"children":1122},[1123,1124,1125],{"id":273,"depth":315,"text":259},{"id":433,"depth":315,"text":434},{"id":526,"depth":315,"text":527},{"id":624,"depth":299,"text":625,"children":1127},[1128,1129,1130],{"id":628,"depth":315,"text":629},{"id":783,"depth":315,"text":784},{"id":847,"depth":315,"text":848},{"id":948,"depth":299,"text":115},{"id":978,"depth":299,"text":979,"children":1133},[1134,1135,1136,1137,1138,1140],{"id":982,"depth":315,"text":983},{"id":995,"depth":315,"text":998},{"id":1015,"depth":315,"text":1016},{"id":1029,"depth":315,"text":1030},{"id":1055,"depth":315,"text":1139},"Node.js errors with an older rev",{"id":1074,"depth":315,"text":1075},{"id":1088,"depth":299,"text":1089},"Run jscpd at git commit with the pre-commit framework, Husky or a plain git hook, so a copy-paste is caught before it leaves the machine.","md",null,{},{"icon":43},{"title":1148,"description":1149},"jscpd as a pre-commit hook","Block a commit that adds duplicated code with the jscpd hook for the pre-commit framework, a Husky hook or a plain git hook, and tune what the hook scans.","pBZtYL0sJsmwmIl4FahnO5ZsGyzrGJT3inDfnYN3jUI",[1152,1154],{"title":35,"path":36,"stem":37,"description":1153,"icon":38,"children":-1},"Run jscpd as a failing check on GitHub Actions, GitLab CI or any other runner, with SARIF, Code Quality and metrics reports.",{"title":45,"path":46,"stem":47,"description":1155,"icon":48,"children":-1},"Record the clones a codebase already has in a baseline file or take them from a git ref, and fail the build only when a change adds a clone.",1791302983797]