1. 程式人生 > >Git-少年,你想學回滾嗎?想撤銷檔案修改嗎?

Git-少年,你想學回滾嗎?想撤銷檔案修改嗎?

哎呀呀,夏天,哪裡涼快滾哪裡,冬天,哪裡暖和滾哪裡

寫在前面

林俊杰有首歌《可惜沒如果》,道盡後悔的遺憾,但是萬幸,在 Git 中你可以擁有如果,用 resetcheckoutrevert 可以用來撤銷當年那些錯誤的決定。

帶著 Git 三大區的概念去閱讀

來,看下面這張圖複習複習 Git 三大區的概念,這個概念即將貫穿今天這篇文章,理解很重要,不太理解的小夥伴可以先去我的另一篇部落格 《 Git三大特色之Stage(暫存區)》學習下先,等你哦。

git 資料流程圖示意圖

吃第一顆栗子,理解一下什麼叫做回滾

有一個Android 開發工程師,他叫小明,他正在 feature 分支上開發,他的 feature 分支歷史如下圖所示,一切都很正常,按照計劃進行,這時他接到一個需求變更通知,說“使用者積分模組,這個版本不要了”,這真是一個悲傷的訊息,小明要趕緊改回來。

如果小明不會回滾歷史,需要人肉操作去找有關 “使用者積分模組” 的程式碼,然後一個個注或刪除釋掉,這個更悲傷了,我猜小明可能會哭。

!
還在路上,稍等...

如果小明用的是 Git,也知道怎麼回滾,他只需要找到回滾的終點提交,然後一個命令就可以開始回滾操作。

如果小明是個老手,他會先新建個分支做備份,然後再回滾,那也只需要兩條命令:

以上完美解決此時的需求變更,小明很開心。

拿起你的栗子,來認識 reset 的三種模式

上面的例子中,我們使用的命令是 git reset --hard <commit-id>,其實 reset 有三種模式:
- –hard
- –mixed(預設模式)
- –soft

三個引數對應的是不同的作用範圍,這個範圍和開篇說的 Git 三大區息息相關,因為這個三個模式就是因為有三個分割槽才存在的。

實際使用中,你可能會遇到各種千奇百怪需求的的撤銷操作,理解好這三種模式的區別,使用起來倍爽。下面列兩三個你肯定會遇到的場景,然後用 reset 的不同模式。可以輕鬆解決問題。

小明寫程式碼ing;
同事說“小明,你快拉取一下倉庫的最新程式碼,blabla…” ;
小明開始“git pull” ;
git pull failed ,原因是暫存區有未提交的修改,這些修改可能會被覆蓋;
小明確定不會被覆蓋,不想建立一個新提交,還想儲存修改內容 ;
請問:該怎麼辦?

//將暫存區的內容與提交歷史同步
git reset

//然後再拉取                               
git pull xxx  

小明剛建立了一個 commit;
然後發現那個 commit 中有個小錯誤;
但是為了維護美好的分支歷史,不想因為這個小修改再建立一個提交;
請問:該怎麼辦?

//將此提交撤銷,但是不修改暫存區和工作區的內容
 git reset --soft HEAD^  

//修改好小問題

//重新提交                      
git add .
git commit -m 'xxx'

其實,這是使用 --soft的解決方案,還有一種更方便的方法,不用撤銷也能完成,git commit --amend追加提交:


//修改好小問題

//重新提交                      
git add .
git commit --amend`

上面的例子,希望給幫你更加清楚的理解 reset 三種的模式的區別,在使用 reset --hard的時候,要小心一點,因為這個操作會徹底地重置工作區,放棄修改,使用之前,要十分確定未儲存的修改要放棄,不然重置後有可能找不回來了哈。

下面給小夥伴們介紹另外一種撤銷方式 revert,是一個神奇的命令。

revert 是一種抵消式撤銷

revert,翻譯是“反轉”,一個比較難理解的詞,快來解開非凡想象力的封印。

想象一下,提交中添加了一個A.txt, revert 建立了一個刪除 A.txt 的新提交,兩個提交同時存在,於是,A.txt 等於從來沒有存在過,等於撤銷了原來提交。

這像一個互為正負的關係 1+(-1)= 0

第一個例子,小明如果用 revert “撤銷積分模組”,就要這樣用了

//將 head^^^到head 範圍內的提交反轉
git revert head^^^..head

git log 看一下操作後的分支歷史,不僅沒少,反而多了,增加幾個反轉提交。

這是 revert 第一個例子中場景的使用表現,也許你會疑問:

有了 reset,為什麼還要有 revert ?

這是因為,在下圖情況下,分支頂端還有要儲存的提交,reset 無法使用,只能用 revert 將中間的提交反轉。

如果不想要新增的反轉提交汙染分支歷史,你也可以選擇發射你的黑魔法 rebase -i 將中間的提交施加 drop 操作,Git 真的無所不能,只要你會。

reset VS revert ,Ready? GO!

由於 resetrevert 的作用都是撤銷提交,所以很多人很自然而然地將兩者對比。我就是那很多人之一,而且我還特別無聊的,畫了對比圖,如下:

可以很明顯的看出兩者對分支歷史改變區別:

  • reset 回退了歷史
  • revert 建立反轉提交,增加了歷史

從資料安全上角度,revertreset安全,因為它的操作可以回溯,反轉了還可以倒回來。reset比較徹底,是直接丟棄了,不過可以考慮想第一個例子中建立一個備份分支來保證安全。

從分支歷史的長期維護角度,reset的歷史比較乾淨,revert的反轉提交沒多大意義,畢竟很少有需求讓你滾來滾去的。

在被撤銷提交,不再分支頂端的場景上,reset 無法使用,revert可以做到,。

以上對兩者的區別討論結束,沒有總結,看你個人喜好和需求場景使用。

撤銷的不是提交,而是檔案

上面給大家介紹的都是關於 commit history 提交歷史的撤銷,但是有時候會出現撤銷單個檔案修改的場景,例如,我就經常寫了很多程式碼之後發現某個想法不可行,那個時候已經寫了很多程式碼了,Command+Z都撤不完了,手動改太麻煩還容易出錯,怎麼辦?

這個時候就需要撤銷檔案的修改了,下面就給大家介紹一下,如何進行檔案撤銷。

checkout 的第二命令格

checkout 這個命令,我們對它太熟悉了,切換分支,檢出提交,檢出 Tag 標籤等等各種對 HEAD 指標花式操作,但是看下,git-checkout 官網文件的定義:

是不是很驚訝,還可以被用來還原工作區的檔案!

其實這個命令就近在我們眼前,執行 git status,會看到工作區和暫存區的修改,如果工作區有修改存在,Git 就會提示,可以通過 git add <file> 來加入暫存區以備下次提交,如果放棄修改,就執行 git checkout -- <file>

git status中的 checkout

所以,要是哪次想撤回修改,恢復到上次 add 時內容 ,除了手動 Command+ZCtrl+Z ,還可以是使用 git checkout -- <file>,改動較多時,簡直一步到位!

reset 的新操作

接著上面,工作區1.txt的修改我想下次提交,於是執行了 git add . 加入了暫存區,這個時候執行 git status,又發現 reset 命令的新操作 git reset HEAD <file>

git status 中的 reset

看註釋,這個操作可以將某個檔案的修改從暫存區拿出去。在錯誤 add 不想提交的檔案時,這個命令還是很有用的。

寫在後面

好了,以上是本部落格的全部內容,希望能幫助大家理解和學會提交歷史的撤銷和檔案文修改的撤銷。

下篇部落格會講一個特別治癒的命令 reflog,和 reset 配合可以被用來恢復資料,包括已經撤銷的,敬請期待,並歡迎關注我的專欄 《帥氣的 Git 操作》,裡面有開始學習 Git 到現在的全部文章。

see you next blog!

歡迎訂閱我的Git系列文章


歡迎關注博主的微信公眾號,快快加入哦,期待與你一起成長!