標題: git reset --解釋
發表:真實
封面圖:https://thepracticaldev.s3.amazonaws.com/i/15wzb8t5310txcukt7t9.png
標籤: git, 重置
描述:「重置可能是最難理解的git 命令之一,而且還因危險而名聲不佳。這兩種說法都有充分的理由:是的,重置命令有點難以理解,並且在某些情況下在這種情況下,這可能很危險。
Reset 可能是最不被理解的 git 命令之一,而且還因危險而名聲不佳。這兩種說法都有一個合理的理由:是的,重置命令有點難以理解,在某些情況下,它可能會很危險。但是,這並不那麼難。因此,在這篇文章中,我將盡力向您展示重置命令的清晰而精煉的教程。為了簡短起見,我抽象化了一些非必要的細節並簡化了一些事情,但如果你想了解更多關於 git 的內部工作原理,你也可以查看我的理解 Git系列,了解這裡介紹的一些內容的更多細節。
在我們深入reset
指令之前,我們需要先看一下所謂的 git 樹:工作目錄、暫存區和儲存庫。
從 git 的角度來看,您可以將它們視為可以進行更改的區域:
工作目錄 - 檔案系統上的專案文件
暫存區 - 下次提交的預覽
儲存庫 - git 保存所有(過去)提交的資料儲存。
reset
命令對這三個/區域進行操作,但首先,讓我們看看我們日常使用的add
和commit
命令如何影響這些區域。
假設我們有一個 Web 應用程式,並且我們對index.php
檔案進行了一些重構。我們所做的更改反映在工作目錄中:
我們可以透過執行git status
來確認這一點:
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.php
現在我們使用add
命令將這些變更移至暫存區域:
現在執行status
指令會告訴我們:
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.php
因為status
指令發現我們在工作目錄和暫存區域都有相同版本的index.php
文件,但在儲存庫中卻沒有。
要將其加入到此處,我們使用commit
命令:
現在工作目錄、暫存區和儲存庫都包含相同版本的index.php
,執行git status
會告訴我們有:
nothing to commit, working tree clean
因此, status
指令的工作方式是比較工作目錄、暫存區域和儲存庫中的檔案版本,如果存在不同,則存在要暫存/提交的檔案。
假設我們現在進一步重構index.php
檔案並再次執行整個新增/提交週期。
現在我們的工作目錄、暫存區和儲存庫都包含了新的第二個版本的index.php
檔案。
但是第一個版本呢?如果您還記得,我們確實說過儲存庫保留所有先前的提交,因此index.php
檔案的第一個版本仍然存在:
為了追蹤我們的index.php
檔案的當前版本,儲存庫有一個名為HEAD
的特殊指針,它指向當前版本( status
命令在將其與當前版本進行比較時僅查看HEAD
指向的當前版本)暫存區的版本)。
現在我們已經了解了這些內容,我們終於可以轉到reset
命令並透過操作這些區域的內容來查看它是如何運作的。
reset
指令的第一種模式只會做一件事:
HEAD
指針。在我們的例子中,我們將透過執行以下操作將其移至先前的提交( index.php
的第一個版本): git reset --soft HEAD~1
git 的樹現在看起來像這樣:
如果我們執行git status
我們會看到一條熟悉的訊息:
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.php
因此,執行git reset --soft HEAD~1
基本上撤消了我們上次的提交,但該提交中包含的更改不會丟失 - 它們位於我們的暫存區域和工作目錄中。
reset
命令的第二種模式將做兩件事:
移動HEAD
指針
更新暫存區(使用HEAD
指向的內容)
因此,第一步與--soft
模式相同。第二步驟取得HEAD
指向的內容(在本例中,它是index.php
檔案的版本一)並將其放入暫存區。
因此,在執行git reset --mixed HEAD~1
後,我們的區域如下所示:
如果我們現在執行git status
,我們會再次看到一條熟悉的訊息:
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.php
因此,執行git reset - mixed HEAD~1
已經撤銷了我們上次的提交,但是這次該提交的更改(僅)在我們的工作目錄中。
現在是臭名昭著的困難模式。執行reset -- hard
會做三件事:
移動HEAD
指針
更新暫存區(使用HEAD
指向的內容)
更新工作目錄以符合暫存區域
因此,前兩個HEAD
與--mixed
相同。
因此,在執行git reset --hard HEAD~1
後,我們的區域如下所示:
執行git status
將為我們提供:
nothing to commit, working tree clean
因此,執行git reset - hard HEAD~1
已經撤消了我們上次的提交,並且該提交中包含的更改現在既不在我們的工作目錄中,也不在暫存區域中。但他們並沒有完全迷失。 Git 不會從存儲庫中刪除提交(實際上,有時會這樣做,但很少),因此這意味著我們的第二個版本的提交仍在存儲庫中,只是它有點難找到(您可以通過看看來追蹤它)稱為reflog 的東西)。
那麼,重置危險的名聲到底是什麼呢?嗯,在一種情況下,某些更改可能會永久丟失。考慮這樣一種情況,在第二次提交之後,您對index.php
檔案進行了更多更改,但沒有暫存並提交它們:
現在執行git reset --hard HEAD~1
:
由於reset
命令將覆蓋工作目錄的內容以匹配暫存區域(即與HEAD
匹配),並且您從未暫存並提交更改(存儲庫中沒有提交這些更改),因此所有這些更改都將現在迷失在時間裡……就像雨中的淚水。
硬重置的危險在於它不是工作目錄安全性- 這意味著如果您的工作目錄中有文件更改,並且如果您執行它,這些文件更改將被覆蓋(並丟失),它不會向您發出任何警告。因此,硬重置時要(格外)小心。
現在你已經有它了: reset
指令。我希望我很好地解釋了它,並且你會同意這畢竟不那麼難。而且,是的,它可能很危險,但前提是與--hard
選項一起使用。
如一開始所說,如果你想了解更多 git 的內部工作原理,你可以查看我的《理解 Git》系列,如果你想更深入地了解reset
命令,你可以查看 git 的Reset Demystified章節親書。
HEAD~1
作為reset
指令的參數。你可能已經知道 git 中的每個提交都有一個稱為校驗和的唯一標識符,我們也可以將它用作reset
命令的參數HEAD
指針通常不會直接指向提交(為了簡單起見,如範例所示),而是指向一個分支指針,然後該分支指針指向特定的提交