如何解決 Git 合併衝突教程

Git 合併衝突是什麼?

Git 版本控制系統是關於團隊合作和貢獻項目的。開發人員通常在隔離的分支上工作,當他們完成後,會將更改與主分支合併。這種團隊合作方式在找出錯誤方面非常高效和有效。有時多位開發人員在相同的代碼行上工作,當他們試圖合併更改時,就會產生衝突。


Git 衝突的簡單示例

上圖完美展示了 Git 合併衝突的一般例子。主分支中有一個文件,內容為「HELLO, WORLD!」。用戶 abid 對主分支進行分叉,將文字更改為「HELLO, CAT!」。當 abid 正在進行更改時,原始主分支也已被修改為「HELLO, DOG!」。合併這些分支將會導致合併衝突問題並暫停process。

`git merge` 命令的主要工作是結合兩個分支並自動解決衝突。然而,有時會出現衝突,兩個人更改了同一行代碼,或者移除了另一個開發者正在處理的關鍵文件。Git 會標記這些更改並停止合併過程。在這種情況下,衝突並未自動解決;而是開發者必須手動進行更改或使用工具來解決衝突。

合併類型

Git 合併和重置是將目標分支的提交整合到源分支的兩種方法。此外,Git 合併 performs either a fast-forward or a no-fast-forward merge。如果目標分支的頭存在於源分支中,那麼默認合併類型將為快進合併,如果缺失,則為無快進合併。Git 重置是另一种合併,它重新排序目標分支的提交歷史。

快進合併

Git 預設的合併會使用快速前進(fast-forward)來整合目標分支中缺少的提交。例如,它用於透過 pull 命令從遠程伺服器更新本地分支。快速前進不會引發合併衝突問題,因為如果目標分支的頭在源分支中不存在,Git 便不會應用此操作。

非快速前進合併

非快速前進合併也被稱為三向或真正合併。它通過整合源分支和目標分支中的更改,在目標分支上創建一個新的提交。這些更改是在兩個分支最後共同的提交之後混合的。在我們的情況下,是在 C 之後。這種合併會在源分支與目標分支發生爭議時引發 Git 合併衝突。在上述圖表 中,合併提交(X)是通過整合源分支和目標分支創建的,其中 K 和 E 是合併提交的父母。

變基 

Git 的變基與其他類型有些不同。它改變了目標分支提交歷史的順序。變基以這種方式整合源分支:使目標分支包含來自源分支的所有更改,然後是自最後共同提交以來的所有目標分支提交。在我們的情況下,最後共同提交是 C,而 D 和 E 來自源分支。K* 提交與 K 相同,但提交 id 不同。它不是連接到 C,而是連接到 E。與非快速前進合併類似,如果源分支和目標分支之間存在兼容性問題,Git 將在完成變基之前提出問題以解決衝突。

Git 合併衝突的類型

Git 合併衝突有两种:合併開始時和合併過程中 – Atlassian。在這一節,我們將學習兩者的類型以及解決每種情況的方法。

合併開始時

如果工作目錄或暂存區有更改,Git 合併將從開始時失敗。它會在開始時失敗,以防止更改被 incoming merge commits 覆寫。這是由於與本地更改發生衝突,而不是其他分支或開發者。為了穩定本地狀態,您可以使用git stashgit commitgit checkoutgit reset等命令。

合併過程中

合併過程中失敗意味著源分支和目標分支之間存在衝突,多個開發者已經修改了同一個文件。如果自動合併失敗,Git 將要求您手動解決問題。您也可以使用第三方工具來幫助您視覺化並整合更改。

解決 Git 合併衝突的命令

在這一節,我們將學習各種原生命令來視覺化並解決 Git 合併衝突。

常用命令

Git status 是最常使用的命令,用於顯示修改過文件的狀態、暫存區和提交。在合併過程中,它用於識別發生衝突的文件。

git status

Git log 使用 –merge 參數會產生與源分支發生衝突的提交列表。

git log --merge

預設情況下,git diff 選項會顯示未提交的變更與之前提交之間的差異。Git diff 用於比較分支、提交和文件,對於防止未來的合併衝突很有用。

git diff

合併一開始失敗的命令

checkout 命令用於撤銷變更或切換到新的或舊的分支。

git checkout

Git reset 用於撤銷工作目錄和暫存區的變更。

git reset --mixed

合併過程中出現衝突的命令

–abort 參數將停止合併過程,並將變更恢復到合併開始前的原始狀態。

git merge --abort

Git reset 通常在合併過程中使用,以將發生衝突的文件恢復到原始狀態。

git reset

解決刪除-修改文件衝突

如果你在當前分支刪除了文件,而別人在另一個分支修改了它,則會發生 Git 衝突。在這種情況下,你可以選擇添加文件並提交。

git add <filename>

或者你可以刪除文件並提交。

git rm <filename>

視覺合併工具

合併工具是 indentifying 和解決各種合併衝突的友善視覺工具。有些工具支援額外的功能,如比較變更、Git 操作和專案及倉庫管理。Git 合併工具分為兩種類型:僅終端機和基於 GUI 的。基於終端機的工具在 PowerShell 或 Bash 中打開,而基於 GUI 的工具在視窗環境中打開。

要檢查已安裝和有效的工具清單,使用:

git mergetool --tool-help

該清單包含了所有可以安裝並與 git 命令整合的有效工具。

例如,我們預設安裝了 vim 和 nvim,如果你想查看未提交的文件與先前提交之間的差異,輸入:

git difftool --tool=vimdiff3

vimdiff3 工具會標記變更,並讓你在終端機內比較提交。

在 Vimdiff3 中比較同一文件的兩個版本

Meld

Meld 是一個免費且開源的工具有著將解決合併衝突提升到另一個層次的功能。要與 Git 整合,你需要先從官方網站下載並安裝設定。接著,將其添加到全域配置中,這樣 Git 將默認啟動 Meld 來解決衝突。

以下的配置命令僅適用於 Windows 用戶。你需要做的唯一改變是替換為 Mac 或 Linux 上安裝的 Meld 檔案的路徑。

git config --global merge.tool meld git config --global mergetool.meld.path "C:/Program Files (x86)/Meld/Meld.exe" git config --global diff.tool meld git config --global difftool.meld.path "C:/Program Files (x86)/Meld/Meld.exe"

設定預設值後,您可以在Git本地目錄中輸入git difftool以啟動Windows版本的Meld,或者您可以使用git mergetool來解決合併衝突,如下所示。

使用Meld解決合併衝突

VSCode

VSCode提供了解決合併衝突最佳且最受欢迎的方法。當Git無法自動合併文件時,VSCode會標記出衝突的代码並給您四個選項:接受當前更改、接受進入更改、接受兩邊更改以及比較更改。您可以使用這些選項來清理您的文件並解決所有待解決問題。

使用VSCode解決合併衝突

如果您正在尋找一個完整的Git操作解決方案,嘗試GitKraken。它附帶一個免费的客戶端、VSCode擴展,並提供一個內置的工具來解決合併衝突。

如何解決Git合併衝突

在本節中,我們將學習如何創建一個 Git 合併衝突,然後解決它。本教程分為兩部分。第一部分,我們將學習如何在本地解決 Git 衝突;第二部分是關於如何使用遠端伺服器(GitHub)解決衝突。

本地合併衝突

創建合併衝突將幫助我們學習這些問題是如何產生的。我們可以用創意的方法來解決這些問題,甚至防止它們未來發生。

我們現在將創建一個包含單一文件的 Git 儲存庫並創建我們的第一次提交以開始。

  1. 創建一個名為 datacamp 的資料夾。
  2. 切換目錄到 datacamp。
  3. 初始化 Git。
  4. 創建一個帶有給定標題的 README.md 文件。
  5. 暫存並提交文件中的更改。
mkdir datacamp cd datacamp git init echo "# How to Resolve Git Merge Conflict" > README.md git add README.md git commit -m "first commit" >>> [main (root-commit) 8199ea2] first commit >>> 1 file changed, 1 insertion(+) >>> create mode 100644 README.md

接下來,我們將創建一個新的分支 readme 並將標題從「..Git Merge..」更改為「..Git..」。添加文件並使用 -am 參數創建提交。

git checkout -b readme echo "# How to Resolve Git Conflict" > README.md git commit -am "new branch conflict added" >>> [readme 155f694] new branch conflict added >>> 1 file changed, 1 insertion(+), 1 deletion(-)

返回主分支,使用 >> 向 README.md 文件添加新行。通過保存更改並創建提交,我們成功地在同一文件的兩個版本之間形成了一個衝突。

git checkout main echo "New change in base branch" >> README.md git commit -am " a line added to base branch Readme file" >>> [main f1f1874] a line added to base branch Readme file >>> 1 file changed, 1 insertion(+)

如我們所見,在合併 readme 分支時,Git 提示一條消息,表示自動合併失敗,我們必須手動進行更改,然後提交結果。

git merge readme >>> Auto-merging README.md >>> CONFLICT (content): Merge conflict in README.md >>> Automatic merge failed; fix conflicts and then commit the result.

我們將手動解決問題,通過開啟和在Notepad中編輯文件。下面的圖案顯示一個帶有HEAD的箭頭、分隔線和一個不同方向的帶有readme的箭頭。HEAD部分顯示主分支中现有的更改,而readme部分是我們想要合併的分支,它包括一個不同的標題。

手動解決合併衝突

為了解決問題,我們將刪除readme分支部分、箭頭和分隔線。文件最終版本應該看起来干净,如下所示。

衝突已解決

在我們添加文件並創建提交後,合併衝突將被解決。這是解決問題最常見和最簡單的方式。您也可以使用 Integrated Development Environment (IDE) 更快地解決問題。

git commit -am "conflict resolved in file README.md" >>> [main 9994a29] conflict resolved in file README.md

遠端合併衝突

為創建和解決遠端合併衝突,我們需要在GitHub上創建一個倉庫。

在GitHub上創建新倉庫

接下來,將遠程名稱(源頭)與地址添加至倉庫,並使用上游將本地的所有更改推送到遠程主分支。

git remote add origin https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git git push --set-upstream origin main >>> Enumerating objects: 12, done. >>> Counting objects: 100% (12/12), done. >>> Delta compression using up to 4 threads >>> Compressing objects: 100% (6/6), done. >>> Writing objects: 100% (12/12), 998 bytes | 499.00 KiB/s, done. >>> Total 12 (delta 2), reused 0 (delta 0), pack-reused 0 >>> remote: Resolving deltas: 100% (2/2), done. >>> To https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git >>> * [new branch] main -> main >>> branch 'main' set up to track 'origin/main'.

為了創建衝突,我們需要在遠程和本地README.md文件中進行更改。您可以使用GitHub文件編輯器將“..Git merge..”更改為“..Sit-Merge..”,然後提交更改。

在GitHub編輯器中進行更改

然後,在本地倉庫中,將README.md文件更改只有一個簡單標題,並提交更改。

echo "# How to Resolve Merge Conflicts in Git Tutorial" > README.md git commit -am "local branch changes in README.md" >>> [main c677a13] local branch changes in README.md >>> 1 file changed, 1 insertion(+), 4 deletions(-)

最後,將更改推送到遠程服務器。注意Git提出了錯誤,並提供了消除問題的提示。

git push >>> To https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git >>> ! [rejected] main -> main (fetch first) >>> error: failed to push some refs to 'https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git' >>> hint: Updates were rejected because the remote contains work that you do >>> hint: not have locally. This is usually caused by another repository pushing >>> hint: to the same ref. You may want to first integrate the remote changes >>> hint: (e.g., 'git pull ...') before pushing again. >>> hint: See the 'Note about fast-forwards' in 'git push --help' for details.

我們將遵循最簡單的提示,即在推送之前從遠程服務器拉取文件。

拉取文件失敗,因為README.md文件中發生了合併衝突。我們可以手動使用Notepad解決,但這次我們將使用一個視覺工具來幫助我們進行此過程。

git pull >>> remote: Enumerating objects: 5, done. >>> remote: Counting objects: 100% (5/5), done. >>> remote: Compressing objects: 100% (2/2), done. >>> remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 >>> Unpacking objects: 100% (3/3), 681 bytes | 75.00 KiB/s, done. >>> From https://github.com/kingabzpro/DataCamp-Git-Merge-Guide >>> aaf149d..49b7d14 main -> origin/main >>> Auto-merging README.md >>> CONFLICT (content): Merge conflict in README.md >>> Automatic merge failed; fix conflicts and then commit the result.

合併工具Meld將識別衝突的文件,並將它們在Meld GUI應用程序中顯示出來。

git mergetool >>> Merging: >>> README.md >>> Normal merge conflict for 'README.md': >>> {local}: modified file >>> {remote}: modified file

有三個栏目:README_LOCAL_473.md、README.md和README_LOCAL_473.md。如果您認為遠程更改是有效的,請點擊遠程列中的黑色箭頭;如果您希望本地更改保留,請點擊本地列中的黑色箭頭。就是那麼簡單。

透過 merge tool Meld 解決衝突

在進行更改後,儲存檔案並提交。正如您所看到的,將檔案推送到遠端伺服器不會引發合併衝突錯誤。

git commit -am "remote main branch conflict resolved" git push >>> Enumerating objects: 16, done. >>> Counting objects: 100% (16/16), done. >>> Delta compression using up to 4 threads >>> Compressing objects: 100% (6/6), done. >>> Writing objects: 100% (10/10), 1.08 KiB | 550.00 KiB/s, done. >>> Total 10 (delta 2), reused 0 (delta 0), pack-reused 0 >>> remote: Resolving deltas: 100% (2/2), completed with 1 local object. >>> To https://github.com/kingabzpro/DataCamp-Git-Merge-Guide.git >>> 49b7d14..8f5c3aa main -> main

我們成功地解決了本地和遠端合併衝突。這些衝突是數據科學家和機器學習工程師每天都要处理的。為了提升您在 Git 操作方面的技能,請參加 Git 入門 課程。

結論

解決 Git 合併衝突是一個複雜且高风险的任務,因為您可能會通過合併有問題的程式碼而破坏軟體。合併工具提供了一個友善的使用者環境和更安全的偵測及解決合併衝突方式。在這個教學中,我們學習了 Git 衝突为何会发生以及如何解決它們。我們也涵蓋了各種合併和衝突類型、有用的 Git 命令和視覺工具。在最後一部分,我們在本地和遠端倉庫中創建並解決了一個合併衝突。

如果您是 Git 初學者並想知道它是如何工作的,請閱讀:Git 和 GitHub 入門教學

Source:
https://www.datacamp.com/tutorial/how-to-resolve-merge-conflicts-in-git-tutorial