Community程式設計與開發github.com

zheref/ao

將分支與其基礎分支同步——若尚未發佈則進行 rebase,已發佈則進行 merge,自動解決機械衝突,並在語意衝突時於 G5 暫停並顯示雙方內容。

ao 是什麼?

Ao 是一個專注於將 Git 分支與其基礎分支(如 main)同步的技能,主要用於 AI 編碼代理(如 Claude Code、Cursor、Codex)。它的核心功能是判斷分支上的提交是否已發佈至遠端:若未發佈則執行 rebase,若已發佈則執行 merge,以確保不會改寫他人可能已取得的提交。當遇到衝突時,Ao 會先對每個衝突路徑進行分類(機械衝突或語意衝突),機械衝突(如同時追加的區域、產生的檔案)會自動解決,而語意衝突(如一方刪除一方編輯)則會直接暫停並顯示雙方差異,交由開發者決定,絕不擅自選擇立場。此外,Ao 絕不執行 push 或 force-push,確保操作始終保持本地安全。它可作為獨立技能使用,也可整合至其他技能(如 murasaki、aka、en)的工作流程中。

相容平台✓Claude Code✓Codex CLI~Cursor
npx skills add https://github.com/zheref/hatsu/tree/HEAD/surfaces/codex/ao

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

在你喜歡的 AI 中提問

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

說明文件

共用政策位置: docs/DISCOVERY.md、docs/WORKFLOW.md、 docs/LAUNCH-MIGRATION.md、docs/STANDALONE-ENTRY.md、docs/PROCESS.md、docs/using/README.md 和 docs/using/mirrors.md 屬於已解析的 Hatsu 外掛 根目錄,而非消費端儲存庫。在已安裝的表面上,請使用 ten 列出的絕對根目錄來讀取這些檔案(若找不到則透過該技能重新解析)。下方的相對連結標示原始位置;消費者缺少 docs/ 副本並不代表缺少政策,且不得觸發重複歸檔。絕不要在目標儲存庫中複製或憑空創造第二份政策。

Ao —— 分支,已同步

本質:操作系。 將分支與其基礎分支進行協調是 Git 端的操作,處理的不僅僅是你自己的成果,無論該 diff 的作者是哪種本質。

將基礎分支置於我之下,僅解決純文字衝突,然後將任何涉及意義的內容交給我。

Ao 是 $murasaki 的第一步、$aka 的第三步, 也是 $en 在 PR 落後時執行的步驟。它也可以獨立呼叫。 其整個紀律僅包含一個區別——文字 vs 意義——以及一個拒絕:它絕不推送它所產生的任何東西。


0. 獨立入口——已是完整的

Ao 已經直接讀取 checkout 內容,而非由呼叫者傳入。 § 2 在進行任何操作前先讀取狀態,§ 3 根據是否有任何內容已發佈來決定 rebase 或 merge,而 from <base> 會將基礎分支攜帶在 nen/workflow.json → branch.base 之下。docs/STANDALONE-ENTRY.md § 4 將其列為已是完整的;適用的條款為 P1 —— 若無複合技能執行它,則由 $ten ao 負責同步此階段的先決條件(ten § 6)——以及基礎分支需對解析至已擷取的 origin/ 參照,§ 3 已要求此行為。

獨立呼叫不影響 § 6 的任何規定:一個 語意 衝突是一個 G5 停止點,需顯示雙方內容,且絕不能透過選擇讓執行繼續進行的那一方來解決。Ao 仍然絕不推送、絕不強制推送,也絕不改寫已存在於遠端的提交。

交回控制權。 在有線執行流程中的下一步:需要分支保持最新的呼叫者—— $murasaki、$aka 或 $en。未推送任何內容,且在這次同步之前取得的證據, 若任何受追蹤的路徑有所變動,則已失效。


1. 呼叫方式

$ao [from <base>]
nen parse ao --grammar "from [<base>]" --line "<呼叫字串,去掉 $ao 前綴>"

已在 v0.3.0 版本驗證(docs/ab/ao.md § 2.1):from release/1.4 → base: release/1.4;單純的 from 會解析為子句缺失,退出碼 0。子句錨定在一個字面值之後,原因記錄在 $spiritual-message § 1。

若無子句,基礎分支預設為 nen/workflow.json → branch.base,預設值為 main,當該鍵值或 檔案不存在時。在擷取之前大聲說出基礎分支的名稱——一個靜默地同步到錯誤基礎分支的分支, 其產生的 diff 之後沒有人能讀懂。

缺少的引數或設定項目會直接被詢問並設定(missing-argument、 missing-configuration;WORKFLOW.md § 4 詢問、設定、繼續)。

2. 先讀取 checkout 狀態

nen wc classify --repo <路徑> --base <基礎分支> --json

已在 v0.3.0 驗證(docs/ab/ao.md § 2.2),--json 攜帶此技能在進行任何操作前所需的 確切資訊:state.branch、state.isTrunk、state.dirty、state.aheadOfBase、 state.existingCommitSubjects[]、state.uncommittedPaths[]。

nen wc classify 情況ao 的行為
must-move —— 在主要分支上拒絕。 沒有什麼需要同步的:主要分支 就是 基礎分支。告知此情況並將控制權交給 $breath 或 $tensho,視情況而定
on-branch-dirty拒絕,並列出該指令列出的未提交路徑。 對未提交的變更進行 merge 或 rebase,要麼會被 Git 直接拒絕,要麼會將變更埋藏在一場無人能釐清的衝突中。請提交它($kokusen)或深思熟慮後 stash,然後重新執行
on-branch-clean繼續執行 § 3

nen wc classify 不是衝突檢測器,而一個未完成的 merge 會被視為一般髒資料——已在 v0.3.0 驗證(docs/ab/ao.md § 2.2)。 在未解決的 merge 內部執行時,它會回報 on-branch-dirty,並將衝突路徑計入「未提交路徑」,對進行中的 merge 隻字未提。因此在分類之前先詢問 Git,絕不要直接讀取磁碟上的 .git/(在連結工作目錄中 .git 是一個檔案):git rev-parse -q --verify MERGE_HEAD 解析成功,或 git rebase --show-current-patch 退出碼為 0(一個因衝突或 edit 而暫停的 rebase),表示先前的操作在此停止,這次執行會回報該狀態且不啟動任何新操作——絕不在其之上執行第二次操作,也絕不從此處重新執行 wc catch-up,這會以 Git 的預設訊息提交一個 merge 且未經重新驗證的建構(§ 5)。探測的其他結果:1(一個暫停且無目前修補程式的 rebase,處於 break 或失敗的 exec)不屬於 ao 的範疇,因此執行流程會回報它並附上 Git 自己的 --continue / --abort 提示,然後就此停止,絕不依賴 catch-up 來拒絕它,因為一個仍執行 nen 0.18.0 或 0.18.1 的主機不會這樣做(zheref/hatsu#182);128 表示沒有 rebase,因此進行分類;其他任何情況則留給 § 3 的「未回答探測」行處理。

3. 決定操作——rebase 或 merge,由是否有內容已發佈來決定

當此分支上沒有任何內容已發佈時進行 rebase。當有內容已發佈時進行 merge。 改寫一個 其他人可能已經擷取過的提交,是唯一會讓他人付出額外工作的 Git 操作, 本地層級絕不這麼做。

nen wc catch-up --repo <路徑> --base <基礎分支> [--strategy rebase|merge|auto] [--dry-run] [--json]

該指令會先擷取,然後決定,而決定是依據 origin 現在說的話, 絕不依據這個 checkout 上次聽到的資訊。auto(ao 傳入的參數)會在分支上沒有任何內容存在於 @{upstream} 時執行 rebase,否則執行 merge;--strategy rebase|merge 僅在呼叫者有理由時指定其中一種,且必須明確說出理由。退出碼 0 會回報 {base, strategy, before, after, behindBefore, aheadBefore, noOp},而一個 noOp: true 仍然會連同兩個 SHA 一併回報,絕不會回報成「無事可做」。其他所有退出碼,根據 nen v0.20.0 定義:

退出碼意義ao 的行為
1,且帶有 conflicted[] 非空的報告發生衝突;會列印中止行§ 4 接手處理
3,且報告中的 conflicted[] 路徑全部位於 nen/contract.json 的頂層 mechanical 區塊(v0.20.0)發生一個由 nen 分類為 manifest、changelog 或 mirror 的衝突;會列印中止行以及每個類別的指令,但皆不執行§ 4 接手處理,並引用 nen 的類別與指令作為證據;§ 4 仍然會自己分類每個路徑
1,但無報告擷取、狀態或 HEAD 讀取失敗,rebase 或 merge 失敗但無衝突,或 --continue 失敗停止:逐字引用 nen 的 stderr 訊息,說明樹的狀態與 Git 留下的狀態一致;絕不執行 § 4
2,一個暫停於 break 或失敗的 exec 的 rebase(v0.18.2)不是 ao 的 rebase引用 nen 的訊息,該訊息會提示 git rebase --continue / --abort;絕不繼續或中止它
2,一個 Git 未回應的 rebase 探測(v0.18.2)可能根本沒有 rebase引用 nen 的訊息並停止,不指定任何 git 指令
2,其他任何情況在進行任何修改前的拒絕:髒的工作目錄、--base 被 Git 拒絕、--strategy 與進行中的操作不符、Git 版本過舊不支援 --end-of-options、找不到儲存庫根目錄逐字引用它並停止
任何其他退出碼未預期停止並引用它
  • strategy: merge → 分支存在於遠端 → 將基礎分支 merge 進來。
  • strategy: rebase → 無任何內容已發佈 → 將分支重播到 origin/<base> 之上。

一個沒有東西可 rebase 的 rebase 仍然會改寫 SHA,這是預期的行為。 已在實際環境驗證: 一個已經直接位於 origin/<base> 頂端的分支回應了「成功 rebase 並更新」,並將 HEAD 從 c8826c7 移動到 84b0444——相同的樹、相同的訊息、新的提交 物件,因為 rebase 重播了提交,而不是發現沒有必要。在這裡且僅在這裡是無害的:§ 3 僅對未發佈的分支執行 rebase,因此被改寫的提交是沒有人擷取過的。但不能將其回報為「無事可做」—— § 7 會要求基礎分支及其解析後的 SHA,而一個在 HEAD 已變動的情況下說「沒有變更」的執行, 會讓之後所有對舊 SHA 的引用都默默錯誤。

用一行話從其報告中說出該指令選擇了哪個操作以及原因,並指出是哪次更新: 「已擷取 origin;分支存在於 @{upstream},因此使用 strategy: merge,而非 rebase。」

已於 nen 0.5 版本退役,並於 0.13 版本被取代。 Merge 部分曾經透過 nen pr cascade-main --repo <路徑> --trunk <基礎分支> --no-push(已在 v0.5.0 版本驗證,docs/ab/ao.md § 已在 nen 0.5 版本退役)執行,而 rebase 部分在任何旗標下都沒有對應的指令。兩者現在皆由 nen wc catch-up 負責, 且該指令絕不推送。cascade-main 保留其在 izanami 自動化表格中的 mutating 行, 包含 --no-push,但已不再是 ao 所呼叫的內容。

4. 在解決任何衝突路徑之前,先分類所有衝突路徑

git -C <路徑> status --porcelain                    # 衝突行
git -C <路徑> diff --name-only --diff-filter=U      # 衝突路徑,單獨列出
git -C <路徑> ls-files -u                           # 每個路徑有哪些階段

已在針對包含每種情況的建構 fixture 上驗證(docs/ab/ao.md § 2.4):

Porcelain種類存在的階段
UU雙方修改 —— 雙方都變更了同一個檔案1 基礎, 2 我們的, 3 他們的
AA新增-新增 —— 雙方都建立了相同的路徑2 我們的, 3 他們的;無基礎
UD / DU刪除-修改 —— 一方刪除了另一方編輯的內容僅存活的側
DD, AU, UA較罕見的形式;用相同表格分類並命名如 Git 所回報

nen wc catch-up --json 在退出碼 1 或 3 時攜帶 conflicted[],每個未合併路徑一個條目,包含 ours 和 theirs 的逐字內容(並且,從 nen.wc.catch-up/v0.2 開始,包含 nen 的 class)。但它不包含 種類,因此上方的 porcelain 表格 仍需用於讀取該資訊,而 § 6 會完全按照指令列印的內容顯示 ours/theirs。該指令自己的 規則是值得在適用之處引用的:它絕不選擇立場。

在解決任何路徑之前先分類所有路徑,並列印表格。 一個解決了前三條路徑卻發現第四條是語意衝突的執行流程, 已經在第一個三個路徑上耗費了維護者的信任。

5. 機械衝突,還是語意衝突

機械衝突 —— 解決方案是確定的,由儲存庫決定,而非由任何人選擇:

  • 一個雙方都進行了追加的僅追加區域(例如 import 區塊、.gitignore、changelog 列表)—— 保留兩者,按照檔案自身慣例的順序排列;
  • 一個由儲存庫重新產生的生成檔案或鎖定檔案(例如 lockfile、snapshot、mirror)——採用 基礎分支的版本並重新產生(Hatsu mirrors:bash scripts/surface_mirror_regenerate.sh), 然後用宣告的建構來證明它(nen shu build;其 退出碼對應表與 $rasengan § 6 相同);
  • 純格式或空白字元的差異,無行為上的不同;
  • 雙方做出的相同變更。

Hatsu 版本碰撞可能在乾淨的同步程式中存活下來: 平行的分支可能選擇了相同的號碼。 在每次同步之後,將兩個實際的 manifest(.claude-plugin/plugin.json 和 .codex-plugin/plugin.json)與最新的基礎分支進行比較。如果分支的版本等於或低於 基礎分支的版本,請在準備就緒前將兩者重新提升至嚴格高於該基礎分支的相同版本;同時更新 此工作現有 changelog.d/ 片段中的版本行。這是 Hatsu 的編寫工作,不是自動的 nen wc catch-up 重新提升。在下一個版本決定後,執行 bash scripts/plugin_version_refs.sh --from <舊版> --to <新版> --body-file <本機草稿> 來列出 受追蹤的參考和草稿行。加入 --write 來改寫宣告的受追蹤形式以及明確草稿行上的每個舊版本精確 token; 閱讀列印出的 review 行,這些行保持不變。 該輔助工具絕不會選擇版本、改寫產生的 mirror 或編輯遠端 PR。使用上述指令重新產生 mirrors,然後重新推導那些因同步而失效的正文事實:

正文事實同步後重新推導
外掛版本在提交版本提升後執行 git show HEAD:.claude-plugin/plugin.json 和 git show HEAD:.codex-plugin/plugin.json
HEAD 和基礎分支提交在最終提交和擷取後執行 git rev-parse HEAD origin/<基礎分支>
相對於基礎分支測量的數據(位元組、diff、覆蓋率、審查證據)針對新的基礎分支和最終 HEAD 重新測量
Drive 行中的 PR 編號gh pr list --repo <擁有者/名稱> --state open --head <分支> --json number,url;使用回傳的當前 PR
Changelog 片段先決條件行針對同步後的依賴合約和此工作的實際先決條件重新測量

檢視更新後的本機草稿,然後使用 nen pr edit-body 作為遠端寫入器。 針對新的基礎分支重新執行版本提升檢查;原始的基準點無法證明此比較。

語意衝突 —— 任何需要人來決定偏好哪個意圖的情況。UD/DU 永遠是語意衝突: 「已刪除」和「仍在編輯中」是兩種關於事物是否存在的信念, 沒有合併演算法可以持有這種信念。一個雙方都改變了相同行為的 UU 也是如此, 以及一個兩個檔案並非同一檔案的 AA。

教科書案例,已在實際環境驗證(docs/ab/ao.md § 2.4): 一個 DU 合併是

Individual skills in this repo

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

相關技能