Community程式設計與開發github.com

roselletabuena/git-衝突解決

正確解決 Git 合併、變基、揀選、還原或暫存彈出的衝突——理解每方的意圖,結合兩者而非只選一方,檢查結果是否可建置,並繼續操作。

git-衝突解決 是什麼?

此技能旨在協助開發者或 AI 代理以安全且正確的方式解決 Git 衝突。它不僅僅是消除 `<<<<<<<` 標記,而是要求深入理解每方修改的意圖,並在解決方案中保留雙方的貢獻。技能涵蓋合併、變基(rebase)、揀選(cherry-pick)、還原(revert)和暫存彈出(stash pop)等操作,並特別警示在變基過程中 `--ours` 和 `--theirs` 的意義與合併時相反,避免錯誤使用。其核心工作流程包含:確認當前操作類型、使用 `git diff` 和 `git log` 查看衝突全貌(含共同祖先)、以「結合」而非「捨棄」為原則進行解決、驗證建置與測試、以及正確使用 `--continue` 繼續操作。此外,技能也提供處理生成檔案、二進位檔案、多次衝突的進階指引(如啟用 `rerere`),並強調在商業邏輯衝突或無法解釋修改原因時應暫停並諮詢使用者,確保不會因盲目採用單方而丟失同事的工作成果。

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

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

在你喜歡的 AI 中提問

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

說明文件

解決衝突

衝突意味著 Git 發現兩處對同一位置的修改,並且拒絕猜測。解決方案是關於意圖的決定,而安全的預設做法是保留雙方的意圖。透過刪除一方來解決問題只會讓標記消失,但問題變得更糟:建置仍然通過,但同事的修改已經消失。

1. 確認你處於哪個操作——ours 和 theirs 取決於此

git status                  # "rebase in progress" / "You have unmerged paths" / "cherry-pick in progress"
操作--ours / HEAD 側--theirs 側
merge你所在的分支被合併進來的分支
rebase你正在變基到的分支(上游)你正在重播的自己的提交
cherry-pick、revert、stash pop你所在的分支正在被應用的提交

變基(rebase)那一行是陷阱。在 git rebase main 期間,「ours」是 main,而「theirs」是使用者自己的工作,這與合併相反。在變基過程中執行 git checkout --theirs file 會保留你的修改,而丟棄上游的修改。在使用任一標誌之前,請重新閱讀此表格。

2. 查看衝突全貌,而不僅僅是標記

git diff --name-only --diff-filter=U        # 未合併的檔案
git status                                   # 類型:兩者皆修改 / 我們刪除 / 兩者皆新增
git config merge.conflictStyle zdiff3        # 也顯示共同祖先(git 2.35+;舊版本使用 diff3)
git checkout --conflict=zdiff3 -- <file>     # 重新渲染單一檔案,顯示基礎版本(會丟棄已對該檔案進行的編輯)

有了基礎版本可見之後,標記有三個部分:你的側、原始、他們的側。當你能看到每方從什麼變成了什麼時,大多數看似衝突的情況就會變得明顯。

要了解每方為何修改:

git log --merge -p -- <file>                 # 從雙方觸及該檔案的提交(合併或變基進行中)
git show REBASE_HEAD                          # 在變基期間:正在重播的提交
git show :1:<file>  :2:<file>  :3:<file>     # 基礎、「ours」、「theirs」作為完整檔案

閱讀提交訊息。如果你無法用一句話解釋每方的修改,那麼你做出的解決方案就是猜測。

3. 透過結合來解決

  • 雙方因不同原因編輯了同一行 - 寫下能同時完成兩件事的版本。例如,一方新增了 tax_rate 參數,另一方新增了 discount 參數:解決後的函式應同時接受兩者並同時應用。
  • 一方移動或重新命名,另一方編輯 - 在新位置上應用編輯。不要同時保留舊檔案和新檔案。
  • 一方刪除,另一方修改 - 這是真正的決策,而非機械性的操作。找出被刪除的原因;如果修改仍然重要,則必須繼承過來,否則刪除獲勝。如果不清楚,請詢問使用者。
  • 產生的檔案和鎖定檔案(package-lock.json、poetry.lock、go.sum、快照、編譯輸出) - 不要手動合併。任選一方,然後使用工具重新產生(npm install、poetry lock、go mod tidy、快照指令)。
  • 二進位檔案 - 無法合併;有意識地選擇一方(注意上方的表格)並說明。

文字可以乾淨地合併但仍然錯誤:如果一方重新命名了函式而另一方對舊名稱新增了呼叫,Git 不會看到衝突。這就是為什麼步驟 4 包含建置。

全面性標誌隱藏了決策。 git checkout --ours .、-X ours / -X theirs(這會將每個衝突塊解析為向一方傾斜)以及 -s ours(這會完全丟棄另一個分支)都在未經審視的情況下丟棄了工作。僅在使用者明確要求且知道正在丟棄什麼時才使用它們。

4. 驗證,然後繼續

git diff --check                              # 檢查遺留的衝突標記和空白錯誤
grep -rnE '^(<<<<<<<|=======|>>>>>>>)' .      # 雙重保險,針對 git diff-check 不會檢查的檔案
# 建置並執行最快相關的測試
git add <resolved-files>

使用匹配的指令繼續。在會開啟編輯器的情況下加上 GIT_EDITOR=true,因為編輯器會在非互動式 shell 中卡住:

git merge --continue            # 或:git commit --no-edit
GIT_EDITOR=true git rebase --continue
GIT_EDITOR=true git cherry-pick --continue

變基會逐個提交重播,因此它可能會在下一個提交時再次停止。重複此循環。在衝突出現的最早提交處解決;後續的提交可能是同一個衝突在重播。

如果解決後測試失敗,失敗就是關於解決方案的資訊。不要忽略它繼續,也不要透過編輯測試來「修復」它,除非你知道測試正在保護哪一方的行為。

5. 何時停止並詢問

在以下情況下停止而非猜測:

  • 雙方以不相容的方式更改了商業邏輯(兩種不同的稅務規則、兩種不同的重試策略);
  • 你無法用一句話說明每方為何進行修改;
  • 解決方案導致測試失敗且原因不明。

陳述兩個意圖,說明你會選擇什麼以及原因,然後讓使用者決定。在 --continue 之前的一切都是可逆的:git merge --abort、git rebase --abort、git cherry-pick --abort 會將樹狀結構恢復到開始時的狀態。

重複衝突

對於長時間的變基,其中相同的衝突反覆出現,或者一個分支反覆被合併然後放棄:

git config rerere.enabled true      # 記錄每個解決方案並在下次重播它
git rerere diff                      # 它所記錄的內容
git rerere forget-path <file>        # 丟棄一個記錄錯誤的解決方案

rerere 會不經詢問地重新應用解決方案,因此錯誤的解決方案會傳播開來。在經過 rerere 解決的衝突之後,仍然要檢查結果。

當一個分支存在時間長且遠遠落後時,合併主幹一次通常只會導致一輪衝突;而變基會逐個提交重播衝突。這個選擇在使用者。

護欄

  • 永遠不要為了讓衝突消失而全面採用一方。 另一方是某人的工作。
  • 不要僅僅因為費時就中止使用者要求你完成的變基;只有在發生錯誤時才中止,並說明原因。
  • 未經要求不要推送解決方案。變基需要強制推送;請遵循 git-safe-history-rewrite。
  • 報告每個衝突的檔案:雙方做了什麼、你保留了什麼,以及任何你無法驗證的事項。

Individual skills in this repo

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

相關技能