Spaces:
Running
Running
| /** | |
| * 递归对比两个目录,列出差异文件(排除 node_modules/.git/dist 等)。 | |
| * 用法: node compare-dirs.js <dirA> <dirB> | |
| */ | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const crypto = require('crypto'); | |
| const EXCLUDE_DIRS = new Set(['node_modules', '.git', 'temp', 'test-results', 'make', 'dist', 'tools', 'tests', 'stress', 'hf-trend', '.sisyphus', '.opencode', 'output', 'test-outputs']); | |
| const EXCLUDE_FILES = new Set(['PERFORMANCE_ANALYSIS.md', 'PERFORMANCE_STRESS_TEST_2026-08-03.md', 'HF_TOKEN.md']); | |
| const [A, B] = process.argv.slice(2); | |
| if (!A || !B) { console.error('need two dirs'); process.exit(1); } | |
| const md5 = (p) => crypto.createHash('md5').update(fs.readFileSync(p)).digest('hex'); | |
| function walk(d, base) { | |
| const out = new Map(); | |
| if (!fs.existsSync(d)) return out; | |
| for (const f of fs.readdirSync(d)) { | |
| const fp = path.join(d, f); | |
| if (fs.statSync(fp).isDirectory()) { | |
| if (EXCLUDE_DIRS.has(f)) continue; | |
| for (const [rel, h] of walk(fp, base)) out.set(rel, h); | |
| } else { | |
| if (EXCLUDE_FILES.has(f)) continue; | |
| const rel = path.relative(base, fp).replace(/\\/g, '/'); | |
| out.set(rel, md5(fp)); | |
| } | |
| } | |
| return out; | |
| } | |
| const mapA = walk(A, A); | |
| const mapB = walk(B, B); | |
| const all = new Set([...mapA.keys(), ...mapB.keys()]); | |
| const changed = []; | |
| const onlyA = []; | |
| const onlyB = []; | |
| for (const rel of all) { | |
| const ha = mapA.get(rel); | |
| const hb = mapB.get(rel); | |
| if (ha && hb) { if (ha !== hb) changed.push(rel); } | |
| else if (ha && !hb) onlyA.push(rel); | |
| else onlyB.push(rel); | |
| } | |
| changed.sort(); onlyA.sort(); onlyB.sort(); | |
| console.log(`=== A=${A}`); | |
| console.log(`=== B=${B}`); | |
| console.log(`\n[不同内容] ${changed.length} 个:`); | |
| for (const f of changed) console.log(` M ${f}`); | |
| console.log(`\n[仅 A 有] ${onlyA.length} 个:`); | |
| for (const f of onlyA) console.log(` A ${f}`); | |
| console.log(`\n[仅 B 有] ${onlyB.length} 个:`); | |
| for (const f of onlyB) console.log(` B ${f}`); | |