『現學現忘』Git基礎 — 23、Git中的撤銷操作

繁華似錦Fighting 發表於 2022-05-09
Git

1、撤銷操作說明

我們在使用Git版本管理時,往往需要撤銷某些操作。比如說我們想將某個修改後的檔案撤銷到上一個版本,或者是想撤銷某次多餘的提交,都要用到Git的撤銷操作,因此撤銷操作在平時使用中還是比較多的。

本文介紹幾種,對於已修改過的檔案,需要進行撤銷操作,根據修改檔案出現的位置,可以分為三種情況:

  1. 僅僅是工作區中內容進行了修改,還未新增到暫存區。
  2. 檔案已經新增到暫存區,但是還未提交到本地版本庫。
  3. 檔案已經提交到本地版本庫。

前兩種可以叫撤銷操作,後面一種叫回退版本,不同的情況具有不同的撤銷方式。

下面就撤銷操作給出詳細的解釋。

2、撤銷工作區中檔案的修改

如果工作區的某個檔案被改亂了,但還沒有提交,可以用git restore或者git checkout命令找回本次修改之前的檔案。

前提:test.txt檔案已在本地版本庫中。

(1)修改test.txt檔案中的內容。

# 1.檢視test.txt檔案內容
[email protected] MINGW64 /j/git-repository/learngit (master)
$ cat test.txt
hello git

# 2.進行修改內容
[email protected] MINGW64 /j/git-repository/learngit (master)
$ echo "hello git v2" >> test.txt

[email protected] MINGW64 /j/git-repository/learngit (master)
$ cat test.txt
hello git
hello git v2

(2)檢視工作目錄中檔案的狀態。

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   test.txt

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

好好的說明一下這些提示資訊:

  • On branch master:當前工作在主分支上。
  • Changes not staged for commit::暫存區中沒有可提交的變化。
  • git add <file>...:可使用該命令把更新新增到暫存區。
  • git restore <file>...:丟棄工作區的修改。同git checkout -- <file>...命令一樣。
  • no changes added to commit (use "git add" and/or "git commit -a"):可以使用git add命令新增到暫存區,也可以使用git commit -a命令直接提交到本地版本庫。

Git的命令提示是非常詳細的,我們按照提示資訊操作就可以。

說明:

老版本Git會提示: (use "git checkout -- <file>..." to discard changes in working directory)

新版本Git會提示: (use "git restore <file>..." to discard changes in working directory)

git restore <file>...git checkout -- <file>...命令:

  • git checkout這個命令承擔了太多職責,既被用來切換分支,又被用來恢復工作區檔案,對使用者造成了很大的認知負擔。
  • Git社群釋出了Git的2.23版本中,有一個特性非常引人矚目,就是引入了兩個新命令 git switchgit restore,用以替代現在的git checkout
    即:Git2.23版本開始,git restore <file>...命令可代替git checkout -- <file>...命令檔案恢復的工作。

(3)撤銷工作區中test.txt檔案的修改。

# 1.使用git restore 命令撤銷工作區中的操作
[email protected] MINGW64 /j/git-repository/learngit (master)
$ git restore test.txt

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
nothing to commit, working tree clean

# 2.再次修改test.txt檔案
[email protected] MINGW64 /j/git-repository/learngit (master)
$ echo "hello git v3" >> test.txt

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   test.txt

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

# 使用git checkout命令撤銷工作區中的操作
# 撤銷操作固定寫法“git checkout -- 檔案”
[email protected] MINGW64 /j/git-repository/learngit (master)
$ git checkout -- test.txt

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
nothing to commit, working tree clean

說明:git checkout -- file命令中的--很重要,沒有--,就變成了“切換到另一個分支”的命令,我們在後面的分支管理中會再次遇到git checkout命令。

(4)總結

  1. git restore <file>...git checkout -- <file>...命令原理:
    這裡有兩種情況:
    一種是test.txt自修改後還沒有被放到暫存區,現在的撤銷修改就回到和版本庫一模一樣的狀態;
    一種是test.txt已經新增到暫存區後,又作了修改,現在的撤銷修改就回到新增到暫存區後的狀態。
  2. 撤銷命令是一個危險的命令,這個很重要,你對該檔案做的任何修改都會消失。原理是你拷貝了該檔案在暫存區或者本地版本庫中的副本,來覆蓋工作區的該檔案。
    即:工作區的檔案變化一旦被撤銷,就無法找回了,除非你確實不想要這個檔案了。

所以我的理解:撤銷命令實際的操作是重置(原檔案覆蓋),而實際的效果上是撤回。

3、撤銷暫存區中檔案的修改

如果已經把修改的個檔案新增到暫存區,可以用下面的命令撤銷。

還是以上面test.txt檔案為例。

(1)修改test.txt檔案並儲存到暫存區。

# 1.修改檔案
[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
nothing to commit, working tree clean

[email protected] MINGW64 /j/git-repository/learngit (master)
$ echo "hello git v4" >> test.txt

# 2.新增檔案到暫存區
[email protected] MINGW64 /j/git-repository/learngit (master)
$ git add test.txt
warning: LF will be replaced by CRLF in test.txt.
The file will have its original line endings in your working directory

# 3.檢視工作目錄中檔案狀態
[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   test.txt

說明:

  • Changes to be committed::表示暫存區中所做的更改如下,可以提交。
  • git restore --staged <file>...:該命令表示取消暫存,把暫存區中的檔案撤回到工作區。

Tips:

老版本Git會提示: (use "git reset HEAD <file>..." to unstage)

新版本Git會提示: (use "git restore --staged <file>..." to unstage)

和上面同理:

  • 早期的Git中git checkout命令承載了分支操作和檔案恢復的部分功能,有點複雜,並且難以使用和學習,所以社群解決將這兩部分功能拆分開,Git 2.23.0版本中中引入了兩個新的命令git switchgit restore
  • 早期的Git中,檔案恢復涉及到兩個命令,一個是git checkout命令,一個是git reset命令。git reset命令除了重置分支之外,還提供了恢復檔案的能力。
  • 而新的git restore命令,代替了checkout命令和reset命令(但是這兩個命令還可以進行檔案恢復),專門用來恢復暫存區和工作區的檔案。

(2)撤銷暫存區中test.txt檔案的修改。

1)使用git restore --staged命令撤銷暫存區中檔案的修改。

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git restore --staged test.txt

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   test.txt

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

(重點)我們可以看到,test.txt檔案從暫存區中撤銷到工作區中,但是不會撤銷工作區中檔案的更改。

2)使用git restore命令,不帶--staged引數,撤銷暫存區中檔案的修改。

# 1.檔案新增到暫存區中
[email protected] MINGW64 /j/git-repository/learngit (master)
$ git add test.txt
warning: LF will be replaced by CRLF in test.txt.
The file will have its original line endings in your working directory

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   test.txt

# 2.使用`git restore`命令,撤銷暫存區中檔案的修改
[email protected] MINGW64 /j/git-repository/learngit (master)
$ git restore test.txt

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   test.txt

我們可以看到,工作目錄中test.txt檔案狀態沒有改變,所以要撤銷暫存區中檔案的修改,必須加上--staged引數。

這是為什麼呢?(個人理解)

因為git restore命令,不帶--staged引數,背後的執行邏輯是,把暫存中的test.txt檔案複製一份,覆蓋掉工作區的test.txt檔案,但是這樣就形成了test.txt檔案在工作區和暫存區一樣的狀態,所以工作區和暫存區的狀態也一樣。這也就是上面執行完git restore test.txt命令,在工作目錄test.txt檔案的狀態,沒有變化的原因。

3)使用git reset HEAD <file>...命令撤銷暫存區中檔案的修改。

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git reset HEAD test.txt
Unstaged changes after reset:	# 表示重置後檔案未被跟蹤
M       test.txt	# M:應該是modified的意思吧

[email protected] MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   test.txt

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

我們看到git reset HEAD <file>...命令的執行效果和git restore --staged命令執行的效果是一樣的,這裡就不多解釋了。

之後要在進行撤銷工作區中檔案的修改,就和上一步一樣了。

4、總結

前面通過詳細的步驟,分別演示了從工作區和暫存區做撤銷修改的操作。

下面我們總結一下,方便我們對前面知識的理解。

  1. git checkout命令,既可以用來切換分支,又可以用來恢復工作區檔案。
    恢復檔案命令:git checkout -- 檔案
  2. git reset命令,除了能夠重置分支之外,還提供了恢復檔案的能力。
    恢復檔案命令:git reset HEAD 檔案
  3. Git 2.23.0版本中,提供了git restore命令,代替了git checkout命令和git reset命令的恢復檔案功能。(但上面兩個命令也能恢復檔案)
    修改的檔案只在工作區,沒有新增到暫存區中,使用git restore 檔案命令。
    修改的檔案已經新增到暫存區,但還沒有提交,使用git restore --staged 檔案命令。

在來詳細說明一下git restore命令:

這裡有兩種情況:

一種是test.txt自修改後還沒有被放到暫存區,現在的撤銷修改就回到和版本庫一模一樣的狀態;

一種是test.txt已經新增到暫存區後,又作了修改,現在的撤銷修改就回到新增到暫存區後的狀態。

git restore --staged命令:

用於已經把修改的檔案新增到暫存區,沒有被修改,但還未提交。這時執行該命令,直接撤銷暫存區總儲存的修改,將檔案恢復到工作區去。

參考:http://www.bubuko.com/infodetail-3431539.html