Git
English ▾ 主題 ▾ 最新版本 ▾ git-reset 最後更新於 2.39.3

名稱

git-reset - 將目前的 HEAD 重設為指定狀態

概要

git reset [-q] [<tree-ish>] [--] <pathspec>…​
git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>…​]
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]

描述

在前三個形式中,將條目從 <tree-ish> 複製到索引。在最後一個形式中,將目前分支的頭 (HEAD) 設定為 <commit>,可選擇性地修改索引和工作目錄以符合。在所有形式中,<tree-ish>/<commit> 預設為 HEAD

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

這些形式會將所有符合 <pathspec> 的路徑的索引條目重設為它們在 <tree-ish> 的狀態。(它不會影響工作目錄或目前分支。)

這表示 git reset <pathspec>git add <pathspec> 相反。此命令等同於 git restore [--source=<tree-ish>] --staged <pathspec>...

在執行 git reset <pathspec> 以更新索引條目後,您可以使用 git-restore[1] 將內容從索引簽出到工作目錄。或者,使用 git-restore[1] 並指定帶有 --source 的提交,您可以將路徑的內容從提交複製到索引和工作目錄中,一步到位。

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

互動式選取索引與 <tree-ish> (預設為 HEAD) 之間的差異中的區塊。所選的區塊會反向套用到索引。

這表示 git reset -pgit add -p 相反,也就是說,您可以使用它來選擇性地重設區塊。請參閱 git-add[1] 的「互動模式」章節,以了解如何操作 --patch 模式。

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

此形式會將目前分支的頭重設為 <commit>,並可能根據 <mode> 更新索引 (將其重設為 <commit> 的樹狀結構) 和工作目錄。在操作之前,ORIG_HEAD 會設定為目前分支的尖端。如果省略 <mode>,則預設為 --mixed<mode> 必須是以下之一

--soft

完全不觸碰索引檔案或工作目錄 (但會將頭重設為 <commit>,就像所有模式一樣)。這會讓所有變更的檔案保持 "待提交的變更",就像 git status 會顯示的那樣。

--mixed

重設索引但不重設工作目錄 (也就是說,變更的檔案會被保留但不會標記為提交),並報告未更新的內容。這是預設動作。

如果指定 -N,則移除的路徑會標記為意圖新增 (請參閱 git-add[1])。

--hard

重設索引和工作目錄。自 <commit> 以來,工作目錄中對追蹤檔案的任何變更都會被捨棄。任何阻礙寫入任何追蹤檔案的未追蹤檔案或目錄都會被直接刪除。

--merge

重設索引並更新工作目錄中 <commit>HEAD 之間不同的檔案,但保留索引和工作目錄之間不同的檔案 (也就是說,具有未新增的變更)。如果 <commit> 和索引之間不同的檔案有未暫存的變更,則會中止重設。

換句話說,--merge 的作用類似於 git read-tree -u -m <commit>,但會向前傳遞未合併的索引條目。

--keep

重設索引條目並更新工作目錄中 <commit>HEAD 之間不同的檔案。如果 <commit>HEAD 之間不同的檔案有本機變更,則會中止重設。

--[no-]recurse-submodules

當工作目錄更新時,使用 --recurse-submodules 也會根據超專案中記錄的提交,遞迴地重設所有作用中子模組的工作目錄,並將子模組的 HEAD 設定為分離在該提交處。

請參閱 git[1] 中的「重設、還原和復原」,以了解三個命令之間的差異。

選項

-q
--quiet

保持安靜,僅報告錯誤。

--refresh
--no-refresh

在混合重設後重新整理索引。預設為啟用。

--pathspec-from-file=<file>

Pathspec 在 <file> 中傳遞,而不是命令列引數。如果 <file> 完全是 -,則會使用標準輸入。Pathspec 元素以 LF 或 CR/LF 分隔。Pathspec 元素可以依照設定變數 core.quotePath 的說明進行引用 (請參閱 git-config[1])。另請參閱 --pathspec-file-nul 和全域 --literal-pathspecs

--pathspec-file-nul

僅對 --pathspec-from-file 有意義。Pathspec 元素以 NUL 字元分隔,且所有其他字元都會按字面解釋 (包括換行符號和引號)。

--

不要將任何其他引數解譯為選項。

<pathspec>…​

限制作業影響的路徑。

如需更多詳細資料,請參閱 gitglossary[7] 中的 pathspec 條目。

範例

復原新增
$ edit                                     (1)
$ git add frotz.c filfre.c
$ mailx                                    (2)
$ git reset                                (3)
$ git pull git://info.example.com/ nitfol  (4)
  1. 您正愉快地處理某些內容,並發現這些檔案中的變更都井然有序。當您執行 git diff 時,您不希望看到它們,因為您計劃處理其他檔案,而這些檔案的變更會分散您的注意力。

  2. 有人請您提取,而這些變更聽起來值得合併。

  3. 但是,您已經弄髒了索引 (也就是說,您的索引與 HEAD 提交不符)。但您知道您將要進行的提取不會影響 frotz.cfilfre.c,因此您會還原這兩個檔案的索引變更。您在工作目錄中的變更會保留在那裡。

  4. 然後,您可以提取並合併,將 frotz.cfilfre.c 的變更仍保留在工作目錄中。

復原提交並重做
$ git commit ...
$ git reset --soft HEAD^      (1)
$ edit                        (2)
$ git commit -a -c ORIG_HEAD  (3)
  1. 當您想起您剛提交的內容不完整,或者您拼錯了提交訊息,或者兩者都有時,通常會這樣做。將工作目錄保留為「重設」之前的狀態。

  2. 修正工作目錄檔案。

  3. "重設" 會將舊的頭複製到 .git/ORIG_HEAD;從其記錄訊息開始重做提交。如果您不需要進一步編輯訊息,您可以改為提供 -C 選項。

    另請參閱 git-commit[1]--amend 選項。

復原提交,使其成為主題分支
$ git branch topic/wip          (1)
$ git reset --hard HEAD~3       (2)
$ git switch topic/wip          (3)
  1. 您已進行了一些提交,但意識到它們在 master 分支中為時過早。您想要在主題分支中繼續潤飾它們,因此從目前的 HEAD 建立 topic/wip 分支。

  2. 倒回 master 分支以擺脫這三個提交。

  3. 切換到 topic/wip 分支並繼續工作。

永久復原提交
$ git commit ...
$ git reset --hard HEAD~3   (1)
  1. 最後三個提交 (HEADHEAD^HEAD~2) 不好,您不希望再看到它們。如果您已經將這些提交給其他人,請不要這樣做。(請參閱 git-rebase[1] 中的「從上游重新定基底中復原」章節,以了解這樣做的影響。)

復原合併或提取
$ git pull                         (1)
Auto-merging nitfol
CONFLICT (content): Merge conflict in nitfol
Automatic merge failed; fix conflicts and then commit the result.
$ git reset --hard                 (2)
$ git pull . topic/branch          (3)
Updating from 41223... to 13134...
Fast-forward
$ git reset --hard ORIG_HEAD       (4)
  1. 嘗試從上游更新導致許多衝突;您現在還沒有準備好花費大量時間進行合併,因此您決定稍後再做。

  2. "提取" 沒有建立合併提交,因此 git reset --hard (這是 git reset --hard HEAD 的同義詞) 會清除索引檔案和工作目錄中的混亂。

  3. 將主題分支合併到目前分支中,導致快轉。

  4. 但您決定主題分支尚未準備好公開發布。「提取」或「合併」始終將目前分支的原始尖端保留在 ORIG_HEAD 中,因此將其硬重設會將您的索引檔案和工作目錄帶回該狀態,並將分支的尖端重設為該提交。

在髒的工作目錄中復原合併或提取
$ git pull                         (1)
Auto-merging nitfol
Merge made by recursive.
 nitfol                |   20 +++++----
 ...
$ git reset --merge ORIG_HEAD      (2)
  1. 即使你的工作目錄中有本機修改,當你確認其他分支的變更不會與你的修改重疊時,你可以安全地執行 git pull

  2. 在檢查合併的結果後,你可能會發現其他分支的變更不盡如人意。執行 git reset --hard ORIG_HEAD 會讓你回到之前的狀態,但會捨棄你不想丟失的本機修改。 git reset --merge 則會保留你的本機修改。

工作流程中斷

假設你正在進行一個大的變更,但卻被緊急的修復請求打斷。你的工作目錄中的檔案尚未準備好提交,但你需要切換到其他分支進行快速錯誤修復。

$ git switch feature  ;# you were working in "feature" branch and
$ work work work      ;# got interrupted
$ git commit -a -m "snapshot WIP"                 (1)
$ git switch master
$ fix fix fix
$ git commit ;# commit with real log
$ git switch feature
$ git reset --soft HEAD^ ;# go back to WIP state  (2)
$ git reset                                       (3)
  1. 這個提交將被捨棄,所以使用一次性的日誌訊息是可以的。

  2. 這會從提交歷史中移除 *WIP* 提交,並將你的工作目錄設定為你在建立快照之前的狀態。

  3. 此時,索引檔案仍然包含你以 *snapshot WIP* 提交的所有 WIP 變更。這會更新索引,將你的 WIP 檔案顯示為未提交的狀態。

    另請參閱 git-stash[1]

重設索引中的單個檔案

假設你已將一個檔案加入到索引中,但後來決定不將其加入到提交中。你可以使用 git reset 從索引中移除該檔案,同時保留你的變更。

$ git reset -- frotz.c                      (1)
$ git commit -m "Commit files in index"     (2)
$ git add frotz.c                           (3)
  1. 這會將檔案從索引中移除,同時保留在工作目錄中。

  2. 這會提交索引中的所有其他變更。

  3. 再次將檔案加入到索引中。

保留工作目錄中的變更,同時捨棄之前的提交

假設你正在處理某個東西並提交了它,然後你繼續工作了一點,但現在你認為你工作目錄中的內容應該放在另一個與你之前提交的內容無關的分支中。你可以建立一個新的分支並重設它,同時保留工作目錄中的變更。

$ git tag start
$ git switch -c branch1
$ edit
$ git commit ...                            (1)
$ edit
$ git switch -c branch2                     (2)
$ git reset --keep start                    (3)
  1. 這會將你在 branch1 中的第一次編輯提交。

  2. 在理想情況下,當你建立並切換到 branch2 時(例如 git switch -c branch2 start),你應該意識到之前的提交不屬於新主題,但人無完人。

  3. 但是,你可以在切換到 branch2 後使用 reset --keep 來移除不需要的提交。

將一個提交拆分為一系列提交

假設你建立了許多邏輯上獨立的變更並將它們一起提交。然後,後來你決定最好讓每個邏輯區塊與其自己的提交相關聯。你可以使用 git reset 來回溯歷史記錄,而無需變更本地檔案的內容,然後依次使用 git add -p 來互動式地選擇要包含到每個提交中的程式碼片段,並使用 git commit -c 來預先填入提交訊息。

$ git reset -N HEAD^                        (1)
$ git add -p                                (2)
$ git diff --cached                         (3)
$ git commit -c HEAD@{1}                    (4)
...                                         (5)
$ git add ...                               (6)
$ git diff --cached                         (7)
$ git commit ...                            (8)
  1. 首先,將歷史記錄回溯一個提交,以便我們移除原始提交,但將工作目錄保留所有變更。 -N 可確保使用 HEAD 新增的任何新檔案仍然被標記,以便 git add -p 可以找到它們。

  2. 接下來,我們使用 git add -p 功能互動式地選擇要新增的差異程式碼片段。這會按順序詢問你每個差異程式碼片段,你可以使用簡單的命令,例如「yes, include this」、「No don’t include this」甚至是非常強大的「edit」功能。

  3. 一旦對要包含的程式碼片段感到滿意,你應該使用 git diff --cached 來驗證為第一個提交準備的內容。這會顯示所有已移動到索引中並即將提交的變更。

  4. 接下來,提交儲存在索引中的變更。 -c 選項指定從你在第一個提交中開始使用的原始訊息中預先填入提交訊息。這有助於避免重新輸入。 HEAD@{1}HEAD 在原始重設提交(1 次變更之前)所在的提交的特殊表示法。有關更多詳細資訊,請參閱 git-reflog[1]。你也可以使用任何其他有效的提交參考。

  5. 你可以多次重複步驟 2-4,將原始程式碼分成任意數量的提交。

  6. 現在,你已將許多變更拆分到它們自己的提交中,並且可能不再使用 git add 的 patch 模式,以便選擇所有剩餘的未提交變更。

  7. 再次檢查以驗證你已包含要包含的內容。你可能還希望驗證 git diff 是否沒有顯示任何剩餘的變更需要稍後提交。

  8. 最後建立最終提交。

討論

下面的表格顯示執行

git reset --option target

HEAD 重設為另一個提交 (target) 時,根據檔案的狀態使用不同的重設選項會發生什麼情況。

在這些表格中,ABCD 是檔案的一些不同狀態。例如,第一個表格的第一行表示,如果檔案在工作目錄中處於狀態 A,在索引中處於狀態 B,在 HEAD 中處於狀態 C,在目標中處於狀態 D,則 git reset --soft target 會將檔案在工作目錄中保留在狀態 A,在索引中保留在狀態 B。它會將 HEAD(也就是你目前所在分支的頂端,如果你有的話)重設(也就是移動)到 target(該檔案處於狀態 D)。

working index HEAD target         working index HEAD
----------------------------------------------------
 A       B     C    D     --soft   A       B     D
			  --mixed  A       D     D
			  --hard   D       D     D
			  --merge (disallowed)
			  --keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
 A       B     C    C     --soft   A       B     C
			  --mixed  A       C     C
			  --hard   C       C     C
			  --merge (disallowed)
			  --keep   A       C     C
working index HEAD target         working index HEAD
----------------------------------------------------
 B       B     C    D     --soft   B       B     D
			  --mixed  B       D     D
			  --hard   D       D     D
			  --merge  D       D     D
			  --keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
 B       B     C    C     --soft   B       B     C
			  --mixed  B       C     C
			  --hard   C       C     C
			  --merge  C       C     C
			  --keep   B       C     C
working index HEAD target         working index HEAD
----------------------------------------------------
 B       C     C    D     --soft   B       C     D
			  --mixed  B       D     D
			  --hard   D       D     D
			  --merge (disallowed)
			  --keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
 B       C     C    C     --soft   B       C     C
			  --mixed  B       C     C
			  --hard   C       C     C
			  --merge  B       C     C
			  --keep   B       C     C

reset --merge 的目的是在重設衝突的合併時使用。任何合併操作都保證參與合併的工作目錄檔案在開始之前相對於索引沒有本地變更,並且將結果寫入工作目錄。因此,如果我們看到索引和目標之間,以及索引和工作目錄之間存在一些差異,則表示我們不是從合併操作在發生衝突後遺留的狀態中重設。這就是為什麼我們在這種情況下不允許使用 --merge 選項。

reset --keep 的目的是在移除目前分支中的某些最後提交時使用,同時保留工作目錄中的變更。如果我們要移除的提交中的變更與我們要保留的工作目錄中的變更之間可能存在衝突,則不允許重設。這就是為什麼當工作目錄和 HEAD 之間,以及 HEAD 和目標之間都存在變更時,不允許重設。為了安全起見,當存在未合併的條目時,也不允許重設。

下面的表格顯示存在未合併條目時會發生什麼情況

working index HEAD target         working index HEAD
----------------------------------------------------
 X       U     A    B     --soft  (disallowed)
			  --mixed  X       B     B
			  --hard   B       B     B
			  --merge  B       B     B
			  --keep  (disallowed)
working index HEAD target         working index HEAD
----------------------------------------------------
 X       U     A    A     --soft  (disallowed)
			  --mixed  X       A     A
			  --hard   A       A     A
			  --merge  A       A     A
			  --keep  (disallowed)

X 表示任何狀態,U 表示未合併的索引。

GIT

屬於 git[1] 套件的一部分

scroll-to-top