【吐血整理】Git的各種撤銷姿勢

sidfate發表於2020-01-18

前提

如果要排一個程式設計師在git使用中遇到的最多問題的排名,我想 “怎麼正確的撤銷git操作?” 肯定是名列第一(第二應該是windows如何安裝使用git)。就比如,“我提交了一個錯誤的檔案咋辦?”,“我push錯了分支怎麼辦?”,“我分支合錯了怎麼辦?”,這種問題在開發中總是能聽到,不絕於耳。

今天,這篇文章,就來好好說說git撤銷的各種姿勢,解決你的撤銷恐懼症。

為了保證文章的生動形象,請允許我在圖片中插播文章。

準備工作

我們為了方便演示,在本地的 “學習資料目錄” 下新建一個git目錄 “test-git”,並在目錄下初始化git倉庫。

⚡ mkdir test-git
⚡ cd test-git
⚡ git init
複製程式碼

【吐血整理】Git的各種撤銷姿勢

知識點整理:

  • 工作區

    工作區就是你本地倉庫的所在目錄,在我們這就是指的 “test-git” 這個目錄。

  • 暫存區

    暫存區就是暫時存放被修改檔案的區域,比如 git add 就是把要提交的修改放進暫存區。

為了方面接下來的演示,我們先在master分支上建立並提交一個 README.md 檔案,然後從 master上新建1個分支名為 test1。這裡我就省略了具體操作步驟,純新手請自行搜尋下如何實現。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/08addd54-e9e2-44ac-9498-28613b60ebe8/Untitled.png

下面,我整理了多個高頻率的疑難點,通過一個個場景來解答。

PS:以下場景簡單化了我們實際遇到的情況,請各位觀眾對號入座,舉一反三。

場景1:我在暫存區新增了一個不想提交的檔案,怎麼辦?

小A在test1分支修改了 README.md ,過了幾天後新加了一個檔案 a.c

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/569760ab-0c2d-4627-a8a2-aec339f08f95/Untitled.png

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/98381a81-cdf1-4aca-844b-dd9c6dd18dac/Untitled.png

知道上圖中演算法名稱的可以在評論下留言~

小a準備提交程式碼,只想提交 a.c 檔案但是忘記了自己一共修改了2個檔案,按以往習慣一併使用 git add . 新增修改到暫存區,然後一看 git status 人傻了發現不對勁,自己多新增了 README.md 的修改,這是這次提交不需要的,這可咋辦?

別慌,下面命令可以幫到你:

⚡ git rm --cached [filename]
複製程式碼

在我們這個場景中,filename 帶入 README.md 就ok了,它的作用就是撤銷暫存區的檔案修改

PS:以上場景被我簡化了可能不明顯。實際情況是那些習慣在專案中用 git add . 統一新增修改到暫存區的童鞋,他們很容易遇到上面的問題。

場景二:我寫錯了提交資訊,怎麼辦?

繼續上面的場景,小a在完成撤銷後打算提交這次修改,然而悲劇他又在填寫提交資訊的時候手快筆誤:

⚡ git commit -m "add a.cpp"
複製程式碼

雖然提交資訊很多人不重視,但是小a同學還是很羞愧,把c檔案“改成“了c++檔案,這可咋辦?

別慌,git可以讓你反悔:

⚡ git commit --amend -m "add a.c"
複製程式碼

這個命令的原理是產生一個新的提交物件,替換掉上一次提交產生的提交物件。

這時如果暫存區有發生變化的檔案,會一起提交到倉庫。所以,--amend不僅可以修改提交資訊,還可以整個把上一次提交替換掉。

場景三:我的檔案改亂了,想要還原檔案到上一次提交,怎麼辦?

成功提交後,不知道大家還記得不,此時我們的倉庫中還有一個沒處理的修改 README.md

⚡ git status
On branch test1
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   README.md
複製程式碼

因為已經過了一段時間,小a甚至都忘了自己當初為什麼要去改 README.md ,覺得不說破更神祕,所以小a想要撤銷這個檔案的修改,這可咋辦?

別慌,工作區的檔案修改也能撤銷:

$ git checkout -- README.md
複製程式碼

它的原理是先找暫存區,如果該檔案有暫存的版本,則恢復該版本,否則恢復上一次提交的版本。當然如果工作區的檔案修改被撤銷了,也就沒法找回了。

我們可以看到checkout除了切換分支的作用還能撤銷檔案修改,同一個命令會讓人困惑。針對切換分支,用 switch 命令更加符合語義。

場景四:我提交了程式碼,想要還原到以前的一個版本,怎麼辦?

讓我們回顧下現在 git 倉庫的提交歷史:總共執行了2次提交。

⚡ git log --oneline
200e6d2 (HEAD -> test1) add a.c
9d7fb60 (master) Initial Commit
複製程式碼

小a對最近的一次提交後悔了,原因是他做錯了老師出的演算法題,於是他決定先回退到最初的版本,重新寫一個 a.c ,這可咋辦?

別慌,我給出2種回退版本的方案,先講第一種:

$ git revert HEAD
複製程式碼

這個命令的原理是新增一次提交,抵消掉上一次提交的修改,所以不會丟失歷史記錄,只不過是新增一次歷史。

如果想要撤銷多次提交,需要依次寫出:

$ git revert [上一次] [上上次] ......
複製程式碼

再來講下第二種:

$ git reset 9d7fb60
複製程式碼

這個命令的原理是,讓最新提交的指標重置到指定的提交上,所有指定提交之後的提交都不可見。

需要提到的是,reset 不會修改工作區的檔案(會修改暫存區),如果想要同時修改工作區檔案:

$ git reset --hard [banch]
複製程式碼

小a成功這一下用了第二種方法回退了版本,重新寫了 a.c,然後將 test1 分支合併到了 master,交給了老師 :

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6060f851-6182-4fa4-9c6e-af204d7699d2/Untitled.png

猜猜 guessme 是什麼演算法?

場景五:我合併錯了分支,想要取消合併的操作,怎麼辦

由於老師佈置的演算法題太多了,小a邀請了室友小b幫他分擔一些演算法題,小b為了與小a區分,從master上拉了最新的內容,並自己新建了 test2 分支。聰明且“熱心“的小b同學注意到小a寫的 guessme 演算法,覺得有更好的實現,於是他也修改了 a.c 檔案。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8f8f956d-09e4-46aa-a0c8-03256ad87556/Untitled.png

修改完後沒有告訴小a,自己準備合併到master的時候,正好被小a看到,小a感覺基友之情被傷害了。小b為了挽回,道歉並告訴小a:“我馬上把合併的內容還原回去“,然後他快速的敲下了下面一行命令:

git merge --abort
複製程式碼

大團圓結局,兩人迴歸於好,並一起探討了演算法的實現,得到了老師的表揚。

該命令最多的場景在於當合並程式碼衝突後無從下手的時候可以先使用,避免程式碼混亂。

總結

  1. 養成良好的git習慣,上面我也有提到,例如 git add . 謹慎使用,多使用 git status檢視當前工作區和暫存區的狀態。
  2. 使用idea內建的git工具,或者其他第三方的app,可以有效的防止命令列事故,也提高了開發效率。但是,不能只當工具人,當你熟練了後更要看核心原理,會有一種豁然開朗的感覺。
  3. 根據大資料分析,評論+點贊+關注的童鞋買車票不排隊,拿壓歲錢拿的手軟。

對本文有疑問或質疑,以及小彩蛋的看法可以在評論區留言~~

相關文章