Git命令學習之git-reset詳解

2023-03-23 22:00:48

git-reset的作用是重置當前分支的HEAD指標,將HEAD指標指向特定的狀態。

使用概述

git reset [<tree-ish>] [--] <pathspec>
git reset [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>]
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [<commit>]
登入後複製

前三行reset命令的作用是將指定的<tree-ish>內容作為參考依據,然後把內容拷貝到目標的快取區中。

<tree-ish>就是像樹一樣的東西,git中是有很多都能組成樹的,比如commit樹或者tag樹。

最後一行reset命令的作用是將當前分支的HEAD指標指向<commit>,同時可以使用多種可選項來控制是否修改快取區或工作區。

在以上所有的命令形式中,都是把<tree-ish>或<commit>預設作為HEAD指標。

git reset [<tree-ish>] [--] <pathspec>

該方法有如下規則:

  • 會將與<pathspec>相匹配的路徑資源在快取區中重置成<tree-ish>中的狀態。

  • 被提交到快取區中的改動會被還原到工作區中。

舉個?

假設專案中有一個coffee.txt檔案,檔案內容如下:

卡布奇諾納瑞冰-19.9¥
標準美式-14.9¥
香草拿鐵-19.9¥
生椰愛摩卡-19.9¥
...
登入後複製
登入後複製

我們使用git的tag命令將當前的版本標記成v1.0.0(這裡使用tag標籤作為)。

隨後修改coffee.txt檔案,修改內容如下:

卡布奇諾納瑞冰-19.9¥ --> 卡布奇諾納瑞冰-14.9¥
標準美式-14.9¥ --> 標準美式-9.9¥
香草拿鐵-19.9¥ --> 香草拿鐵-14.9¥
生椰愛摩卡-19.9¥ --> 生椰愛摩卡-14.9¥
...
登入後複製

變更了coffee.txt檔案後,再使用git tag打上了v1.0.1標記。

如果此時想將coffee.txt檔案還原成v1.0.0版本中的檔案,就可以使用git reset命令並指定為v1.0.0,操作如下:

git reset v1.0.0 coffee.txt
登入後複製

使用VSCode檢視快取區的檔案變動:

image.png

可以看到圖中的右側的程式碼改動對比,此時快取區中的coffee.txt檔案已經成功被重置成v1.0.0版本中檔案的狀態(規則1)。並且此時v1.0.1已經提交到快取區中的變動也被還原到了工作樹中(規則2)。

image.png

可以看出git reset命令與git add命令作用相反,一個是將指定的資源新增到快取區,另一個則是從快取區中移除。並且這個命令與git restore [--source=<tree-ish>] --staged <pathspec>具有相同的作用,對restore感興趣的可以看看。

上面說過了,在使用reset命令後,此時快取區中檔案內容是v1.0.0的,可以配合git restore命令將快取區中的內容還原到工作區中然後進行修改:

git restore coffee.txt --staged
登入後複製

也可以根據需要選擇還原工作區的內容:

git restore coffee.txt --worktree
登入後複製

git reset [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]

在上面的例子中每次進行reset和restore的都是想同的<pathspec>路徑(coffee.txt)。因為該檔案路徑比較簡單,所以每次操作都比較方便輸入。但是在一些情況下,可能需要reset比較複雜的<pathspec>路徑,比如專案目錄層次較深,那麼很可能需要輸入一長串的<pathspec>路徑,這樣每次進行操作就會很麻煩,所以git提供了一個--pathspec-from-file選項,讓我們能夠直接指定一個檔案,這個檔案就包含了可能需要重複使用的<pathspec>路徑。有更詳細的用法。

該檔案每一行都是一個<pathspec>,如果有多個<pathspec>使用換行符作為分隔。當然也可以使用--pathspec-file-nul讓NUL作為每一個<pathspec>的分隔符。

git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>]

互動式的選擇<tree-ish>與快取區之間的差異而產生的hunks。這些被選擇的hunks將會復原快取區中的產生的修改。有更詳細的--patch選項用法。

git reset [<mode>] [<commit>]

該命令會把當前分支的HEAD指標指向某個<commit>,然後由<mode>決定是否同時更新快取區或工作區的內容。<mode>預設值是--mixed,且必須是以下幾種:

--soft

工作區和快取區中的檔案變動都將被保留,然後將HEAD指標指向<commit>。

還是以咖啡選單為?,假如第一次commit到倉庫中的檔案內容如下:

卡布奇諾納瑞冰-19.9¥
標準美式-14.9¥
香草拿鐵-19.9¥
生椰愛摩卡-19.9¥
...
登入後複製
登入後複製

然後做第二次commit操作,刪除標準美式,增加生椰拿鐵:

卡布奇諾納瑞冰-19.9¥
-標準美式-14.9¥
+生椰拿鐵-19.9¥
香草拿鐵-19.9¥
生椰愛摩卡-19.9¥
...
登入後複製

在commit後,修改卡布奇諾納瑞冰的價格,新增到快取區。再修改香草拿鐵的價格,保留在工作區:

-卡布奇諾納瑞冰-19.9¥
+卡布奇諾納瑞冰-14.9¥ // 新增到快取區中
生椰拿鐵-19.9¥
-香草拿鐵-19.9¥
+香草拿鐵-14.9¥ // 保留在工作區
生椰愛摩卡-19.9¥
...
登入後複製

此時我想保留工作區和快取區做的改動,並且將HEAD指標指向第一次commit。這時可以使用--soft選項實現:

git reset HEAD^ --soft // 這裡使用HEAD^表示上一個commit,同樣也可以使用hash id
登入後複製

使用git log檢視當前HEAD指標確實已經指向第一個commit,第二個commit被拋棄了:

image.png

同時所有的工作區和快取區的改動都被保留了:

image.png

當前只有兩次的commit,是為了方便演示--soft選項的作用。但是在實際開發中,我們可能會commit多次,尤其是在測試環境改幾個BUG就要提交到釋出平臺,這樣會導致很多無意義的commit。這時候就可以使用--soft選項,重置HEAD到指定的<commit>只保留一到兩個重要的commit。

--mixed

重置快取區,但是會保留工作區的內容,這是<mode>的預設值。

相信在理解了--soft作用後,理解--mixed不難,上面例子中如果是使用--mixed那麼最終結果如下:

image.png

該選項會重置快取區,但是保留工作區的改動,並將當前指標指向<commit>。

--hard

重置快取區和工作區中的所有的變動,並且將指標指向<commit>。

--hard更加的簡單粗暴,我們將--soft例子改為--hard來檢視結果:

image.png

如圖所示,工作區和快取區的內容都被重置了。不止是如此,就連Untracked檔案同樣也會被刪除。

--merge

該選項的作用,看字面意思就知道大概就是把當前分支和指定的<commit>進行合併,規則如下:

  • 重置快取區,任何已經新增到快取區的改動都將被拋棄

  • 如果<commit>和HEAD之間有檔案存在不同(這個不同指的是檔案被刪除或者新增),那麼將會把該檔案重置成<commit>中的狀態(新增或刪除)。

  • 如果<commit>和HEAD之間有檔案存在不同(這個不同是指檔案內容的不同),且此時工作區也存在未提交的改動,那麼本次的reset將會被終止。

  • 如果一個檔案在<commit>和HEAD中完全相同,但是它的工作區存與快取區存在著不同(也就是改動未提交到快取區),那麼該檔案在工作區的改動在重置之後就會被保留。

我們舉個例子來驗證一下以上列出的規則,假設此時的咖啡店專案有如下的幾個commit。

第一個commit和檔案內容如下:

image.png

第一個commit中只有一個coffee.txt選單檔案,此時如果咖啡店引進了新品種開始賣果汁,那麼就需要新增果汁選單檔案fruits.txt,於是就有了第二個commit:

image.png

此時我們做一些改動來驗證1,2,4這幾點的規則,改動後的檔案如下:

image.png

首先修改coffee.txt檔案,新增一款生椰拿鐵咖啡,保留在工作區中。然後增加咖啡豆選單檔案beans.txt,將其新增到快取區中。

假設因需求變動,咖啡豆選單檔案在快取區中需要清除,果汁選單檔案需要刪除,只有咖啡選單中新增的生椰拿鐵的改動需要保留。那麼就可以使用git reset --merge將HEAD和commit-1進行合併,操作如下:

git reset --merge HEAD^
登入後複製

結果如下:

image.png

執行命令reset命令產生了如下效果:

  • 將當前HEAD指標指向了commit-1
  • 將快取區中的beans.txt檔案拋棄(規則1)
  • HEAD(commit-2)和commit-1中的fruits.txt檔案存在不同(commit-2中新增fruits.txt),所以會將fruits.txt刪除(規則2)
  • coffee.txt檔案新增生椰拿鐵的改動被保留在工作區中(規則4)

再來驗證一下第3點規則,假設咖啡店專案此時第一個commit如下:

image.png

接下去同樣新增水果茶選單,然後再修改coffee.txt檔案,第二個commit如下:

image.png

然後在HEAD中再修改coffee.txt檔案,刪除掉標準美式品種:

image.png

此時,如果我們再使用git reset --merge HEAD^就無法再進行重置,該操作會被git終止(規則3)。並且控制檯會進行報錯提示:

image.png

--keep

該選參的作用和--merge相似,唯一的區別就是快取區中被重置的會被保留在工作區中。

構造如下第一個commit:

image.png

改造第二個commit:

image.png

在HEAD中進行修改

image.png

使用git reset --keep命令:

image.png

從結果上來看,只有fruits.txt檔案被刪除了,beans.txt檔案被重置回了工作區中。coffee.txt檔案的改動也被保留了。

--[no-]recurse-submodules

使用該選項可以控制是否遞迴的重置submdoule。如果想要更詳細瞭解,。

總結

git-reset命令的作用就是重置快取區和工作區,同時它也提供多個選項來做更具體的控制,使得該命令更加靈活多變。git-reset命令在我們的工作中經常使用,因此熟練掌握該命令是非常重要的。

更多程式設計相關知識,請存取:!!

以上就是Git命令學習之git-reset詳解的詳細內容,更多請關注TW511.COM其它相關文章!