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

名称

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) [<目录树对象>] [--] [<路径规范>…​]

在索引和`<tree-ish>(默认为`HEAD)之间的差异中交互式地选择猎物。 选择的猎物将被反向应用到索引中。

这意味着 git reset -pgit add -p 相反,也就是说,你可以用它来有选择地重置 hunks。参见 git-add[1] 的 ‘`互动模式’'部分,了解如何操作`--patch`模式。

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

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

--soft

完全不碰索引文件或工作树(但将头部重置为`<commit>`,就像所有模式一样)。这使你的所有更改的文件都是 "待提交的更改",正如`git status`所说的。

--mixed

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

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

--hard

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

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

--pathspec-file-nul

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

--

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

<pathspec>…​

限制受操作影响的路径。

更多细节请参见 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. 有人要求你拉,而且这些变化听起来值得合并。

  3. 然而,你已经破坏了索引(也就是说,你的索引与`HEAD`提交不匹配)。 但你知道你要做的拉动并不影响`frotz.c`或`filfre.c`,所以你恢复了这两个文件的索引修改。 你在工作树上的修改仍然存在。

  4. 然后你可以拉出并合并,留下`frotz.c`和`filfre.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 --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 ;# 你在 "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修改,作为 "快照WIP"。 这样就可以更新索引,将你的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. 在理想的世界里,你可以在创建并切换到 "分支2"(即 "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 "工具,交互式地选择要添加的diff hunks。这将依次询问每个差异块,你可以使用简单的命令,如 "是,包括这个","不,不包括这个",甚至是非常强大的 "编辑 "工具。

  3. 一旦对你想要包括的hunks感到满意,你应该通过使用`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),根据文件的状态,有不同的重置选项。

在这些表中,ABC`和`D`是一个文件的一些不同状态。例如,第一个表格的第一行意味着如果一个文件在工作树中处于`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(保持) (不允许)
工作索引 HEAD 目标工作索引 HEAD
----------------------------------------------------
 A B C C --soft(软的) A B C
			  --mixed(混合型) A C C
			  --hard(硬性) C C C
			  --merge(合并)(不允许)
			  --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