【學了就忘】Git後悔藥 — 36.通過路徑進行重置

1、通過路徑來重置修改說明

前面講述了 git reset 命令的基本用法,不過你還可以給它提供一個作用路徑(路徑+目錄/文件)。

若指定了一個路徑,git reset命令將會跳過第 1 步,並且將它的作用範圍限定爲指定的文件或文件集合。

這樣做自然有它的道理,因爲 HEAD 只是一個指針,你無法讓它同時指向兩個提交中各自的一部分。

不過索引和工作目錄 可以部分更新,所以回退會繼續進行第 2、3 步。

現在,假如我們運行 git reset file.txt ,這其實是 git reset --mixed HEAD file.txt 的簡寫形式。

他會做如下操作:

  1. 移動 HEAD 分支的指向 (已跳過):實際上命令中寫的是HEAD,就是當前commit,所以顯示的效果是跳過了這一步。(我的理解)
  2. 讓暫存區中的file.txt文件進行撤銷。
  3. 而工作區中的file.txt文件的修改,保持不變。

大家想一下這個場景:我在工作區修改完文件,然後添加到了暫存區中,但是我還沒有提交到本地版本庫中。這個時候我發現有內容寫錯了,我此時執行 git reset file.txt命令,即git reset --mixed HEAD file.txt 命令。

就會發生表面現象,只是從暫存區中把file.txt文件,撤回到工作區了,且工作區中的修改保持不變,同時暫存區中其它文件不改變。

這樣的效果,也就相當於是git add filename命令和git reset filename命令,是相互的反操作。

2、圖解說明

1)步驟1:

現在有一個V1版本的file.txt文件,進行修改後,變成V2版本,添加到暫存區中。

就是如下圖所示的狀態:

2)步驟2:

我發現剛剛修改file.txt文件有錯誤,需要從暫存去中撤回到工作區中,進行重新修改。

執行命令:git add filename

如下圖所示:


說明:

它本質做的是,是將 file.txt 文件從 HEAD 複製到暫存區中,進行單文件的覆蓋。

這樣就有了"取消暫存文件"的實際效果。 然後再想想 git add 命令所做的事,就會發現它們正好相反。

這就是爲什麼 git status 命令的輸出提示中,會建議運行此命令來取消暫存一個文件。

3、拓展

我們可以不讓 Git 從 HEAD 拉取數據,而是通過具體指定的某一個提交,來拉取該文件的對應版本。

如下:現在Git的工作目錄中,工作區、暫存區和本地版本庫中的file.txt文件都是V3版本,我需要把暫存區中的file.txt文件恢復成V1版本。

只需執行命令:git reset eb43bf -- file.txt 即可。

即執行了git reset --mixed eb43bf -- file.txt命令。

如下圖所示:

(到這裏我也沒有,沒有具體的底層操作流程是什麼。我只知道,只要這樣執行命令,工作區和本地版本庫中的文件都不會改變,只有暫存區中的文件會回退到指定的版本。)

下面我們通過命令行演示:

# 1.查看歷史提交信息
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git log --oneline
e72b30f (HEAD -> master) 第4次提交,新增內容:readme.txt file v4
529ad74 第3次提交,新增內容:readme.txt file v3
1b23cae 第2次提交,新增內容:readme.txt file v2
2612adf 第1次提交,創建readme.txt文件

# 2.查看可回退的歷史提交信息
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git reflog
e72b30f (HEAD -> master) HEAD@{0}: reset: moving to e72b30f
529ad74 HEAD@{1}: reset: moving to HEAD^
e72b30f (HEAD -> master) HEAD@{2}: commit: 第4次提交,新增內容:readme.txt file v4
529ad74 HEAD@{3}: commit: 第3次提交,新增內容:readme.txt file v3
1b23cae HEAD@{4}: commit: 第2次提交,新增內容:readme.txt file v2
2612adf HEAD@{5}: commit (initial): 第1次提交,創建readme.txt文件

# 3.查看工作目錄中文件狀態,很乾淨
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
nothing to commit, working tree clean

# 4.查看readme.txt文件內容
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ cat readme.txt
readme.txt file v1
readme.txt file v2
readme.txt file v3
readme.txt file v4

# 5.把暫存區中的readme.txt文件回退到V1版本。
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git reset 2612adf -- readme.txt
Unstaged changes after reset:
M       readme.txt

# 6.比較工作區和暫存區中readme.txt文件的差別
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index 0d065f4..47b238c 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1 +1,4 @@
 readme.txt file v1
+readme.txt file v2
+readme.txt file v3
+readme.txt file v4

# 7.比較暫存區與本地版本庫中readme.txt文件的差別
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git diff --cached readme.txt
diff --git a/readme.txt b/readme.txt
index 47b238c..0d065f4 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,4 +1 @@
 readme.txt file v1
-readme.txt file v2
-readme.txt file v3
-readme.txt file v4

# 8.查看暫存區readme.txt文件的內容如下:
# 查看暫存區中文件的信息
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git ls-files -s
100644 0d065f480e5ac0200f678ff99a206729d47d808f 0       readme.txt

# 查看tree對象的內容
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git cat-file -p 0d065f480e5ac0200f678ff99a206729d47d808f
readme.txt file v1

如上已確認,工作區和本地版本庫中都是V4版本,而暫存區回退到V1版本。(這個示例比圖片中多一個提交)

那我們查看一下,當前工作目錄中文件的狀態,還有歷史提交信息。

# 1.查看當前工作目錄中文件的狀態
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   readme.txt

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:   readme.txt

# 2.再次查看歷史提交版本
L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git log --oneline
e72b30f (HEAD -> master) 第4次提交,新增內容:readme.txt file v4
529ad74 第3次提交,新增內容:readme.txt file v3
1b23cae 第2次提交,新增內容:readme.txt file v2
2612adf 第1次提交,創建readme.txt文件

L@DESKTOP-T2AI2SU MINGW64 /j/git-repository/learngit (master)
$ git reflog
e72b30f (HEAD -> master) HEAD@{0}: reset: moving to e72b30f
529ad74 HEAD@{1}: reset: moving to HEAD^
e72b30f (HEAD -> master) HEAD@{2}: commit: 第4次提交,新增內容:readme.txt file v4
529ad74 HEAD@{3}: commit: 第3次提交,新增內容:readme.txt file v3
1b23cae HEAD@{4}: commit: 第2次提交,新增內容:readme.txt file v2
2612adf HEAD@{5}: commit (initial): 第1次提交,創建readme.txt文件
# 發現日誌信息和最開始一樣,沒有變動。

我們可以看到,暫存區中有一個已修改狀態的readme.txt文件,工作區中有一個未被追蹤的readme.txt文件。

同是也還發現了一個現象,就是這樣操作不會生成新的commit。不像之前使用git reset命令後,會自動生成一個commit提交。

看到這裏我就大概明白了,git reset --mixed eb43bf -- readme.txt命令,它其實做了同樣的事情:

  1. 因爲--mixed後邊加了路徑(包括文件和目錄),所以會跳過第1步,也就是HEAD指針不進行移動。
  2. 然後繼續執行第2步,把暫存區中的readme.txt文件回退到V1版本。
  3. 然後工作區中的readme.txt文件,在使用--mixed參數進行回退的時候,內容不會變動。

所以工作區和暫存區中的readme.txt文件內容不一樣,纔會出現暫存區中有已修改狀態的readme.txt文件,和工作區中有一個未被追蹤的readme.txt文件。

這時如果我們直接執行git commit命令進行提交,暫存區中V1版本的readme.txt文件就會被提交到本地版本庫中,會新生成一個commit提交,它就會記錄一條“將該文件恢復到 v1 版本”的更改。

提示:在這種情況下,如果把工作區的readme.txt文件,先添加到暫存區中,然後在提交,當前版本庫的歷史提交信息,是不會有任何變化的。(你可以試試)

分析原因:

我之前一直以爲只要有git commit提交操作,就應該有一個commit提交生成。我是這樣想的,當我把工作區的readme.txt文件,添加到暫存區後。這個時候工作區,暫存區和本地版本庫中readme.txt文件都是一樣的,等於我沒有做任何的修改。然後我直接提交新的commit,應該這個操作會被Git忽略把。Git也給你提示nothing to commit, working tree clean:沒有可提交的內容,暫存區中的樹對象沒改變。

說明:還有一點同 git add 一樣,就是 reset 命令也可以接受一個 --patch 選項,來一塊一塊地取消暫存的內容。 這樣你就可以根據選擇,來取消暫存或恢復內容了。

參考:https://git-scm.com/book/zh/v2/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章