Reset 區分為 mixed, hard 與 soft 三種模式
咱們就來實際演練一下,看看各自的差異及應用時機吧
前言
Git Reset可用來重置 repository 到特定 commit 結點,換句話說就是可以讓HEAD(最新的commit)移動到指定結點上;這個指令搭配了三種不同參數 soft、mixed(default)及hard 讓使用者調用,我們可以利用其特性在不同場景中達成我們的目的。
知識背景
要開始比較 soft、mixed及hard有什麼不同前,我們先來複習一下Git將檔案存入repository的流程。首先先來簡單定義一下流程中會使用到的三區塊。
1. Working Tree (工作目錄): Git管理的實體資料夾,也就是我們實際操作的資料夾(檔案)
2. Index (系統索引): 存放一堆需要被commit的(異動)文件內容集合,把檔案加入索引稱 Stage 或 Cache。
3. Repository (檔案庫): 是Git存放檔案的位置,許多commit結點(版本)紀錄於此。
以下簡單敘述一下把檔案存入檔案庫流程:
1. 剛開始 working tree 、 index 與 repository(HEAD)資料內容都是一致的
2. 當實體檔案被異動後,此時 working tree 資料內容就會跟 index 及 repository(HEAD)的不一致,而Git知道該那些檔案(Tracked File)被異動過,直接將檔案狀態會調整為 modified (Unstaged files)。
3. 當我們執行 git add 後,會將這些異動檔案內容加入 index 中 (Staged files),所以此時working tree跟index資料內容是一致的,但他們與repository(HEAD)資料內容不一致。
4. 接著執行 git commit 後,將Git索引中所有異動檔案內容提交至 Repository 中,建立出新的 commit 結點(HEAD)後,資料內容又會於 working tree 、 index 與 repository(HEAD) 中保持一致。
實際演練
有了前面的一些概念,說穿了 reset 就只是把HEAD(最新commit點)移動到指定結點上罷了,各參數之間的差異就只是在資料恢復的範圍,決定是否把原來 working tree 或 index 中的資料內容一起 reset 成指定結點。
為了實際進行演練,首先建立出如上圖的 commit 線圖
實體檔案(aoo.txt)於各提交結點之內容
執行前的狀態(HEAD),三者資料內容皆一致
Soft
此模式下會保留 working tree 資料內容,不會異動到目前所有的實體檔案內容;也會保留 index 資料內容,讓 index 與 working tree 資料內容是一致的。就只有 repository 中的檔案內容變更與 reset 目標結點一致,因此原始結點與 reset 結點之間的差異變更集會存在於 index中(Staged files),所以我們可以直接執行 git commit 將 index 中的資料內容提交至 repository 中。當我們想合併「當前結點」與「reset目標結點」間不具太大意義的 commit 紀錄(可能是階段性地頻繁提交)時,可以考慮使用 Soft Reset 來讓 commit 演進線圖較為清晰。
動手做做,首先執行 git reset --soft HEAD~2
來使用soft模式執行reset功能
目前 working tree 檔案內容會保留,不會被 reset
執行 git status
來查看目前檔案狀態 (發現異動保存在index中 [Staged files])
執行 git diff
比對的是「工作目錄(working tree)」與「索引(index)」之間的差異。無任何差異。
執行 git diff --cached HEAD
代表進行「當前的索引狀態(index)」與「當前分支的最新版(repo. HEAD)」比對。差異確實存在於此。
直接用SourceTree來顯示差異如下( index(Staged file) vs. repository[HEAD])
看一下線圖,HEAD已經reset到目標結點上了(存在未提交之異動)
Mixed (default)
此模式下會只保留Working Tree資料內容,不異動到目前所有的實體檔案內容。但會將 Index 與 Repository 中的檔案內容變更與目標結點一致,因此原始結點與Reset結點之間的差異變更集會存在於Working Tree中,Git會知道那些被追蹤檔案(Tracked File)與Index不相同,直接將檔案狀態會調整為 modified (Unstaged files)。我們可以直接執行 git add 將這些異動檔案內容加入 index 中,再執行 git commit 將 Index 中的資料內容提交至Repository中,一樣可以達到合併commit結點之效果;另外最常使用的情境為移除所有Index中準備要提交的檔案(Staged files),我們可以執行 git reset HEAD 來 Unstage 所有已列入 Index 的待提交檔案。
動手做做,執行 git reset HEAD~2
來使用mixed模式執行reset功能
目前working tree檔案內容會保留,不會被 reset
執行 git status
來查看目前檔案狀態 (異動保存在working tree中 [Unstaged files])
執行 git diff
比對的是「工作目錄(working tree)」與「索引(index)」之間的差異。差異確實存在於此。
直接用SourceTree來顯示差異如下(working tree (unstaged files) vs. index )
執行 git diff --cached HEAD
代表進行「當前的索引狀態(index)」與「當前分支的最新版(repo. HEAD)」比對。無任何差異。
看一下線圖,HEAD已經reset到目標結點上了(存在未提交之異動)
Hard
此種模式完全不保留原始 commit 結點的任何資訊,會連同資料夾中實體檔案內容都進行重置,也就是直接將 working Tree、index 及 repository 都重置成目標Reset結點的資料內容。使用情境可以是要放棄目前本地的所有變更時,執行 git reset -hard HEAD 來強制恢復資料內容及狀態;亦或是真的想拋棄目標結點後的所有變更。
動手做做,執行 git reset --hard HEAD~2
來使用 hard 模式執行reset功能
目前working tree檔案內容就不會保留了,檔案內容直接 reset 成目標結點
執行 git status
來查看目前檔案狀態 (沒有任何異動存在)
執行 git diff
比對的是「工作目錄(working tree)」與「索引(index)」之間的差異。無任何差異。
執行 git diff --cached HEAD
代表進行「當前的索引狀態(index)」與「當前分支的最新版(repo. HEAD)」比對。無任何差異。
看一下線圖,HEAD已經reset到目標結點上了(沒有任何未提交之異動)
復原方式
如果不小心執行 reset 或 rebase 等可能會造成版本號消失的指令時,而我們想要恢復但卻在演進線圖(commit log)上找不到commit結點,這個時候就可透過 git reflog 找出所有歷史紀錄,透過 HEAD@{N} 這個特殊的「參考名稱」來對此版本「定位」,進而使用 git reset --hard HEAD@{N} 指令回歸原有版本。
剛執行了 git reset --hard HEAD~2
來重置,此事若後悔想要恢復,線圖是找不到原始commit節點的
這時候可以執行 git reflog
列出所有歷史紀錄,其中HEAD@{0}~HEAD@{4}都是剛剛進行測試時所產生的,因此需要恢復到執行 reset 指令之前的 commit 結點,也就是HEAD@{5}這個結點位置。
執行 git reset --hard HEAD@{5}
回歸原始版本,會連同working tree(工作目錄)檔案也會一併恢復。
看一下線圖,真的回來了,真的沒有什麼回不來的~
參考資訊
git reset soft,hard,mixed之區別深解
希望此篇文章可以幫助到需要的人
若內容有誤或有其他建議請不吝留言給筆者喔 !