原文: git merge使用不當引發的程式碼丟失血案@blog.23lab.com
背景
幾年前大批量的團隊都在轉用git
,git
的本地庫和分支特性讓程式碼管理的便利性大大增加,也因為本地庫和分支的大批量使用導致了程式碼之間的頻繁merge,我們團隊以前就有遇到過git merge
以後丟程式碼的情況,表現就是某些變更開發A提交了,經過中間以序列的commit
, merge
,push
,最終某些改動沒了,去看commit history,也沒有相應的變更記錄。一次次commit對比以後總是會找到某次的merge
,但是也沒法解釋為什麼會丟,最後為了保險通常會選擇beyond compare全量對比一次確保所有丟失的程式碼找回來,那是一個痛苦而又讓人睡不安穩的解決辦法。
今天又遇到了一次這個問題,這次的更加詭異,在我查為什麼丟的時候,另外一個同事提交程式碼以後竟然丟失的程式碼回來了,我當時腦袋裡面只想到一個詞,那就是: 見鬼。但是冷靜下來想,git這種工具出現真丟程式碼的概率肯定很小,肯定是我們姿勢不對。於是認認真真的研究了我們今天的提交記錄,終於找到答案,就是git merge
後只提交部分程式碼導致的,這裡順帶提一下,git pull
等於git fetch + git merge
,所以git pull
時也需要特別關注本文提到的問題。
驗證過程
為了驗證這個問題,我建了一個全新的git
庫來複現上述問題,根據我們遇到的情況模擬,整個過程我使用三個使用者模擬交叉提交和合並,分別是user1,user2,user3,為了方便我沒有選擇註冊三個號,而是在提交資訊裡面都帶上了名字,可以製造兩次程式碼衝突,並且兩次都進行部分提交。最終的程式碼提交記錄如下:
為了更清晰的看到這個提交過程,我將三個使用者的提交分開來,並根據提交先後做了下面的圖。
程式碼怎麼丟的?
首先,第1步和第2步,user1和user2分別在本地做修改,共同修改了file1
,只有user2對file2
也做了修改,所以如果這兩人的程式碼合併,可以預計合併的時file1
會衝突,file2不會。user2修改完成以後做了一次git push
,這時候git
遠端庫的head應該是指向user2的最後一次提交的。接著user1做git pull
,因為file1被兩個使用者修改了,所以需要合併,接著user1處理完衝突以後只提交了file1
,沒有提交file2
(用命令列需要手動reset
,但是用有些GUI工具是可以選擇只提交file1
的)。此時的庫已經丟失了user2:file2:add line
的那次改動,這就是血案的開始,丟程式碼。可以對比一下 merge前 和merge後 。
程式碼怎麼”恢復”的?
前面只是驗證了程式碼怎麼丟,但是事情沒有結束,我上面提到更詭異的是程式碼恢復了,這又是怎麼回事呢?user1在第3步合完程式碼以後,自己進行了兩次提交,修改了file3和file1,同時,user3加入並本地修改了file1(預計會和user1衝突),接著user1git push
,然後user3 git pull
,這時候會報file1
衝突,同樣,user3犯了user1類似的錯誤,只提交了衝突檔案file1
。按第3步丟程式碼的經驗,file3
的改動肯定是沒有了,驗證以後確實如此,但是再看看file2的變更呢,它竟然又回來了,所以所謂的恢復不是恢復,所謂的丟失沒有丟失,就問你怕不怕。更可怕的是我們再去看file2
的變更歷史。
中間消失和恢復的過程沒有任何變更記錄,這也是之前我們經常查到沒法往下查到地方。從表現上看,是第一次誤操作忽略了這一行的變更,而第二次誤操作,可以理解為是忽略了忽略新增這一行的那一次變更,但是,所有的都沒有記錄。
總結
上面的實驗可能看起來有點繁瑣,總結一下,核心的問題就是不要在git merge
的時候只提交部分程式碼,特別是好多人剛剛用git
的時候,執行完成一次git merge
,發現自己修改了1個檔案,結果git
提示要提交100個,這時候小心謹慎的新同學就會選擇只提交自己的變動吧,殊不知這是在犯大錯啊。除了這種情況,之前還遇到有的新同學會修改git
預設生成的merge資訊,這也是非常不好的習慣,改了以後在查問題的時候沒辦法很容易的看到哪些是merge。所以請注意:
- git merge以後不要部分提交!!
- git merge以後不要部分提交!!
-
git merge以後不要部分提交!!
-
git merge的資訊不要手動改!!
- git merge的資訊不要手動改!!
- git merge的資訊不要手動改!!
文中提到的實驗庫的地址是: find-the-missing-code, 有興趣的童鞋可以自己詳細看一下。