git 入門教程之撤銷更改

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

撤銷更改

相信你已經瞭解了 git 的基本概念,也清楚了工作區,暫存區和版本庫的關係,現在讓我們用所學的知識繼解決實際問題吧!

背景

正常看得見的目錄是我們最為熟悉的工作區,在工作中不可能總是100%的精力,難免會犯錯,尤其是下午犯困,晚上加班更是如此.下面列舉了常見的一些場景

  • 場景一: 工作區出現意外更改且尚未新增到暫存區

北京時間現在是晚上10點鐘,你正在趕製一份工作報告,儘管心中一萬個不願意,還是不得不做.

開始模擬意外更改前,先檢視一下 test.txt 檔案相關資訊:

# 列出當前目錄的檔案
$ ls
file1.txt   file2.txt   file3.txt   newFile.txt test.txt
# 檢視 `test.txt` 檔案內容
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
# 檢視 `test.txt` 檔案狀態
$ git status
On branch master
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:   test.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

no changes added to commit (use "git add" and/or "git commit -a")
# 檢視 `test.txt` 檔案差異
$ git diff 
diff --git a/test.txt b/test.txt
index d31bdd2..56c76b7 100644
--- a/test.txt
+++ b/test.txt
@@ -3,4 +3,4 @@ git init
 git diff
 understand how git control version
 how git work
-git tracks changes
+git tracks changes of files
$ 
複製程式碼

還記得在上一節中我們講解 git 版本控制的到底是什麼,為了證明 git 管理的是更改而不是檔案本身,我們特意在第二次更改時沒有新增到暫存區,現在我們先把這個遺留問題解決掉.

# 工作區更改新增到暫存區
$ git add test.txt
# 暫存區內容提交到版本沒哭
$ git commit -m "git tracks changes of files"
[master b7bda05] git tracks changes of files
 1 file changed, 1 insertion(+), 1 deletion(-)
# 檢視檔案狀態
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

nothing added to commit but untracked files present (use "git add" to track)
$ 
複製程式碼

現在正在加班加點幹活,一不小心將心中的不滿表露出來了,於是有了下面的內容:

# 意外更改正是這麼犯傻的一句話
$ echo "My stupid boss still prefers svn" >> test.txt
# 當前檔案內容
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
My stupid boss still prefers svn
$ 
複製程式碼

雖然強打精神,可還是很困,於是打算喝杯咖啡提提神,猛然發現 stupid boss 可能會讓你丟掉這個月的獎金!

暗自慶幸,咖啡果然是個好東西,既然發現了問題,那就事不宜遲趕緊修復,因為不適宜的話正是 stupid boss ,所以你完全可以手動刪除,但是假如你說了一大堆不合適的話,或者複製貼上時弄錯了,這就不是刪除一兩行那麼簡單了!

既然手動解決比較麻煩,那git 有沒有什麼好方法來解決這類問題呢?在尋求git 幫助前,首先再看一下當前檔案狀態(git status).正所謂"知己知彼方能百戰百勝",還是看一眼吧!

# 檢視檔案狀態
$ git status
On branch master
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:   test.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

no changes added to commit (use "git add" and/or "git commit -a")
$ 
複製程式碼

git 不負眾望,果然給了我們希望,(use "git checkout -- <file>..." to discard changes in working directory) 這句話的告訴我們可以丟棄工作區的更改!

腦海中在快速回憶一下工作區,暫存區,版本庫三者之間的關係,其實git checkout -- <file> 命令的意思是用暫存區的內容替換掉工作區內容,因此也就是丟棄掉工作區的更改了.

事不宜遲,執行 git checkout -- <file> 命令試試看吧:

# 丟棄工作區的更改
$ git checkout -- test.txt
# 檢視檔案內容: My stupid boss still prefers svn 終於不見了
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
# 檢視檔案狀態
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

nothing added to commit but untracked files present (use "git add" to track)
$ 
複製程式碼

一頓操作猛如虎,撤銷掉意外更改,回到上一次版本控制狀態,世界如此美好...

注意: git checkout -- <file> 中的 -- 至關重要,沒有它就是切換分支了!

  • 場景二: 工作區出現意外更改且已經新增到暫存區,但尚未提交到版本庫

時間一分一秒過去了,轉眼間已經11點了,假設你不但寫了一些胡話,還新增到暫存區了(git add).可想而知,這次意外比場景一要糟糕.

# 模擬正常提交(不然豈不是從場景一到場景二你什麼都沒做,那還能叫做趕製工作報告嗎?!)
$ echo "someone prefers svn,but i don't care it" >> test.txt
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
someone prefers svn,but i don't care it
$ git add test.txt
$ git commit -m "normal commit"
[master ab1cbd2] normal commit
 1 file changed, 1 insertion(+)

# 意外更改的前夕 
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
someone prefers svn,but i don't care it

# 意外更改內容: my teammate is stupid too.
$ echo "my teammate is stupid too." >> test.txt
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
someone prefers svn,but i don't care it
my teammate is stupid too.

# 意外操作: 將意外更改內容提交到暫存區
$ git add test.txt 
複製程式碼

不過慶幸的是,在提交到版本庫(git commit)之前及時發現問題,還是看一下現在的檔案狀態(git status)吧!

# 檢視檔案狀態: 救命稻草 (use "git reset HEAD <file>..." to unstage)
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   test.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

$ 
複製程式碼

git 同樣告訴我們,可以使用 git reset HEAD <file> 命令撤銷暫存區更改.

其實 git reset HEAD <file> 命令是用版本庫的內容替換掉暫存區的內容,也就是說原來暫存區的內容已被丟棄了!

所以說這個命令並不會影響工作區內容,不如我們現在再看一眼工作區內容,方便執行 git reset HEAD <file> 命令後證實我們的結論.

# 檢視檔案內容: my teammate is stupid too.
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
someone prefers svn,but i don't care it
my teammate is stupid too.
$ 
複製程式碼

迫不及待執行 git reset HEAD <file> 命令,先睹為快!

# 救命稻草: 版本庫內容替換掉暫存區內容
$ git reset HEAD test.txt
Unstaged changes after reset:
M   test.txt

# 效果: 目標檔案已修改但未新增到暫存區
$ git status
On branch master
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:   test.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

no changes added to commit (use "git add" and/or "git commit -a")

# 目標檔案內容: 仍然保持不變
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
someone prefers svn,but i don't care it
my teammate is stupid too.
$ 
複製程式碼

現在場景二已經退化成場景一了,目標檔案發生意外更改但還沒新增到暫存區,如何撤銷工作區更改,請參考場景一方法.

提示: git checkout -- test.txt

  • 場景三: 工作區出現意外更改不僅已新增到暫存區,還已提交到版本庫,但尚未推送到遠端倉庫

時間不緊不慢地已經到凌晨了,睏意越來越濃,洋洋灑灑寫下幾千字的工作報告,總算是寫完了,新增到暫存區(git add),提交到版本庫(git commit)一氣呵成,等等,好像有什麼不對勁,難免會犯糊塗,這不又發生意外了!

# 銜接場景二
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
someone prefers svn,but i don't care it

# 正常提交一
$ echo "i love working,work makes me happy" >> test.txt
$ git add test.txt
$ git commit -m "encourage myself"
[master a44cf7a] encourage myself
 1 file changed, 1 insertion(+)

# 正常提交二
$ echo "fix 110 bugs,so happy" >> test.txt
$ git add test.txt
$ git commit -m "fix bugs"
[master c66399d] fix bugs
 1 file changed, 1 insertion(+)
sunpodeMacBook-Pro:demo sunpo$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

nothing added to commit but untracked files present (use "git add" to track)

# 意外更改: hate to work overtime
$ echo "hate to work overtime" >> test.txt
$ git add test.txt
$ git commit -m "test.txt"
[master c965724] test.txt
 1 file changed, 1 insertion(+)
 $ 
複製程式碼

天妒英才,加班加點做事情,本想贏得老闆的賞識,沒想到最後一句話"hate to work overtime"讓所有的努力都付之一炬,怎麼辦?

死馬當活馬醫,還是照例看看git status 能提供什麼建議吧!

$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

nothing added to commit but untracked files present (use "git add" to track)
$ 
複製程式碼

沒有提供任何意見能幫助我們撤銷意外更改,先別慌,容我深思三秒鐘...

既然意外更改已經提交到版本庫,那麼應該用什麼內容替換版本庫內容呢?有了,既然最新版本庫不可用,那上一個版本庫內容可用的啊,完全可以用上一個版本庫內容替換最新版本庫內容,真乃"天生我材必有用"!

# 當前檔案內容: 闖禍的"hate to work overtime"
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
someone prefers svn,but i don't care it
i love working,work makes me happy
fix 110 bugs,so happy
hate to work overtime

# 版本回退: 回到過去假裝什麼都沒發生過
$ git reset --hard HEAD^
HEAD is now at c66399d fix bugs
sunpodeMacBook-Pro:demo sunpo$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

nothing added to commit but untracked files present (use "git add" to track)

# 歲月靜好,一切似乎都沒發生過
$ cat test.txt
git test
git init
git diff
understand how git control version
how git work
git tracks changes of files
someone prefers svn,but i don't care it
i love working,work makes me happy
fix 110 bugs,so happy

# 當前檔案狀態
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)

    .DS_Store

nothing added to commit but untracked files present (use "git add" to track)
$ 
複製程式碼

詳情請參考回到過去,時空穿越之旅就是這麼方便哈!

提示: git reset --hard HEAD^

  • 場景四: 工作區出現意外更改不僅已新增到暫存區,還提交到版本庫,還已推送到遠端倉庫

場景一到場景三都是本地倉庫,所有的檔案更改只能本機訪問,小夥伴也好,上級領導也罷都無法檢視到你本地更改,但是一旦你推送到遠端倉庫了,那麼其他人就能檢視你的更改了!

正常的提交更改還好,怕就怕這種"stupid boss"被領導看到就不好了,那應該怎麼辦?暫時還是自求多福吧!

小結

  • 丟棄工作區更改: git checkout -- <file>
  • 丟棄暫存區更改: git reset HEAD <file>
  • 丟棄本地版本庫更改: git reset --hard HEAD^
  • 丟棄遠端版本庫更改: 自求多福

相關文章