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. "reset" 将旧头复制到.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. "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 功能交互式地选择要添加的 diff 块。这将按顺序询问你每个 diff 块,你可以使用简单的命令,例如“yes,包含此块”、“No,不包含此块”,甚至非常强大的“edit”功能。

  3. 对要包含的块感到满意后,你应该使用 git diff --cached 验证为第一个提交准备的内容。这显示了已移入索引并即将提交的所有更改。

  4. 接下来,提交存储在索引中的更改。-c 选项指定从你最初在第一个提交中使用的原始消息中预先填充提交消息。这有助于避免重新键入它。HEAD@{1} 是一个特殊符号,表示在原始重置提交(1 个更改前)之前 HEAD 所在的提交。有关更多详细信息,请参阅 git-reflog[1]。你也可以使用任何其他有效的提交引用。

  5. 你可以多次重复步骤 2-4,将原始代码拆分为任意数量的提交。

  6. 现在,你已将许多更改拆分为其自己的提交,并且可能不再使用 git add 的补丁模式来选择所有剩余的未提交更改。

  7. 再次检查以验证你是否包含了所需内容。你可能还希望验证 git diff 是否没有显示任何剩余的更改以供稍后提交。

  8. 最后创建最终提交。

讨论

下表显示运行以下命令时会发生什么情况

git reset --option target

HEAD 重置到另一个提交(target),并根据文件的不同状态使用不同的重置选项。

在这些表中,ABCD 是文件的某些不同状态。例如,第一个表的第 1 行表示如果一个文件在工作树中处于状态 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