简体中文 ▾ 主题 ▾ 最新版本 ▾ git-reset 上次更新于 2.52.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(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>]

此形式将当前分支 HEAD 重置为 <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 之间不同且具有本地更改的文件,则重置将中止。

--recurse-submodules
--no-recurse-submodules

当工作树被更新时,使用 --recurse-submodules 还会根据超级项目中记录的提交,递归地重置所有活动子模块的工作树,同时将子模块的 HEAD 设置为分离状态(detached at that commit)。

有关这三个命令之间的区别,请参阅 git[1] 中的“Reset、restore 和 revert”部分。

选项

-q
--quiet

保持安静,只报告错误。

--refresh
--no-refresh

在混合重置后刷新索引。默认启用。

--pathspec-from-file=<file>

Pathspec 从 <file> 而不是命令行参数传递。如果 <file>-,则使用标准输入。Pathspec 元素由 LFCR/LF 分隔。Pathspec 元素可以按 core.quotePath 配置变量的解释进行引用(参见 git-config[1])。另请参见 --pathspec-file-nul 和全局 --literal-pathspecs

--pathspec-file-nul

仅在与 --pathspec-from-file 一起使用时有意义。路径规范元素由 NUL 字符分隔,所有其他字符都被视为字面值(包括换行符和引号)。

-U<n>
--unified=<n>

生成具有 <n> 行上下文的 diff。如果配置选项未设置,则默认为 diff.context 或 3。

--inter-hunk-context=<n>

在差异块之间显示上下文,最多达指定行数 <number>,从而合并彼此接近的块。默认为 diff.interHunkContext,如果未设置配置选项则为 0。

--

不再将任何后续参数解释为选项。

<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”会将旧的 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. 最后三个提交(HEADHEAD^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 会保留您的本地更改。

中断的工作流程

假设您在进行一个大型更改的过程中被一个紧急修复请求打断。您工作树中的文件还没有准备好提交,但您需要切换到另一个分支进行快速的 bug 修复。

$ 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} 是一个特殊符号,用于表示在原始重置提交(1 次更改之前)之前 HEAD 所指向的提交。有关更多详细信息,请参阅 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] 套件的一部分