Git
简体中文 ▾ Topics ▾ Latest version ▾ git-reset last updated in 2.39.3

名称

git-reset - 重置当前HEAD到指定的状态

概述

git reset [-q] [<目录树对象>] [--] <路径规范>…​
git reset [-q] [--pathspec-from-file=<文件> [--pathspec-file-nul]] [<目录树对象>]
git reset (--patch | -p) [<目录树对象>] [--] [<路径规范>…​]
git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<提交>]

描述

在前三种形式中,从 <treeish> 复制条目到索引。 在最后一种形式中,将当前的分支头( HEAD )设置为 <commit> ,可选地修改索引和工作树以匹配。在所有形式中, <tree-ish> / <commit> 默认为 HEAD

git reset [-q] [<目录树对象>] [--] <路径规范>…​
git reset [-q] [--pathspec-from-file=<文件> [--pathspec-file-nul]] [<目录树对象>]

这些形式将所有匹配 <路径规范> 的路径的索引条目重置为 <目录树对象> 时的状态。 (它不会影响工作目录树或当前分支。)

这意味着, git reset <pathspec>git add <pathspec> 相反。该命令等同于 git restore [--source=<treeish>] --staged <pathspec>...

运行 git reset <pathspec> 更新索引条目后,您可以使用 git-restore[1] 将索引中的内容检出到工作树中。或者,使用 git-restore[1] 并使用 --source 指定一个提交,您可以一次性将提交中某个路径的内容复制到索引和工作树中。

git reset (--patch | -p) [<目录树对象>] [--] [<路径规范>…​]

交互式选择索引和 <目录树对象> (默认为 HEAD )之间差异的代码块。选择的代码块将逆序应用于索引。

这意味着 git reset -pgit add -p 相反,即您可以用它来有选择地重置代码块。参见 git-add[1] 的 "交互模式"部分,了解如何操作 --patch 模式。

git reset [<模式>] [<提交>]

这种形式将当前分支头重置为 <提交> ,并可能根据 <模式> 更新索引(将其重置为 <提交> 的树)和工作目录树。在操作之前, ORIG_HEAD 会被设置为当前分支的顶端。如果省略 <模式> ,则默认为 --mixed<模式> 必须是以下之一:

--soft

完全不触及索引文件或工作树(但将头重置为 <提交> ,就像所有模式都做的那样)。这使所有已更改的文件保持 "Changes to be committed" 状态,就像 git status 会显示的那样。

--mixed

重置索引,但不重置工作树(即保留已修改的文件,但不标记为提交)并报告哪些内容未更新。这是默认动作。

如果指定了 -N ,则删除的路径将被标记为要添加(请参阅 git-add[1])。

--hard

重置索引和工作树。工作树中自 <提交> 以来对跟踪文件的任何更改都会被丢弃。 任何妨碍写入跟踪文件的未跟踪文件或目录都会被删除。

--merge

重置索引并更新工作树中与 <提交>HEAD 之间不同的文件,但保留那些在索引和工作树之间不同的文件(即尚未被添加的修改)。如果 <提交> 和索引之间不同的文件有未暂存的更改,重置操作将被中止。

换句话说, --merge 做的事情类似于 git read-tree -u -m <提交> ,但会保留尚未合并的索引条目。

--keep

重置索引条目并更新工作树中与 <提交>HEAD 之间不同的文件。 如果 <提交>HEAD 之间不同的文件有本地修改,重置操作将被中止。

--[no-]recurse-submodules

当更新工作树时,使用 --recurse-submodules 还将递归性地根据超级项目中记录的提交来重置所有活动子模块的工作树,同时将子模块的 HEAD 设置为分离在该提交处。

关于这三个命令的区别,见git[1]中的 "重置、恢复和还原"。

选项

-q
--quiet

静默模式,只报告错误。

--refresh
--no-refresh

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

--pathspec-from-file=<file>

Pathspec 在 <文件> 中传递,而不是在命令行参数中传递。如果 <文件> 正好是 -,则使用标准输入。路径规范元素由 LF 或 CR/LF 分隔。可以引用配置变量 core.quotePath 的路径规范元素(请参见 git-config[1])。另请参见 --pathspec-file-nul 和全局 --literal-pathspecs

--pathspec-file-nul

只有在使用 --pathspec-from-file 选项时才有意义。指定路径元素用 NUL 字符分隔,所有其他字符都按字面意思(包括换行符和引号)表示。

--

不将之后的参数解释为选项。

<路径规范>…​

限制受操作影响的路径。

更多细节请参见 gitglossary[7] 中的 路径规范 条目。

实例

撤销添加
$ 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. 有人要求你 pull,而且这些变化听起来值得合并。

  3. 然而,你已经破坏了索引(也就是说,你的索引与 HEAD 提交不匹配)。但你知道你要做的 pull 并不影响 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. 回溯主分支,去掉这三个提交。

  3. 切换到 topic/wip 分支并继续工作。

永久撤销提交
$ git commit ...
$ git reset --hard HEAD~3 (1)
  1. 最后三个提交(HEADHEAD^,和 HEAD~2 )是坏的,你不希望再看到它们。 如果你已经把这些提交给了别人,请 不要 这样做。 参见 git-rebase[1] 中的 “从上游仓库重建中恢复” 一节,了解这样做的意义。)

撤销合并或拉动
$ git pull                         (1)
自动合并nitfol
CONFLICT(内容):nitfol的合并冲突
自动合并失败;修复冲突,然后提交结果。
$ git reset --hard                 (2)
$ git pull . topic/branch          (3)
从41223...更新到13134...
快进
$ git reset --hard ORIG_HEAD       (4)
  1. 试图从上游更新导致了很多冲突;你现在还没有准备好花费大量的时间来合并,所以你决定以后再做这个。

  2. "pull " 没有进行合并提交,所以 git reset --hardgit 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 ;# 你在 "feature "分支工作,并且
$ work work work;# 被打断了
$ git commit -a -m "snapshot WIP"               (1)
$ git switch master
$ fix fix fix
$ git commit ;# 提交时有真实日志
$ git switch feature
$ git reset --soft HEAD^ ;# 回到 WIP 状态         (2)
$ git reset                                       (3)
  1. 这个提交将会被丢弃,所以可以使用一个临时的提交信息。

  2. 这将从提交历史中删除 "WIP" 提交,并将你的工作树设置为刚刚做出快照之前的状态。

  3. 此时,索引文件仍然包含您提交为 "快照 WIP" 的所有工作进度更改。这会更新索引来显示您的工作进度文件为未提交状态。

    也请参见 git-stash[1]

重置索引中的单个文件

假设你在索引中添加了一个文件,但后来决定不想把它加入你的提交中。你可以用 git reset 将该文件从索引中删除,同时保留你的修改。

$ git reset -- frotz.c <1>.
$ git commit -m "将文件存入索引" (2)
$ git add frotz.c (3)
  1. 这将从索引中删除该文件,同时将其保留在工作目录中。

  2. 这将提交索引中的所有其他变化。

  3. 再次将该文件添加到索引中。

在工作树中保留修改,同时丢弃一些以前的提交内容

假设你正在处理一些事情,并提交了它,然后你又继续做了一会儿,但现在你认为你在工作树中的内容应该位于另一个与您之前提交的内容无关的分支中。你可以启动一个新的分支并在保留工作树中更改的情况下重置它。

$ git tag start
$ git switch -c branch1
$ 编辑
$ git commit ...                            (1)
$ 编辑
$ git switch -c branch2                     (2)
$ git reset --keep start                    (3)
  1. 这将提交你在 branch1 中的第一次编辑。

  2. 在理想的世界里,你可以在创建并切换到 "branch2"(即 "git switch -c branch2 start")时意识到先前的提交不属于新主题,但人无完人。

  3. 但你可以用 reset --keep 来删除你切换到 branch2 后不需要的提交。

将一个提交分割成一连串的提交

假设你创建了很多逻辑上独立的修改,并将它们一起提交。然后,后来你决定让每个逻辑块与自己的提交相关联可能更好。你可以使用 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 目标

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

在这些表中, ABCD 是一个文件的一些不同状态。例如,第一个表格的第一行意味着如果一个文件在工作树中处于 A 状态,在索引中处于 B 状态,在 HEAD 中处于 C 状态,在目标中处于 D 状态,那么 git reset --soft target 将使文件在工作树中处于 A 状态,在索引中处于 B 状态。 它重设(即移动) HEAD (即当前分支的顶端,如果你在一个分支上)到 target (它的文件处于 D 状态)。

工作树  索引  HEAD  目标                 工作树   索引   HEAD
------------------------------------------------------------
  A      B     C     D    --soft           A       B      D
			  --mixed          A       D      D
			  --hard           D       D      D
			  --merge (不允许)
			  --keep (不允许)
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
工作树  索引  HEAD  目标                 工作树   索引   HEAD
------------------------------------------------------------
  B      B     C     D     --soft          B       B      D
			  --mixed         B       D      D
			  --hard          D       D      D
			  --merge         D       D      D
			  --keep(不允许)
工作树  索引  HEAD  目标                 工作树   索引   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
工作树  索引  HEAD  目标                 工作树   索引   HEAD
------------------------------------------------------------
  B      C     C     D     --soft          B       C      D
			  --mixed         B       D      D
			  --hard(硬的)    D       D      D
			  --merge (不允许)
			  --keep (不允许)
工作树  索引  HEAD  目标                 工作树   索引   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 和目标之间都有更改时不允许重置。为了安全起见,当有未合并的条目时,也不允许这样做。

下表显示了有未合并的条目时发生的情况:

工作树  索引  HEAD  目标                 工作树   索引   HEAD
------------------------------------------------------------
  X      U      A     B    --soft(不允许)
			  --mixed         X       B      B
			  --hard          B       B      B
			  --merge         B       B      B
			  --keep (不允许)
工作树  索引  HEAD  目标                 工作树   索引   HEAD
------------------------------------------------------------
   X     U     A     A     --soft (不允许)
			  --mixed         X       A      A
			  --hard          A       A      A
			  --merge         A       A      A
			  --keep (不允许)

X 指任何状态, U 指未合并的索引。

GIT

属于 git[1] 文档

scroll-to-top