無(wú)論是數(shù)據(jù)科學(xué)家、算法工程師還是普通開(kāi)發(fā)人員,在每個(gè)團(tuán)隊(duì)協(xié)作開(kāi)發(fā)任務(wù)中,Git都是必不可少的版本控制工具,因此掌握它的基本操作十分有必要。但即便是教程滿天飛的今天,開(kāi)發(fā)人員在使用Git時(shí)也還是會(huì)犯一些不應(yīng)該犯的錯(cuò)誤。本文總結(jié)了其中的幾種常見(jiàn)錯(cuò)誤,希望能對(duì)新手有所幫助。
force push
有時(shí),我們會(huì)需要用force push把commit推送到遠(yuǎn)端倉(cāng)庫(kù)。
假設(shè)有2名開(kāi)發(fā)人員正在合作開(kāi)發(fā)一個(gè)分支
之前開(kāi)發(fā)人員1已經(jīng)完成更改,把代碼push到了遠(yuǎn)程倉(cāng)庫(kù)
現(xiàn)在,開(kāi)發(fā)人員2也完成了更改,正當(dāng)他準(zhǔn)備提交時(shí),他卻發(fā)現(xiàn)自己無(wú)法將代碼推送到遠(yuǎn)程倉(cāng)庫(kù)
由于開(kāi)發(fā)人員2是個(gè)初學(xué)者,他Google了一下,發(fā)現(xiàn)了一個(gè)神奇的命令git push -f,于是進(jìn)行了強(qiáng)制push
之后開(kāi)發(fā)人員1在檢查遠(yuǎn)程倉(cāng)庫(kù)時(shí),發(fā)現(xiàn)自己編寫(xiě)的代碼全消失了
出現(xiàn)這個(gè)問(wèn)題的原因是force push會(huì)覆蓋遠(yuǎn)程倉(cāng)庫(kù)中的代碼,使現(xiàn)有代碼全部丟失。
如果開(kāi)發(fā)人員2想避免這個(gè)問(wèn)題,一種理想方法是他先把開(kāi)發(fā)人員1的更新從遠(yuǎn)程倉(cāng)庫(kù)pull到本地,然后把自己的代碼rebase一下,再進(jìn)行push。這里我們討論的是在同一分支中從遠(yuǎn)程到本地倉(cāng)庫(kù)的rebase。
git push -f這個(gè)命令非常不安全,除非有絕對(duì)的必要,大家最好還是不要用它。它會(huì)把本地分支的提交覆蓋遠(yuǎn)程推送分支的提交,給協(xié)作的同伴帶去不少麻煩,即便是上面的解決方案,它也可能存在一個(gè)時(shí)間差的問(wèn)題,因?yàn)槟悴豢赡軙r(shí)刻掌握同伴的工作進(jìn)展。
所以如果大家都用正確的git工作流,讓每個(gè)開(kāi)發(fā)人員都擁有自己的功能分支,這種情況根本不會(huì)發(fā)生。
Rebase
如果你想把一個(gè)分支的修改合并到當(dāng)前分支,你可以用git rebase。它和git merge的區(qū)別是merge有一個(gè)合并commit的步驟,而rebase是把所有commit都串聯(lián)在一起,讓你本地的分支歷史看起來(lái)像沒(méi)有經(jīng)過(guò)任何合并一樣。
假設(shè)有2名開(kāi)發(fā)人員正在合作開(kāi)發(fā)一個(gè)功能分支
開(kāi)發(fā)人員1率先完成了一系列commit,并將其推送到遠(yuǎn)程功能分支
之后,開(kāi)發(fā)人員2把遠(yuǎn)程功能分支的最新更改pull到本地
開(kāi)發(fā)人員2向本地功能分支添加了一堆commit
這時(shí),他想把本地倉(cāng)庫(kù)的更新重新rebase到遠(yuǎn)程倉(cāng)庫(kù)中,于是他把整個(gè)預(yù)發(fā)分支(release branch)在本地功能分支上rebase了一下。這里我們討論的是在不同分支中從遠(yuǎn)程到本地倉(cāng)庫(kù)的rebase
現(xiàn)在,開(kāi)發(fā)人員2試著把代碼push到遠(yuǎn)程功能分支上,由于提交歷史記錄已更改,這個(gè)操作不被允許,他只能又開(kāi)始用git push -f
最后,當(dāng)開(kāi)發(fā)人員1想從遠(yuǎn)程倉(cāng)庫(kù)提取最新代碼時(shí),由于提交記錄已更改,他被迫需要處理大量代碼沖突問(wèn)題
常規(guī)rebase
開(kāi)發(fā)人員2的操作
如上圖所示,rebase遠(yuǎn)程倉(cāng)庫(kù)會(huì)改變提交歷史記錄,并在其他開(kāi)發(fā)人員嘗試從遠(yuǎn)程倉(cāng)庫(kù)中提取最新代碼時(shí)產(chǎn)生問(wèn)題。處理這種情況的理想方法是始終只rebase本地倉(cāng)庫(kù),本地倉(cāng)庫(kù)中的任何commit都不應(yīng)該被push到遠(yuǎn)程倉(cāng)庫(kù)。
如果別人事先已經(jīng)把commit推送到遠(yuǎn)程功能分支,那么你最好先用pull命令把更新拉到本地,用merge和你的修改合并,因?yàn)閙erge不會(huì)改變提交歷史,而rebase會(huì)。
此外,和上個(gè)問(wèn)題一樣,如果使用正確的git工作流,每個(gè)開(kāi)發(fā)人員都會(huì)有自己的功能分支,這時(shí),開(kāi)發(fā)者在自己的功能分支上進(jìn)行更新并且在遠(yuǎn)程功能分支上做rebase是不會(huì)報(bào)錯(cuò)的,因?yàn)闆](méi)有其他開(kāi)發(fā)人員從同一個(gè)遠(yuǎn)程功能分支中提取代碼。無(wú)論如何,盡量避免重新定義遠(yuǎn)程倉(cāng)庫(kù)。
Rebase是一個(gè)非常強(qiáng)大的功能,使用時(shí)也需謹(jǐn)慎。
amend
git amend命令的作用是修復(fù)最近一次commit,讓你合并你緩存區(qū)的修改和上一次commit,而不是提交一個(gè)新的快照。這里需要注意一點(diǎn),它不是修改最近一次commit,而是整個(gè)替換掉原commit,所以對(duì)Git來(lái)說(shuō)這是一個(gè)新的commit。此外,它還可以用來(lái)編輯上一次的commit描述。
假設(shè)有2名開(kāi)發(fā)人員正在合作開(kāi)發(fā)一個(gè)功能分支
開(kāi)發(fā)人員1率先完成了commit,并將其推送到遠(yuǎn)程功能分支,我們把它稱(chēng)為“old commit”
之后,開(kāi)發(fā)人員2把最新代碼從遠(yuǎn)程功能分支pull到本地功能分支
然后他開(kāi)始處理本地倉(cāng)庫(kù)中的代碼,在這個(gè)過(guò)程中,他沒(méi)有向遠(yuǎn)程倉(cāng)庫(kù)push任何commit
這時(shí)開(kāi)發(fā)人員1突然發(fā)現(xiàn)之前的commit中存在bug,他用amend命令修復(fù)了本地倉(cāng)庫(kù)里的最近一次commit,我們把它稱(chēng)為“new commit”
開(kāi)發(fā)人員1嘗試把這個(gè)新commit重新push到遠(yuǎn)程功能分支,由于提交歷史記錄發(fā)生了變化,這個(gè)操作報(bào)錯(cuò)了,于是他調(diào)用了git push -f
現(xiàn)在,當(dāng)開(kāi)發(fā)人員2想從遠(yuǎn)程功能分支中提取最新代碼時(shí),git會(huì)注意到提交歷史記錄的變化并創(chuàng)建合并的commit。因此當(dāng)他pull到本地后,他會(huì)在本地倉(cāng)庫(kù)里發(fā)現(xiàn)“commit old”和“commit new”,這就破壞了amend這個(gè)操作的意義。
最后,即便開(kāi)發(fā)人員2從遠(yuǎn)程分支到本地分支執(zhí)行rebase,這個(gè)“commit old”還是會(huì)出現(xiàn)在本地倉(cāng)庫(kù)中,它仍然會(huì)作為歷史提交的一部分。
amend commit會(huì)更改提交歷史記錄,所以當(dāng)其他開(kāi)發(fā)人員嘗試從遠(yuǎn)程倉(cāng)庫(kù)提取最新代碼時(shí),修改遠(yuǎn)程倉(cāng)庫(kù)中的commit會(huì)產(chǎn)生混淆。
為了避免這個(gè)錯(cuò)誤,最好的方法是只在本地倉(cāng)庫(kù)里修改commit,不要對(duì)遠(yuǎn)程庫(kù)里的commit做任何修改。當(dāng)然,一人一個(gè)分支也不會(huì)出現(xiàn)這個(gè)問(wèn)題。
Hard reset
git reset命令是用來(lái)將當(dāng)前branch重置到另外一個(gè)commit的。它不會(huì)產(chǎn)生commit,而是只更新一個(gè)branch(branch本身就是一個(gè)指向一個(gè)commit的指針)指向另外一個(gè)commit。
Hard reset的命令是git reset --hard
此外,git reset還有--soft和--mixed,只不過(guò)它們都沒(méi)有Hard reset那么不安全
假設(shè)開(kāi)發(fā)人員1正在開(kāi)發(fā)一個(gè)功能分支,并在本地倉(cāng)庫(kù)中完成了5次commit
與此同時(shí),他還正在處理尚未提交的兩個(gè)文件
這時(shí),如果他運(yùn)行了git reset --hard
那么功能分支中的最新commit會(huì)變成是commit4,commit5丟失
同時(shí)他正在處理的那兩個(gè)未提交文件也會(huì)丟失
這時(shí)commit5還在git內(nèi)部,只不過(guò)對(duì)它的引用丟失了,我們可以用git reflog把它恢復(fù),但總體來(lái)說(shuō),hard reset還是很不安全。在git中使用reset命令時(shí)要非常小心,如果必須得用,確保你已經(jīng)完全評(píng)估所有情況。
小結(jié)
綜上所述,為了避免使用git時(shí)出錯(cuò),我們可以牢記這幾條教訓(xùn):
避免多人在同一分支上協(xié)作。上述四個(gè)例子中有三個(gè)都是在說(shuō)明這個(gè)問(wèn)題,在日常工作中,遵守正確的工作流非常重要,要確保只有一個(gè)人在一個(gè)功能分支上工作,這是技術(shù)主管、高級(jí)開(kāi)發(fā)人員尤其需要注意的。
不要到處實(shí)用force push。
如果萬(wàn)不得已必須使用force push,先評(píng)估其他方案,把它作為最后的手段。
不要試圖修改遠(yuǎn)程倉(cāng)庫(kù)里的commit,要只在本地倉(cāng)庫(kù)中更改commit歷史記錄。但即便是在本地倉(cāng)庫(kù)里,用rebase還是要謹(jǐn)慎。
-
代碼
+關(guān)注
關(guān)注
30文章
4788瀏覽量
68616 -
Git
+關(guān)注
關(guān)注
0文章
199瀏覽量
15765
原文標(biāo)題:實(shí)用:Git中的一些常見(jiàn)錯(cuò)誤
文章出處:【微信號(hào):jqr_AI,微信公眾號(hào):論智】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論