Community程式設計與開發github.com

roselletabuena/git-atomic-commit

將工作目錄的變更拆分為原子性、單一目的的 Git 提交——調查變更內容,將其分組為邏輯單元,排序使每個提交都能獨立運作,並撰寫符合儲存庫現有風格的訊息。每當使用者要求提交時使用此技巧,特別是當工作目錄包含多個邏輯變更時,以及當他們要求拆分、分離、重新排序、重組或清理提交時。

git-atomic-commit 是什麼?

這個技能提供了一個結構化的工作流程,用於建立原子性 Git 提交,也就是每個提交只包含一個邏輯變更。它著重於四個主要優點:讓程式碼審查更容易(審查者一次只需理解一個變更)、讓復原更安全(可以只撤銷壞的變更,而不影響好的變更)、讓 `git bisect` 能夠準確定位引入錯誤的提交,以及確保每個提交都能獨立建構。該技能首先引導使用者調查整個工作目錄,使用 `git status` 和 `git diff` 等指令了解所有變更,然後根據一個簡單的語言測試(如果提交主旨需要「和」這個字來描述,那就代表應該拆分)將變更分組。接著,它詳細說明如何排序這些提交,以便每個提交都能獨立運作,並提供分階段的暫存策略,從簡單的整個檔案暫存(`git add <file>`)到更複雜的同一個檔案內部分割(詳細說明於 `references/splitting-one-file.md`)。最後,它強調了比對儲存庫現有提交訊息風格的重要性,包括類型前綴(如 Conventional Commits)或問題追蹤編號,但明確警告不要偽造或推測缺失的識別碼。該技能適用於任何支援 Git 的環境,包括 Claude Code、Cursor 和 Codex,因為它完全基於標準的 Git 指令和概念。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/roselletabuena/skills/tree/HEAD/plugins/git-workflows/skills/git-atomic-commit

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

原子性提交

原子性提交恰好包含一個邏輯變更。這樣做的回報並非為了整潔而整潔——而是團隊在提交混合時會失去的三種具體能力:

  • 審查:審查者可以將一個想法記在腦中來判斷它。
  • 復原:可以撤銷一個壞的變更,而不會帶走好的變更。
  • 二分搜尋:git bisect 可以找出引入錯誤的提交,這只有在每個提交都是一個變更 並且 每個提交都能獨立建構時才有效。

最後一個條件是最常被忽略的。原子性並不意味著「盡可能小」——而是意味著「一個完整的變更」。一個將函式從其所需的呼叫點分開的提交雖然較小,但並非原子性的,因為它會導致工作目錄出現問題。

工作流程

1. 在暫存任何東西之前先調查

在決定如何分割之前,先了解整個工作目錄:

git status --porcelain      # 每個路徑,包括未追蹤的
git diff                    # 未暫存的變更
git diff --cached           # 任何已暫存的內容
git log --oneline -15       # HEAD 的位置,以及本機風格

避免將 git add -A 和 git commit -a 作為第一步。它們是將混合的工作目錄變成混合提交的方式,而且一旦提交,將其拆分的工作量會比事先拆分嚴格來說更多。同時檢查那些根本不應該被提交的變更——例如建構輸出、.env 檔案、除錯輸出、不小心加入的編輯器設定。

2. 將變更分組為邏輯單元

最可靠的測試是語言學上的:起草提交主旨,如果它需要一個「和」才能準確描述,那就代表你有多個提交。「修正登入重新導向和更新 lodash」是兩個變更。審查者的問題是第二個測試:他們是否必須先了解變更 X 才能判斷變更 Y?如果不是,那麼這些變更是獨立的,應該分開。

分開:

保持獨立原因
格式化 / 空白字元 vs. 邏輯重新格式化會將那三行真正的程式碼埋在 400 行噪音中。這是單一價值最高的拆分。
重新命名 vs. 行為變更相同原因——重新命名會影響所有地方,並隱藏了關鍵的那一行。
相依套件更新 vs. 程式碼每個都需要自己的復原故事。
不相關的錯誤修正獨立的復原,獨立的二分搜尋。
重構 vs. 使用它的功能讓審查者可以確認重構並未改變行為。

合併在一起:

保留在一個提交中原因
一個變更及其測試它們是一個邏輯單元;未發布程式碼的測試,或沒有測試的程式碼,各自都是半個變更。
一個重新命名及其所有呼叫點拆分會導致工作目錄出現問題——與原子性相反。
一個簽名變更及其呼叫者同上。
一個變更及其說明文件/更新日誌它們描述同一個決定,應該一起復原。

3. 排序提交,使每個都能獨立建構

順序很重要,因為每個提交都應該讓工作目錄保持可運作狀態。將基礎放在使用它們的東西之前:在呼叫該助手的程式碼之前先加入該助手,在基於重構的功能之前先進行重構,在查詢之前先準備好資料庫綱要。如果兩個群組彼此相依,這是一個信號,表示它們實際上是一個提交。

4. 暫存每個群組

使用最不侵入性的可行技術。只在較簡單的方法無法表達拆分時才升級:

第 1 級 - 暫存整個檔案。 這涵蓋了絕大多數真實世界的拆分,因為邏輯變更通常位於不同的檔案中:

git add src/auth.py tests/test_auth.py
git commit -m "..."

第 2 級 - 將整個檔案擱置一旁,當工作目錄中有其他內容否則可能會被意外提交時使用:

git stash push -- requirements.txt    # 擱置它
# ... 提交其餘部分 ...
git stash pop                         # 取回它

第 3 級 - 在單一檔案內部分割變更。 僅在一個檔案確實包含兩個邏輯變更時才需要。其機制很繁瑣且容易出錯,因此在嘗試之前,請先閱讀 references/splitting-one-file.md。

git add -p 在非互動式環境中無法作用,而且它會靜默失敗**。它會顯示第一個區塊,在 stdin 遇到 EOF,不暫存任何東西,並且以狀態碼 0 退出。這裡的成功退出碼代表什麼都沒發生。git add -i、git commit -p 和 git checkout -p 也一樣。因為失敗看起來像成功,造成的損害通常是一個空白或缺少預期變更的提交——所以請改用參考檔案中的技術,並在提交前使用 git diff --cached 驗證索引。

5. 用儲存庫自己的語言來撰寫每則訊息

比對現有的歷史記錄,而不是匯入一個慣例。在純文字主旨的日誌中插入一個 feat: 前綴是雜訊,它會讓歷史看起來像兩個專案。先閱讀本機風格:

git log --no-merges --format='%s' -n 30               # 主旨風格
git log --no-merges -n 10 --format='%n---%n%s%n%b'    # 是否使用說明主體

尋找:type: 或 type(scope): 前綴(Conventional Commits)、像 [PROJ-123] 這樣的票證前綴、大小寫、祈使句 vs. 過去式(「Add」vs.「Added」)、典型的主旨長度,以及說明主體是正常的還是主旨獨自存在。採用你所發現的風格。Conventional Commits 是一個很好的預設值,僅當日誌已經在使用它,或者儲存庫還沒有任何歷史記錄且使用者沒有偏好時。

無論本機風格為何,主旨應該說明改變了什麼,而說明主體——當歷史記錄使用說明主體時——應該說明為什麼,因為 diff 已經展示了「什麼」。如果儲存庫有 CONTRIBUTING.md 或一個 commit-msg 鉤子,它們的優先級高於從日誌中的推斷。

比對慣例絕不意味著虛構它所攜帶的資料。 如果本機風格嵌入了票證或問題參考——[PROJ-123]、Fixes #456——而你沒有被給予真實的識別碼,不要猜測一個,也不要繼續你在日誌中看到的編號。一個看似合理的 [PROJ-124] 比完全沒有前綴更糟糕:它是永久性的,它看起來像真正的可追溯性,而且它很可能指向別人的不相關票證。省略該參考,比對風格的其餘部分,並在你的回覆中說明缺少 ID,以便使用者可以提供——該提交尚未推送,很容易修改。如果使用者還在,就詢問號碼。

6. 在交還控制權之前進行驗證

git log --oneline -n <count>     # 你建立的結構
git status --porcelain           # 還剩下什麼(如果有的話)

確認每個提交僅包含其自己的變更(git show --stat <sha>),並且沒有遺漏任何東西。如果有些變更是刻意保留未提交的,請明確說明——使用者認為已清理但實際上並非如此的目錄是最糟糕的結果,因為他們稍後會發現,並且不知道這是否是刻意的。

護欄

  • 形塑如何提交,而不是是否提交。 如果使用者沒有要求提交,這個技能並不代表適合建立一個,並且永遠不暗示推送。
  • 永遠不要為了讓拆分更容易而丟棄工作。 git checkout -- <file>、git reset --hard 和 git stash drop 會毀滅尚未被任何提交保存的未提交工作——這些工作是無法復原的。如果某個技術需要其中一個指令,請先備份內容,並在執行前確認備份存在。
  • 不要為了整理而改寫已發布的歷史記錄。 拆分已經推送的提交意味著強制推送,這會改寫其他人可能已經拉取的歷史記錄;請先詢問。
  • 不要為了看起來徹底而過度拆分。 三個各自會破壞建構的提交,比一個能正常運作的提交更糟糕。有疑問時,優先選擇能讓每個提交保持功能性的分組。
  • 如果工作目錄恰好只有一個邏輯變更,就將其作為一個提交。 並非每個工作目錄都需要拆分,而說明這一點也是一個很好的結果。

Individual skills in this repo

This repo contains 2 individual skills — each has its own dedicated page.

相關技能