test(分批提交): 把變更依類型分批 commit

一個 commit 只裝一種類型:程式碼、測試、文件、雜項各自成批,reviewer 一次只看一件事,
日後 git log 也讀得懂。全部混成一顆「完成工作包」的巨大 commit,等於沒有歷史。

類型多半看得出來——測試檔就是 test、README 就是 docs——但 scripts/ 底下的改動是新功能
還是修 bug,只有做的人知道,所以那一批由 --type 指定。這張對照表是純字串規則,表格驅動。

scope 單檔用檔名(claim.test.js 的 scope 是 claim,不是 claim.test),多檔用 --scope 的
功能名。描述要有中文:日後回顧時看得懂的是中文,而夾雜英文的專有名詞本來就該保留原文。

--files 讓一次變更橫跨兩個功能時能分兩次跑;--body 讓工具產出的歷史與本 repo 既有的
commit 一樣說明得出「為什麼」。

列變更檔案刻意不用 git status --porcelain:它的前兩欄是狀態碼,而 runGit 會 trim 掉
輸出的前導空白,未 staged 的修改會少掉檔名的第一個字元。
This commit is contained in:
2026-09-17 08:23:21 +00:00
parent bb886457fd
commit d6b44e0ba8
2 changed files with 357 additions and 2 deletions
+3 -2
View File
@@ -8,7 +8,8 @@ import { join } from 'node:path';
import { tmpRoot } from './run-script.js';
/**
* @returns {{dir: string, cleanup: Function}} dir 為已有一顆 commit 的 git repo
* @returns {{dir: string, git: Function, cleanup: Function}} dir 為已有一顆 commit 的 git repo,
* git 為綁在它身上的執行器(與 makeTempRepoWithRemote 對稱)
*/
export function makeTempRepo() {
mkdirSync(tmpRoot, { recursive: true });
@@ -18,7 +19,7 @@ export function makeTempRepo() {
git('init', '-q', '-b', 'master');
seed(git, dir, 'tester', 'tester@example.com');
return { dir, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
return { dir, git, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
}
/**