簡體中文 ▾ 主題 ▾ 最新版本 ▾ git-reset 上次更新於 2.50.0

名稱

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 相反,即您可以使用它來選擇性地重置程式碼塊。有關如何操作 --patch 模式的更多資訊,請參閱 git-add[1] 的“互動模式”部分。

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

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

--soft

完全不觸碰索引檔案或工作目錄(但會將 HEAD 重置為 <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 設定為在該提交處處於分離 HEAD 狀態。

有關這三個命令之間的區別,請參閱 git[1] 中的“Reset、restore 和 revert”部分。

選項

-q
--quiet

安靜模式,只報告錯誤。

--refresh
--no-refresh

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

--pathspec-from-file=<file>

路徑規範從 <file> 而非命令列引數中傳入。如果 <file> 恰好是 -,則使用標準輸入。路徑規範元素由 LFCR/LF 分隔。路徑規範元素可以像配置變數 core.quotePath 所解釋的那樣進行引用(請參閱 git-config[1])。另請參閱 --pathspec-file-nul 和全域性 --literal-pathspecs

--pathspec-file-nul

僅在與 --pathspec-from-file 一起使用時有意義。路徑規範元素由 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. 這通常發生在您想起剛剛提交的內容不完整,或者拼錯了提交訊息,或兩者兼而有之時。工作目錄將保持“reset”之前的狀態。

  2. 對工作目錄檔案進行更正。

  3. “reset”會將舊的 HEAD 複製到 .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. 最後三個提交 (HEAD, HEAD^, 和 HEAD~2) 是不好的,您不希望再次看到它們。如果您已經將這些提交提供給其他人,請不要這樣做。(有關這樣做的影響,請參閱 git-rebase[1] 中的“從上游 rebase 中恢復”部分。)

撤消合併或拉取
$ 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. “pull”尚未建立合併提交,因此 git reset --hard(它是 git reset --hard HEAD 的同義詞)會清除索引檔案和工作目錄中的混亂。

  3. 將主題分支合併到當前分支,這導致了快進合併。

  4. 但您認為該主題分支尚未準備好公開發布。“pull”或“merge”總會將當前分支的原始尖端留在 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. 此時,索引檔案仍包含您作為 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 功能互動式選擇要新增的差異程式碼塊。這會按順序詢問您每個差異程式碼塊,您可以使用簡單的命令,例如“是,包含此項”、“否,不包含此項”,甚至是非常強大的“編輯”功能。

  3. 一旦對要包含的程式碼塊滿意,您應該使用 git diff --cached 驗證為首次提交準備的內容。這會顯示已移入索引並即將提交的所有更改。

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

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

  6. 現在您已經將許多更改拆分到它們自己的提交中,並且可能不再使用 git add 的補丁模式來選擇所有剩餘的未提交更改。

  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

git reset --merge 旨在用於從衝突合併中重置時。任何合併操作都保證,在開始之前,涉及合併的工作目錄檔案相對於索引沒有本地更改,並且它會將結果寫入工作目錄。因此,如果我們在索引和目標之間以及索引和工作目錄之間看到一些差異,則意味著我們不是從合併操作在衝突失敗後留下的狀態中重置。這就是在這種情況下我們不允許使用 --merge 選項的原因。

git 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