git reset 命令详解(一)—— Git 学习笔记 07
git reset 命令詳解(一)
簡而言之,git reset 命令是用來將當前 branch 重置到另外一個 commit 的,這個動作可能同時影響到 index 以及 work directory.
先舉個例子,來一個感性的認識。下面這兩條命令讓 hotfix 分支向后回退兩個提交。
git checkout hotfix git reset HEAD~2hotfix 分支末端的兩個提交現在變成了孤兒提交。下次 Git 執行垃圾回收的時候,這兩個提交會被刪除。如果你的提交還沒有共享給別人,可以用git reset撤銷這些提交。
三棵樹
理解 reset (以后還要說 checkout )的最簡方法,就是以 Git 的思維框架(將其作為內容管理器)來管理三棵不同的樹。“樹” 在我們這里的實際意思是 “文件集合”,而不是指特定的數據結構。
Git 作為一個系統,是以它的一般操作來管理并操縱這三棵樹的:
| HEAD | 上一次提交的快照,下一次提交的父結點 |
| Index | 預期的下一次提交的快照 |
| Working Directory | 沙盒 |
HEAD
HEAD 是當前分支引用的指針,它總是指向該分支上的最后一次提交。 這表示 HEAD 將是下一次提交的父結點。 通常,可以把 HEAD 看做你的上一次提交的快照。
下面是我從網上搜來的 2 張圖片,感謝原作者。
大意就是:HEAD 指向分支(branch),分支指向提交。
索引(index)
索引是你預期的下一次提交。這就是當你運行 git commit 時 Git 看起來的樣子。Git 將上一次檢出到工作目錄中的所有文件填充到索引(暫存區),之后你會將其中一些文件替換為新版本,接著通過 git commit 將它們轉換為樹來用作新提交。
工作目錄
另外兩棵樹以一種高效但并不直觀的方式,將它們的內容存儲在 .git 文件夾中。工作目錄會將它們解包為實際的文件以便編輯。 你可以把工作目錄當做 “沙盒”,在你將修改提交到暫存區并記錄到歷史之前,可以隨意更改。
流程圖解
下文會用一幅幅圖說明從初始化倉庫到操作工作區,再到 add 文件,最后到提交等整個流程。
僅工作目錄有內容
假設我們進入到一個新目錄,其中有一個文件。 我們稱其為該文件的 v1 版本,將它標記為藍色。 現在運行 git init,這會創建一個 Git 倉庫,其中的 HEAD 引用指向未創建的分支(master 還不存在)。
添加到索引
現在我們想要提交這個文件(file.txt ),所以用 git add 把工作目錄中的內容復制到索引中。
提交
接著運行 git commit,它會取得索引中的內容并將它保存為一個永久的快照,然后創建一個指向該快照的提交對象,最后更新 master 來指向本次提交。
此時如果我們運行 git status,會發現沒有任何改動,因為現在三棵樹完全相同。
修改文件
現在我們想要對文件進行修改然后提交它。我們將會經歷同樣的過程;首先在工作目錄中修改文件。 我們稱其為該文件的 v2 版本,并將它標記為紅色。
如果現在運行 git status,我們會看到文件顯示在 “Changes not staged for commit” 下面,并被標記為紅色,因為該條目在索引與工作目錄之間存在不同。 接著我們運行 git add 來將它暫存到索引中。
再次添加到索引
此時,由于 Index 和 HEAD 不同,若運行 git status 的話就會看到 “Changes to be committed” 下的該文件變為綠色 ——也就是說,現在預期的下一次提交與上一次提交不同。 最后,我們運行 git commit 來完成提交。
再次提交
現在運行 git status 會沒有輸出,因為三棵樹又變得相同了。
切換分支或克隆的過程也類似。 當檢出一個分支時,它會修改 HEAD 指向新的分支引用,將索引填充為該次提交的快照,然后將索引的內容復制到工作目錄 中。
reset 的作用
在以下情景中觀察 reset 命令會更有意義。
為了演示這些例子,假設我們再次修改了 file.txt 文件并第三次提交它。 現在的歷史看起來是這樣的:
現在,假設我們運行git reset HEAD~(后面可能會跟不同的參數)。
第 1 步:移動 HEAD
reset 做的第一件事是移動 HEAD 的指向。 這與改變 HEAD 自身不同(checkout 所做的);reset 移動 HEAD 指向的分支。 這意味著如果 HEAD 設置為 master 分支(例如,你正在 master 分支上),運行 git reset 9e5e6a4 將會使 master 指向 9e5e6a4。
使用 reset –soft,它將僅僅停在那兒。
結合上圖,我們理解一下發生的事情:它本質上是撤銷了上一次 git commit 命令。 當你在運行 git commit 時,Git 會創建一個新的提交,并移動 HEAD 所指向的分支來使其指向該提交。 當你將它 reset 回 HEAD~(HEAD 的父結點)時,其實就是把該分支移回原來的位置,而不會改變索引和工作目錄。
第 2 步:更新索引(–mixed)
接下來,reset 會用 HEAD 指向的當前快照的內容來更新索引。
如果指定 –mixed 選項,reset 將會在這里停止。 這也是默認行為,即如果沒有指定任何選項(在本例中是 git reset HEAD~),reset 將會在這里停止。
現在再看一眼上圖,理解一下發生的事情:它依然會撤銷一上次提交,但還會取消所有暫存。 于是,我們回滾到了所有 git add 和 git commit 的命令執行之前。
第 3 步:更新工作目錄(–hard)
如果使用 –hard 選項,reset 要做的的第三件事情就是讓工作目錄看起來像索引。
現在讓我們回想一下剛才發生的事情:你撤銷了最后的提交(git commit )、git add 和工作目錄中的所有工作。
必須注意,–hard 標記是 reset 命令唯一的危險用法,它也是 Git 會真正地銷毀數據的僅有的幾個操作之一。其他任何形式的 reset 調用都可以輕松撤消,但是 –hard 選項不能,因為它強制覆蓋了工作目錄中的文件。
總結
reset 命令會以特定的順序重寫這三棵樹,在你指定以下選項時停止:
參考資料
【0】《Pro Git》(Scott Chacon, Ben Straub Version 2.1.14, 2018-05-19)
【1】https://www.atlassian.com/git/tutorials/resetting-checking-out-and-reverting
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的git reset 命令详解(一)—— Git 学习笔记 07的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小度智能音箱维修点_小度智能音箱无法唤醒
- 下一篇: 定义一个圆的类,输入半径,计算周长和面积