git reset --hard HEAD~1 和 git revert HEAD 有什么异同?

  • 本文关键字:git HEAD reset --hard revert git head revert
  • 更新时间 :
  • 英文 :


我正试图解决这个问题。

据我所知,"git reset --hard HEAD~1"将删除最后一个提交,并且该提交不会在"git log"中看到。

"git revert HEAD"将把我放在nano中,在那里我可以编辑提交消息,并且它还将恢复保存为"git log"中的新提交

我说的对吗?

实际上,git revert将生成一个新的提交,该提交取消了另一个给定提交的效果,重播其变更集的负版本。特别是,git revert不会做mercurial会做的事情。

您可以将其视为"维基百科"的取消:还原某些内容被视为修改,就像其他修改一样,并且可以过时。

但是,git reset会将分支指针带回给定位置。根据你指定的参数,--soft--mixed(默认)或--hard,git 将分别只修改分支指针、分支和索引的状态,或者分支、索引和工作目录内容。因此,这是用于完全摆脱分支尖端的方法,但命令可以超越此唯一情况。

请注意,即使您使用git reset --hard或其他内容取消引用提交,此提交也会保留在对象集合中,直到垃圾回收器运行,并且仅在垃圾回收器足够旧(通常为三个月)时才被丢弃。

所以请记住,如果你不小心这样做了,如果你不等待太久,总有办法恢复你的提交。

你是对的....他们可能会给你同样的"最终结果",就工作树的样子而言......但是该项目的结果是完全不同的......如果你问我,如果你只是注意到不需要最后一次提交,只需将其从历史记录中完全删除(git reset --hard)。除非有重大理由不这样做。有人说,一旦出版,就不应该收回......我的看法是,任何经验丰富的 git 用户都知道如何使用 --onto 重新设置内容并指定要跳过的修订版,因此即使它已发布,您也应该将其删除(让其他开发人员知道您做了什么)....但这只是我的假设/意见。

请参阅Git Revert,Checkout和Reset之间的区别是什么? 但我们应该从更基本的东西开始。

我们为什么要使用版本控制?

版本控制系统的目的是永远保存所有已完成的工作。 好吧,除非不是:有时目的是将一些事情永远保存下来,有些事情保存一段时间有些事情保存很短的时间。

Git 将这些保存为完整快照的方式,我们称之为提交,它们还带有一些关于提交的额外信息,我们称之为元数据元数据包括提交者的姓名和电子邮件地址,以便我们可以询问他们为什么提交,以及日志消息,以便他们可以告诉我们他们为什么提交,而无需我们打扰他们。 Git 中的元数据还包括先前或提交的概念。 通过将父快照与此特定快照进行比较,Git 可以告诉我们进行提交的人更改了什么。

考虑到这一点,我们可以看看这三个 Git 动词(我也要加入git checkout):

git checkout是在某个时候完成一些事情

我们使用git checkout来获取一个特定的提交。 提交是某人在某个时间制作的快照。 据推测,该快照出于某种目的。 我们使用git checkout来获取快照,就像当时所做的那样,无论我们的下一个目的是什么。

在 Git 中,作为将git checkout分支名称一起使用的副作用,我们现在准备做新的工作。 但是我们也可以将git checkout与原始提交哈希一起使用,之后新的提交是......嗯,有点棘手。 (它们仍然是可能的,但 Git 调用这种分离的 HEAD模式,在你对 Git 有了更多的了解之前,你可能不想使用它。

例如,git checkout master致力于在master上获取最新提交的原因是,每次我们在master上进行提交时,Git 都会自动更新我们的名字master,以便它意味着最新的此类提交。 最新的提交会记住它的父级,而父级曾经是最新的。 第二个一回提交会记住它的父级,当单回提交也不存在时,父级是最新的,依此类推。

这意味着名称实际上master找到最后一个提交,从中找到每个较早的提交:

... <-F <-G <-H   <--master

其中每个大写字母代表提交哈希 ID。 我们说每个提交都指向其父级,master指向最新的提交。

git revert是回退错误的提交

鉴于每次提交都记录其父级,并且 Git 可以因此告诉我们进行提交的人更改了什么,我们总是可以让 Git撤消其他人(甚至是我们自己的更改)。 我们选择一个提交,将其视为更改 - 这就是 Git 在使用git log -pgit show时向我们显示的方式 - 并发现,嘿,更改是错误的。 该更改应退出或"还原"。1


1这里的动词还原实际上是一个糟糕的选择。 最常见的英语定义几乎总是跟着辅助词to,如恢复到,它的意思是回到以前的状态。 但是,放弃一些改变并不一定会让我们回到旧状态! 只有当我们退出最近的更改时,我们才会返回到以前的状态。

其他版本控制系统使用动词backout,这更好。 无论如何,当我们使用此动词时,Git 会进行新的提交,保存一个新的快照,就像我们之前的结帐一样,只是它已经撤销了某人的更改。 好吧,也就是说,除非存在合并冲突,否则 Git 会进行此提交,但我们在这里会忽略这种可能性。


git reset是...好吧,很混乱,但我们可以使用它来丢弃提交

Git 的重置动词非常复杂。 在一种特定形式中,它最多做三件事。 对于其他形式,它做其他事情。 你特别问过的那个,git reset --hard HEAD~1,告诉 Git :

  1. 使当前分支名称(无论是什么)指向当前提交的父级。
  2. 擦除当前索引(我们在这里没有描述,但索引暂存区域甚至缓存实际上只是 Git 中同一事物的三个名称)并从步骤 1 中选择的提交中填写它。
  3. 在重置索引之前,请删除索引附带的所有工作树文件,并将它们替换为从步骤 1 中选择的提交中提取并在步骤 2 期间复制到索引中的副本。

因此,如果我们有:

... <-F <-G <-H   <--master

我们更改了名称master以指向G,将提交H推开:

H
/
... <-F <-G   <-- master

哈希值H的提交现在实际上丢失了,就好像它从未进行过一样。 它仍然在存储库中,只是很难找到。 假以时日,如果我们不采取任何其他措施来保护它,承诺H真的会消失。

记住我们提交的目的

我们希望提交,以便它们永远保存所有已完成的工作。 但有时,我们所做的——比如,也许,做出承诺H——是一个错误:

...--F--G--H--I--J--K--L   <-- master

如果我们不久前做了H并且它都像这样嵌入,那么很难删除,因为每个提交都被完全冻结,所以要删除H,我们必须I复制到一个新的不同的提交I'G作为其父级,然后将J复制到以I为父级的新提交, 等等:

H--I--J--K
/
...--F--G--I'-J'-K'  <-- master

在这里更容易 恢复H,添加一个新的提交来撤消我们在H中更改的任何内容。 提交IK保持不变 - 可能略有中断,但这就是它们一直以来的真实情况 - 现在我们有一个新的提交L来撤消我们在H中所做的操作:

...--F--G--H--I--J--K--L   <-- master

但是,如果H是最近的,我们可以使用git reset --hard. 我们会忘记我们曾经犯过那个错误。 没有必要告诉其他人。

最新更新