“git reset --hard HEAD~1”和“git reset --soft HEAD~1”的区别是什么?

147
我尝试在git中撤销我的提交。使用git reset --hard HEAD~1是否危险? git reset的不同选项有什么区别?

5个回答

304

git reset 命令有五种 "模式":soft、mixed、hard、merge 和 keep。本文将从前三种模式开始介绍,因为这是你通常会遇到的模式。接下来你会发现一个不错的小奖励,所以请继续关注。


假设你有一个类似于以下历史记录的代码库:

7e05a95  (HEAD -> main) Update a
e62add5  Update b
ca9ae0a  Update a
9b6060d  Add c
eebe372  Add b
947586a  Add a

最新提交的版本 (7e05a95) 包含以下更改:

diff --git a/a b/a
index b66ba06..28b68e2 100644
--- a/a
+++ b/a
@@ -1 +1 @@
-new content
+new new content

如果你使用不同模式的git reset,会发生什么呢?让我们来看看吧!

软重置(soft)

当你使用 git reset --soft HEAD~1 命令时,你将从当前分支中移除最近一次提交,但文件更改将保留在工作树中。此外,更改也将保留在索引中,因此接下来如果执行git commit ,将创建一个包含与之前“删除”的提交完全相同的更改的提交。

这在实践中是怎样的呢?像这样:

> git reset --soft HEAD^ # Assuming HEAD points at 7e05a95
> git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    modified:   a

你可以看到文件a中的更改已经在索引中,并且准备好再次提交。

混合模式

这是默认模式,与软重置相似。当使用git reset HEAD~1来“移除”一次提交时,您仍将保留工作树中的更改,但不会保存到索引中;因此,如果您想“重新做”提交,则需要在提交之前添加更改(git add)。

实际上,结果可能看起来像这样:

> git reset --mixed HEAD^ # Assuming HEAD points at 7e05a95
> git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   a

no changes added to commit (use "git add" and/or "git commit -a")

文件a的更改仍然存在,但它们未在索引中。

hard

使用命令git reset --hard HEAD~1时,除了最后一次提交引入的更改外,您还将丢失所有未提交的更改和所有未跟踪的文件。这些更改不会留在您的工作树中,因此运行git status命令会告诉您在您的存储库中没有任何更改。

请小心谨慎。如果意外删除了从未被git跟踪(即:提交或至少添加到索引中)的未提交更改,则无法使用git获取它们。

一个实际的例子可能是这样的:

> git reset --hard HEAD^ # Assuming HEAD points at 7e05a95
> git status
On branch main
nothing to commit, working tree clean

正如你所看到的,没有任何更改留下。假设你在文件b中也有一些未提交的更改,这些更改也会丢失!

> echo 'some uncommitted changes' > b
> git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   b

no changes added to commit (use "git add" and/or "git commit -a")

> git reset --hard HEAD^ # Assuming HEAD points at 7e05a95
> git status
On branch main
nothing to commit, working tree clean

奖金

保留

git reset --keep HEAD~1 是一个有趣且有用的命令。它只会重置在当前HEAD和给定提交之间不同的文件。如果其中一个或多个文件存在未提交更改,它将中止重置。这基本上是hard的更安全版本。

让我们重新审视一下之前的示例,其中你有一些未提交更改在b中:

> echo 'some uncommitted changes' > b
> git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   b

no changes added to commit (use "git add" and/or "git commit -a")

> git reset --keep HEAD^ # Assuming HEAD points at 7e05a95
> git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   b

no changes added to commit (use "git add" and/or "git commit -a")
你在文件a中删除了更改,但保留了文件b中未提交的更改!
重申一下:使用“hard”将删除所有更改,而使用“keep”仅删除来自重置提交的更改。
每种模式都在git重置文档中有详细解释。
注意:使用git reset删除提交时,提交并不会真正丢失,只是没有指向它或任何子提交的引用。你仍然可以通过找到其SHA-1键来恢复使用git reflog等命令“删除”的提交。

1
我不同意这三个通常是我们应该使用的。它们是最早可用的三个,所以人们更多地谈论这三个,但是--hard几乎从来不是正确的选择,因为--keep更安全,并适用于大多数--hard适用的情况。训练你的手指使用--keep可能会在某一天拯救你... - Matthieu Moy
1
我并没有试图建议我们“应该”使用它们,只是这些命令是我们大多数时候遇到的。请随意根据您的需要编辑答案。 - Sascha Wolf
为了增加一些细节,执行 git reset --soft HEAD~1 后,使用 git commit --reuse-message=HEAD@{1} 以保留旧索引重用最后一次提交,如此处所示 https://dev59.com/9WQn5IYBdhLWcg3wamfO#25930432。 - englealuze
5
@MatthieuMoy,虽然迟到了三年,但我添加了一个“保留”(keep)的部分。;) - Sascha Wolf

13

Git reset有5种主要模式:soft、mixed、merged、hard、keep。它们之间的区别在于是否更改head、stage(索引)、工作目录

Git reset --hard会更改head、索引和工作目录。
Git reset --soft仅更改head,不更改索引和工作目录。

换句话说,如果您想撤消提交,--soft就足够了。但是之后,您仍然可以在索引和工作目录中看到坏提交的更改。您可以修改文件,将其修复,将其添加到索引并再次提交。

使用--hard将使您在项目中完全获得干净的板 slate。就好像没有任何来自上一次提交的更改一样。如果您确定这就是您想要的,请继续操作。但是一旦执行此操作,您将完全失去上一个提交。(注意:仍然有方法可以恢复丢失的提交)。


5

+1为官方文档链接。我还要提一下git reset --help,它很好地解释了五种模式中的至少两种(在我看来)。 官方文档链接:https://git-scm.com/docs/git-reset - ThanksForAllTheFish

3
这是使用git reset --hardgit reset --soft之间的主要区别:

--soft

此模式不会触及索引文件或工作树(但像所有模式一样,会将HEAD重置为新的提交)。这将使得所有已修改的文件都成为“待提交的更改”,就像git status所显示的那样。

--hard

重置索引和工作树。自上次提交以来在工作树中对已跟踪文件所做的任何更改都将被丢弃。


1
这很简单,reset --hard <commit-id> 命令可以让你回到某个提交状态,并忘记该提交后所有的更改。而 reset --soft <commit-id> 命令也可以让你回到某个提交状态,但保留了在该提交后未提交的文件,你可以对其进行操作。
注意:如果你在本地机器上进行此操作,则需要强制推送更新的工作内容 git push -f origin <branch-name>,因为远程分支提交 ID 不同步。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接