git-coda — 一鍵 Ship 工作流 是做什麼的?
git-coda 是專案名稱;coda(終曲)是樂章的收束段,把整段開發歷史收束成一個音符,落回 main。工作流的動詞仍然是 ship。
目標與成功標準
把當前工作區的改動,完整走完這條路徑:
當前改動 → 基於最新 main 切新分支 → commit → PR → squash merge → 回 main
用戶呼叫 ship 就表示已經授權執行整條工作流。流程應連續完成,不要為分支名、commit message、PR 標題、PR 正文、衝突解決方案、驗證修復或是否繼續等資訊請求確認。
這份授權包括:
- 修改當前功能程式碼,使它與最新
main的線上功能相容。 - 修改由本次改動或衝突引起的測試、lint、型別、建置和 CI 問題。
- 建立必要的修復提交並重複推送。
- 在合併前同步最新
main,解決新產生的衝突並重新驗證。
只有同時滿足以下條件才算完成:
- PR 已成功合併到
main,不能把「PR 已建立」或「已開啟自動合併」當作完成。 - 必要檢查已經通過,或倉庫沒有可執行的檢查。
- 本地回到
main並盡可能快進到合併後的提交。
核心執行原則
ship的預設結果是「發布成功」,不是「發現問題後把問題交還給用戶」。- 命令失敗後先判斷原因並恢復流程;不要機械重複同一個失敗命令。
- 以最新
origin/main的現有線上能力和當前改動的產品目標為共同約束,優先保留兩者。 - 修復根因,不透過跳過測試、刪除斷言、關閉規則或使用
--no-verify製造假通過。 - 不使用 force push、
reset --hard、丟棄提交或覆蓋用戶改動等破壞性手段。 - 只有缺少認證/權限、必須由他人審批、平台或網路持續不可用、需求意圖確實無法從程式碼和上下文判斷時,才暫停並說明唯一需要用戶處理的事項。
第 0 步:資訊收集
開始前先檢查現狀:
git status --short --branch
git branch --show-current
git diff --stat
git diff --cached --stat
git remote -v
gh auth status
同時記錄當前分支、未提交改動和未推送提交,防止遺漏用戶工作。沒有未提交改動時,繼續檢查當前分支相對 origin/main 是否存在待發布提交;兩者都沒有才提示沒有可發布內容並停止。
自動確定以下資訊:
1. 分支名
- 用戶已經提供時直接使用。
- 否則根據改動內容推斷,格式為
<type>/<short-desc>。 - 遵循目標倉庫已有分支前綴或命名規則;沒有規則時使用語義化名稱。
- 本地或遠端同名分支已存在時,自動增加簡短後綴,避免因命名衝突中斷。
2. Commit message
- 用戶已經提供時直接使用。
- 否則根據完整 diff 生成 Conventional Commits 格式:
<type>(<scope>): <description>。
3. PR 標題和正文
- PR 標題預設使用 commit message。
- PR 正文根據 diff 自動生成 2–3 條 Summary、改動檔案概覽和驗證結果。
- 不單獨詢問 PR 名稱或正文。
確定後簡短告知用戶即將使用的分支和 commit,然後立即繼續,不等待回覆。
第 1 步:同步主分支
有工作區改動時先建立帶唯一識別的 stash,並記錄對應引用;沒有改動時不要建立空 stash。
git stash push --include-untracked -m "git-coda: <timestamp>"
git fetch --prune origin
fetch 後以第 0 步記錄的原分支和原 HEAD 為基準檢查待發布提交。原功能分支已有提交時保留該分支,直接合併 origin/main,隨後恢復本次 stash;不能先切到 main 再決定原分支是否有提交。只有原分支沒有待發布提交時才從 origin/main 新建發布分支。
先從遠端 HEAD 或倉庫配置核對目標主分支;下文 main 是佔位名稱,倉庫使用 master 等名稱時全部替換為實際分支。
告知用戶最新主分支的 commit hash。
如果本地 main 無法 fast-forward:
- 檢查本地獨有提交,不能刪除或覆蓋它們。
- 直接從最新
origin/main建立發布分支,繼續發布流程。 - 在最終回到
main時保留這些本地提交,並明確說明本地main未被覆蓋;這不影響已經完成的線上合併。
第 2 步:建立分支
git checkout -b <branch-name> origin/main
如果目前工作已經位於包含待發布提交的功能分支,可以繼續使用它;先合併最新 origin/main 並按「衝突處理流程」解決問題。不要把相同改動重複提交。
第 3 步:恢復改動並提交
git stash apply <recorded-stash>
git add -A
git commit -m "<commit-message>"
stash apply 發生衝突時,按「衝突處理流程」自行解決。確認所有改動已經恢復並提交後,再刪除本次建立的 stash;不要誤刪用戶已有 stash。
提交鉤子失敗屬於驗證失敗:修復根因、重新暫存並再次提交,不要使用 --no-verify。
衝突處理流程
以下流程適用於 stash apply、合併最新 main、遠端分支非 fast-forward 和 PR 合併衝突。
-
取得事實:
git status git diff --name-only --diff-filter=U git log --oneline --left-right origin/main...HEAD -
逐個查看衝突檔案的 base、當前分支和對方版本,並閱讀相關呼叫方、測試、型別、配置和近期提交。不要只看衝突標記,也不要整檔案盲選
ours或theirs。 -
判斷兩邊意圖:
origin/main代表當前線上行為,除非本次功能明確取代它,否則必須保留。- 當前分支代表本次功能目標,不能為了消除衝突而靜默刪除。
- 兩邊都新增有效能力時,整合介面、資料流和邊界條件,讓它們同時成立。
- 測試衝突按最終產品行為更新;鎖檔案和生成檔案使用倉庫規定的工具重新生成。
-
編輯到沒有衝突標記,執行
git add,確認git diff --name-only --diff-filter=U為空。 -
執行與衝突檔案相關的最小驗證,再執行倉庫要求的完整驗證。
-
建立合併或修復提交,繼續原流程。
如果無法僅憑衝突塊判斷,應繼續從實作、測試、呼叫鏈、提交歷史和專案文件收集證據。只有兩種行為互斥且產品意圖仍無法判斷時才詢問用戶,並把選項、影響和推薦結論一次說清。
第 3.5 步:本地驗證
根據當前改動和倉庫說明,尋找已有驗證命令:
- 優先讀取
AGENTS.md、CONTRIBUTING.md和README.md。 - 再檢查
package.json、pyproject.toml、Makefile、CI 配置等專案檔案。 - 必要時按鎖檔案使用專案既有套件管理器安裝依賴,不擅自升級依賴。
- 先執行與改動範圍直接相關的檢查,再執行倉庫要求的 lint、typecheck、test 或 build。
- 找不到可信命令時,明確告知用戶跳過了哪些驗證以及原因,不要自行編造命令。
驗證失敗修復循環
驗證失敗時不要暫停,按以下循環處理:
- 保留完整錯誤輸出,定位第一個根因,而不是只修最後一個連鎖報錯。
- 判斷失敗來自產品程式碼、測試預期、型別、格式、依賴、配置還是環境。
- 做滿足線上功能與當前功能的最小正確修復:
- 優先使用倉庫已有的 formatter 或 lint autofix,再處理剩餘問題。
- 程式碼缺陷修程式碼。
- 測試僅在本次功能有意改變行為、原預期已經過時時更新;不得弱化、跳過或刪除有效測試。
- 環境缺少依賴時按專案鎖檔案恢復環境;不得藉機做無關升級。
- 先重跑失敗的最小檢查,再重跑全部必要檢查。
- 發現新失敗就繼續診斷和修復,直到全部通過。
首次 push 前產生的修復可以納入當前提交;首次 push 後產生的修復建立新提交並正常 push,不要為了整理歷史而 force push。只要每輪都有新證據或實質進展,就繼續推進,不因失敗次數多而放棄。
第 4 步:推送並建立 PR
git push -u origin <branch-name>
push 被拒絕時先分類處理:
- 遠端同名分支是無關內容:自動換一個唯一分支名並推送。
- 遠端同名分支屬於同一功能且有新提交:fetch 後合併遠端分支,解決衝突、驗證並再次 push。
- 網路或 GitHub 暫時失敗:短暫重試並檢查服務恢復情況。
- 認證或寫權限缺失:保留完整工作現場並請求用戶完成授權。
使用 gh pr create 建立 PR:
- base 為
main - head 為新分支
- 標題使用 commit message
- 正文包含 2–3 條 Summary、改動檔案概覽、驗證結果和
🤖 Finale via git-coda
如果 PR 已存在則複用,不重複建立。建立成功後向用戶展示 PR URL,然後繼續,不等待回覆。
第 5 步:等待 CI 並修復
使用 gh pr checks <pr-number> --watch 等倉庫可用命令等待必要檢查。
檢查失敗時:
- 用
gh pr checks、gh run view --log-failed等命令讀取失敗任務和記錄。 - 能本地重現時先重現,按「驗證失敗修復循環」修復根因。
- 提交修復並正常 push,重新等待 CI。
- 重複直到必要檢查全部通過。
如果檢查長期 pending,檢查 workflow、並發佇列和 GitHub 狀態;能重新執行失敗任務時自行處理。只有任務必須由有權限的人批准或外部服務持續不可用時才請求用戶介入。
第 6 步:同步最新 main 並 Squash 合併
合併前再次同步主分支:
git fetch origin main
git merge --no-edit origin/main
有衝突時按「衝突處理流程」解決,重新跑本地驗證、push 並等待 CI。然後直接合併:
gh pr merge <pr-number> --squash --delete-branch
如果合併時提示 base 更新或衝突,重新執行同步、解決、驗證、push、等待 CI 的閉環,再次合併。如果倉庫支援合併佇列,進入佇列後持續等待,直到確認 PR 已實際合併。
不得繞過分支保護。缺少必要審批、簽名或權限時,這是外部阻塞:展示 GitHub 的準確返回資訊,說明已經完成的步驟和用戶只需完成的動作。
第 7 步:回到 main 並確認結果
git checkout main
git pull --ff-only origin main
git status --short --branch
gh pr view <pr-number> --json state,mergedAt,mergeCommit,url
確認 PR 狀態為 merged,並列印最終 main commit hash。如果本地 main 有預先存在的獨有提交而無法 fast-forward,保留它們,不做破壞性處理;仍應確認遠端 PR 已合併並把本地狀態講清楚。
列印最終狀態:
🚀 Ship 完成!
✓ 分支 <branch-name> 已合併到 main
✓ 本地驗證和必要 CI 已通過
✓ 當前在 main,已同步最新程式碼(<commit-hash>)
✓ PR: <pr-url>
錯誤處理原則
- 每步操作前簡短說明正在做什麼。
- 任何命令失敗後先診斷、修復並重試,繼續以成功合併為目標。
- 不使用 force push、
reset --hard或其他破壞性恢復方式。 - 主動解決衝突,並同時保護最新線上功能和當前功能目標。
- 主動修復測試、lint、型別檢查、建置、提交鉤子和 CI 失敗。
- 不為自動推斷的分支名、commit message、PR 標題、PR 正文或繼續執行請求確認。
- 不把可修復的失敗、需要多輪除錯或流程耗時當作阻塞。
- 只有當前環境確實無法解決的外部條件才暫停;保留可繼續工作的分支、提交和記錄。
- 暫停時只給出明確、最小、可執行的用戶動作,完成後可從原位置繼續。
常見邊界情況
| 情況 | 處理方式 |
|---|---|
| 當前在非 main 分支且只有工作區改動 | stash → 基於 origin/main 新建分支 → apply → 解決衝突 |
| 當前分支已有待發布提交 | 保留提交,合併最新 origin/main 後繼續 |
| 沒有未提交改動 | 檢查相對 origin/main 的待發布提交;都沒有才停止 |
| 分支名已存在 | 自動生成唯一後綴;同功能遠端分支則複用並整合 |
stash apply 或合併衝突 | 分析兩邊功能意圖,整合實作,驗證後繼續 |
| lint/test/typecheck/build 失敗 | 修復根因並循環驗證,不把失敗交給用戶 |
| CI 失敗 | 讀取記錄、本地重現、修復、push,等待新一輪 CI |
| 合併前 main 更新 | 合併最新 origin/main,解決衝突並重新走驗證閉環 |
gh 未登入 | 提示執行 gh auth login |
| 需人工審批或無寫權限 | 保留現場,說明唯一需要用戶完成的外部動作 |
用戶指定 --no-squash | 使用 --merge |