From 8a6145ea14b5d197c2c4416ec9c900d1de1bb6c9 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Thu, 17 Sep 2026 08:23:24 +0000 Subject: [PATCH] =?UTF-8?q?test(commit-split):=20=E6=94=B9=E5=90=8D?= =?UTF-8?q?=E6=99=82=E5=88=A5=E6=BC=8F=E6=8E=89=E8=88=8A=E6=AA=94=E7=9A=84?= =?UTF-8?q?=E5=88=AA=E9=99=A4=EF=BC=8C=E5=A4=B1=E6=95=97=E6=99=82=E8=AA=AA?= =?UTF-8?q?=E5=87=BA=E5=81=9A=E5=88=B0=E5=93=AA=E8=A3=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git diff --name-only 預設偵測改名,只印出目的地那一個路徑。來源的刪除因此被漏掉—— 留在 index 裡沒被提交,而腳本還回報成功,要等下一次跑才會發現工作區不乾淨。加 --no-renames。 某一批提交失敗時,錯誤現在會列出前面已經建立的那幾顆 commit。不回捲它們:那會動到使用者的 歷史,而那幾顆本身是好的;但一定要說出做到哪裡,否則重跑前得自己去翻 git log。 類型對照表補上目標專案常見的測試擺法:tests/、spec/、__tests__/,以及放在被測檔案旁邊的 user.test.js。先前只認 test/,目標專案的測試會被併進 feat 那一批。 --- test/commit-split.test.js | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/commit-split.test.js b/test/commit-split.test.js index 31de5be..b46b3a5 100644 --- a/test/commit-split.test.js +++ b/test/commit-split.test.js @@ -42,6 +42,11 @@ const subjects = (repo) => const CLASSIFY = [ { path: 'test/claim.test.js', type: 'test', why: '測試檔' }, { path: 'test/helpers/stub-gitea.js', type: 'test', why: '測試用的 helper 也算測試' }, + { path: 'tests/user_test.py', type: 'test', why: '目標專案未必叫 test/' }, + { path: 'spec/user_spec.rb', type: 'test', why: '同上' }, + { path: '__tests__/user.js', type: 'test', why: '同上' }, + { path: 'src/user.test.js', type: 'test', why: '測試與程式碼放在一起也很常見' }, + { path: 'src/User.spec.ts', type: 'test', why: '同上' }, { path: 'README.md', type: 'docs', why: '根目錄的說明文件' }, { path: 'AGENTS.md', type: 'docs', why: '同上' }, { path: 'package.json', type: 'chore', why: '專案設定' }, @@ -306,6 +311,41 @@ test('被刪掉的檔案也照樣分類、照樣進 commit', async (t) => { assert.equal(repo.git('status', '--porcelain'), ''); }); +test('改名時舊檔的刪除也要進 commit,不能只提交新檔', async (t) => { + // git diff --name-only 預設偵測改名,只印目的地那一個路徑。漏掉來源等於把刪除留在 + // index 裡,而腳本還回報成功——下一次跑才會發現工作區不乾淨。 + const repo = withRepo(t); + write(repo, 'scripts/old.js'); + repo.git('add', '-A'); + repo.git('commit', '-qm', '先有這個檔案'); + repo.git('mv', 'scripts/old.js', 'scripts/new.js'); + + const { code, json } = await run(repo, ['--type', 'refactor', '--scope', '改名', '--subject', '換個名字']); + + assert.equal(code, 0, JSON.stringify(json)); + assert.deepEqual(json.data.commits[0].files, ['scripts/new.js', 'scripts/old.js']); + assert.equal(repo.git('status', '--porcelain'), '', '改名的兩邊都要進同一顆 commit'); +}); + +// ── 中途失敗 ─────────────────────────────────────────────────────── + +test('某一批提交失敗時,錯誤要說出前面已經建立了哪幾顆 commit', async (t) => { + // 沒說的話,使用者不知道做到哪裡,重跑前得自己去翻 git log + const repo = withRepo(t); + write(repo, 'scripts/one.js', 'test/one.test.js'); + // 用 pre-commit hook 擋掉測試那一批 + const hook = join(repo.dir, '.git', 'hooks', 'pre-commit'); + mkdirSync(dirname(hook), { recursive: true }); + writeFileSync(hook, '#!/bin/sh\ngit diff --cached --name-only | grep -q "^test/" && exit 1\nexit 0\n', { mode: 0o755 }); + + const { code, json } = await run(repo, ['--type', 'feat', '--subject', '做一件事']); + + assert.equal(code, 1); + assert.equal(json.error.code, 'COMMIT_FAILED'); + assert.match(json.error.message, /feat\(one\): 做一件事/, '要指名已經建立的那一顆'); + assert.match(json.error.message, /test\(one\)/, '也要指名是哪一批失敗的'); +}); + // ── --dry-run ───────────────────────────────────────────────────── test('--dry-run 印出將建立的 commit 與各自的檔案,但不提交', async (t) => {