git 入門教程之衝突合併

雪之夢技術驛站發表於2019-03-20

如果足夠幸運的話,團隊成員互不影響,彼此相安無事,大家各自基於 master 分支的某個 commit 建立自己的分支,平時在分支上獨立工作,等到一段時間後再合併 mergemaster 分支,這樣一樣 master 作為各個功能的集大成者,最終完成專案.

然而事情總不是一帆風順的,團隊協作時由於意見不同,遇到衝突簡直是家常便飯,既然無法迴避衝突,當衝突發生時如何應該呢?

背景

基於 master 分支上的某個 commit ,新功能由此繼續開發:

echo "git commit c1" >> test.txt
$ git add test.txt
$ git commit -m "git commit c1"
複製程式碼

git-commit-c1.png

新功能分支命名為 feature ,使用git checkout -b <name> 建立分支並切換:

$git checkout -b feature
Switched to a new branch 'feature'
$ 
複製程式碼

git-checkout-feature.png

在新功能 feature 分支上開發新功能,並提交:

$ echo "git commit c2" >> test.txt
$ git add test.txt
$ git commit -m "git commit c2"
[feature 0fe95f8] git commit c2
 1 file changed, 1 insertion(+)
$ 
複製程式碼

git-commit-c2.png

無論新功能 feature 是否開發完畢,團隊的其他成員均有可能處於 master 分支並做相應更改:

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 3 commits.
  (use "git push" to publish your local commits)
複製程式碼

git-checkout-master-c1.png

其他成員對新功能有著自己的看法,於是也提交了版本,由於我們之前提交的是 git commit c2,而此時master 分支提交的是git commit c3,顯然我們兩個人的意見不一致!

$ echo "git commit c3" >> test.txt
$ git add test.txt
$ git commit -m "git commit c3"
[master 0949cc3] git commit c3
 1 file changed, 1 insertion(+)
$ 
複製程式碼

git-commit-c3.png

正在此時,feature 分支的新功能已開發完畢並主動切換回 master 分支,準備合併 feature 分支.

# 合併 feature 分支
$ git merge feature
Auto-merging test.txt
CONFLICT (content): Merge conflict in test.txt
Automatic merge failed; fix conflicts and then commit the result.
$
複製程式碼

由於專案成員溝通不暢或者意見不一致,導致了程式碼衝突,git 作為版本控制系統,自然無法解決這類問題,總不能擅自做主拋棄後來的更改吧或者拋棄分支更改?所以 git 只負責丟擲問題,等待我們程式設計師去解決問題.

既然是人的問題,那我們看一下我們到底是哪裡不一致,為什麼會產生衝突?

# 檢視狀態
$ git status
On branch master
Your branch is ahead of 'origin/master' by 4 commits.
  (use "git push" to publish your local commits)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:   test.txt

no changes added to commit (use "git add" and/or "git commit -a")
# 比較差異
$ git diff 
diff --cc test.txt
index 6e00f87,0f95fd7..0000000
--- a/test.txt
+++ b/test.txt
@@@ -3,4 -3,4 +3,8 @@@ see https://snowdreams1006.github.io/gi
  learn git branch
  see https://snowdreams1006.github.io/git/usage/branch-overview.html
  git commit c1
++<<<<<<< HEAD
 +git commit c3
++=======
+ git commit c2
++>>>>>>> feature
複製程式碼

和我們預期一樣,test.txt 檔案產生了衝突,當前 HEAD 指向的提交即 master 分支是 git commit c3 ,而 feature 分支是 git commit c2,對於同一個檔案的同一行內容發生不同的更改,git 不知道也不應該知道如何處理.

# 檢視內容
$ cat test.txt
add test.txt
see https://snowdreams1006.github.io/git/usage/remote-repository.html
learn git branch
see https://snowdreams1006.github.io/git/usage/branch-overview.html
git commit c1
<<<<<<< HEAD
git commit c3
=======
git commit c2
>>>>>>> feature
複製程式碼

git<<<<<<< 標記一個分支衝突開始,======= 標記分支分割線,>>>>>>> 標記另一個分支結束.

經過沖突雙方的討論後,彼此間達成妥協,決定修改成git commit c2 and c3 ,修改後繼續提交:

# 編輯衝突檔案,按照協商一致的內容修改檔案
$ vim test.txt
# 將衝突內容更改為 git commit c2 and c3
$ cat test.txt
add test.txt
see https://snowdreams1006.github.io/git/usage/remote-repository.html
learn git branch
see https://snowdreams1006.github.io/git/usage/branch-overview.html
git commit c1
git commit c2 and c3
$ git add test.txt
$ git commit -m "fix conflict"
[master 3b8f434] fix conflict
複製程式碼

衝突已經解決,現在回顧一下提交歷史,使用git log --graph 圖形化展示提交歷史:

# 檢視提交日誌
$ git log --pretty=oneline --graph
*   3b8f434013caa8c27fade4c59d7aa2ee2c079636 (HEAD -> master) fix conflict
|\  
| * 0fe95f871b371834d30ea17faa82f84b7d67672b (feature) git commit c2
* | 0949cc319e099d554795d03c69ee38923af00d6c git commit c3
|/  
* 5c482cd9965b9dfd4f273b43b240ed7db66167a8 git commit c1
* 413a4d1d2aab5ab85b6097d4b9f81cb5601c3b26 see https://snowdreams1006.github.io/git/usage/branch-overview.html
* 9c30e50248b773e38b032477a859e87abe7c1bb0 learn git branch
* b3d8193bbcb9f76c47e831e3e212f2405ae09f93 (origin/master, origin/HEAD) see https://snowdreams1006.github.io/git/usage/remote-repository.html
* 8e625640348a47ac922409a1ecb4c844385582aa add test.txt
* 9b196aab5bc87eeb11709c9eef35fca283e05c61 Initial commit
$ 
複製程式碼

git-merge-with-conflict.png

最後,刪除新功能分支 feature ,不用的分支及時清理乾淨,需要時再建立分支.

$ git branch -d feature
複製程式碼

小結

  • 無法杜絕衝突的發生,程式碼上的衝突本質上是人為因素造成的衝突.
  • 解決衝突需要有關雙方協商解決,不可能獨自解決衝突,除非你拋棄自我,完全以對方為準.
  • 使用 git log --graph 命令可以圖表化檢視提交歷史,抑或 git log --pretty=oneline --graph

相關文章