解決合併衝突 (Merge Conflicts)
當兩個分支修改了同一個檔案的同一個部分,Git 無法自動決定要保留哪個版本,就會產生合併衝突。
為什麼會有衝突?
假設 main 和 feature 都修改了 index.js 的第 10 行:
main把它改成const name = "Alice";feature把它改成const name = "Bob";
Git 不知道該用哪一個,所以需要你手動處理。
衝突發生時
當你執行 git merge 遇到衝突:
git merge feature
會看到:
Auto-merging index.js
CONFLICT (content): Merge conflict in index.js
Automatic merge failed; fix conflicts and then commit the result.
此時 Git 會在衝突的檔案中標記衝突的位置。
衝突標記
打開有衝突的檔案,會看到類似這樣的標記:
function greet() {
<<<<<<< HEAD
const name = "Alice";
=======
const name = "Bob";
>>>>>>> feature
console.log("Hello, " + name);
}
說明:
<<<<<<< HEAD:當前分支(你正在合併到的分支)的內容開始=======:分隔線>>>>>>> feature:要合併進來的分支的內容結束
解決衝突
手動編輯
編輯檔案,決定最終要保留什麼:
// 選項一:保留 HEAD 的版本
function greet() {
const name = "Alice";
console.log("Hello, " + name);
}
// 選項二:保留 feature 的版本
function greet() {
const name = "Bob";
console.log("Hello, " + name);
}
// 選項三:結合兩者
function greet() {
const name = "Alice and Bob";
console.log("Hello, " + name);
}
記得刪除衝突標記(<<<<<<<、=======、>>>>>>>)。
完成解決
# 1. 編輯好衝突的檔案
# 2. 標記衝突已解決
git add index.js
# 3. 完成合併
git commit
Git 會自動產生合併 commit 訊息。
使用工具解決衝突
使用 VS Code
VS Code 會自動識別衝突,並提供按鈕:
- Accept Current Change:保留 HEAD 的版本
- Accept Incoming Change:保留合併進來的版本
- Accept Both Changes:保留兩者
- Compare Changes:比較差異
使用 Git mergetool
git mergetool
這會開啟你設定的合併工具(例如 VS Code、Meld、Beyond Compare)。
設定 VS Code 為 mergetool:
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
使用命令列選項
直接選擇一方的版本:
# 使用我們的版本
git checkout --ours index.js
# 使用他們的版本
git checkout --theirs index.js
查看衝突狀態
查看哪些檔案有衝突
git status
輸出:
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: index.js
both modified: style.css
查看衝突差異
git diff
取消合併
如果你想放棄這次合併,回到合併前的狀態:
git merge --abort
複雜衝突情境
檔案被刪除 vs 修改
一方刪除了檔案,另一方修改了它:
CONFLICT (modify/delete): index.js deleted in feature and modified in HEAD.
解決方式:
# 保留檔案
git add index.js
# 或刪除檔案
git rm index.js
檔案被重新命名
一方重新命名了檔案,另一方修改了它:
CONFLICT (rename/delete): old-name.js renamed to new-name.js in HEAD.
二進制檔案衝突
圖片等二進制檔案無法合併內容:
# 保留我們的版本
git checkout --ours image.png
# 保留他們的版本
git checkout --theirs image.png
git add image.png
避免衝突的方法
經常同步:定期
git pull保持和遠端同步小步提交:避免大範圍的修改
溝通協調:團隊成員避免同時修改相同的檔案
模組化程式碼:好的程式架構可以減少多人修改同一檔案的機會
衝突解決策略
策略選項
在合併時可以指定策略:
# 有衝突時優先用我們的版本
git merge -X ours feature
# 有衝突時優先用他們的版本
git merge -X theirs feature
注意:這和 git merge -s ours 不同:
-X ours:有衝突時用我們的,沒衝突的正常合併-s ours:完全忽略對方的變更,只保留我們的
實際範例
完整的衝突解決流程
# 1. 嘗試合併
git merge feature
# CONFLICT (content): Merge conflict in index.js
# 2. 查看狀態
git status
# both modified: index.js
# 3. 打開檔案,手動編輯解決衝突
code index.js
# 4. 標記已解決
git add index.js
# 5. 完成合併
git commit -m "Merge feature branch, resolve conflicts"
# 6. 推送
git push
多個檔案衝突
git merge feature
# CONFLICT: index.js
# CONFLICT: style.css
# CONFLICT: app.js
# 一個一個解決
code index.js
git add index.js
code style.css
git add style.css
code app.js
git add app.js
# 完成
git commit
小技巧
查看衝突的三方版本
# 查看共同祖先的版本
git show :1:index.js
# 查看我們的版本
git show :2:index.js
# 查看他們的版本
git show :3:index.js
重新開始解決衝突
如果解決到一半搞砸了:
# 重置單一檔案
git checkout -m index.js
# 或整個重來
git merge --abort
git merge feature