1、通過路徑來重置修改說明
前面講述了 git reset
命令的基本用法,不過你還可以給它提供一個作用路徑(路徑+目錄/文件)。
若指定了一個路徑,git reset
命令將會跳過第 1 步,並且將它的作用範圍限定爲指定的文件或文件集合。
這樣做自然有它的道理,因爲 HEAD 只是一個指針,你無法讓它同時指向兩個提交中各自的一部分。
不過索引和工作目錄 可以部分更新,所以回退會繼續進行第 2、3 步。
現在,假如我們運行 git reset file.txt
,這其實是 git reset --mixed HEAD file.txt
的簡寫形式。
他會做如下操作:
- 移動 HEAD 分支的指向 (已跳過):實際上命令中寫的是HEAD,就是當前commit,所以顯示的效果是跳過了這一步。(我的理解)
- 讓暫存區中的
file.txt
文件進行撤銷。 - 而工作區中的
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
命令,它其實做了同樣的事情:
- 因爲
--mixed
後邊加了路徑(包括文件和目錄),所以會跳過第1步,也就是HEAD指針不進行移動。 - 然後繼續執行第2步,把暫存區中的
readme.txt
文件回退到V1版本。 - 然後工作區中的
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
選項,來一塊一塊地取消暫存的內容。 這樣你就可以根據選擇,來取消暫存或恢復內容了。