我正试图解决这个问题。
据我所知,"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 -p
或git show
时向我们显示的方式 - 并发现,嘿,更改是错误的。 该更改应退出或"还原"。1
1这里的动词还原实际上是一个糟糕的选择。 最常见的英语定义几乎总是跟着辅助词to,如恢复到,它的意思是回到以前的状态。 但是,放弃一些改变并不一定会让我们回到旧状态! 只有当我们退出最近的更改时,我们才会返回到以前的状态。
其他版本控制系统使用动词backout,这更好。 无论如何,当我们使用此动词时,Git 会进行新的提交,保存一个新的快照,就像我们之前的结帐一样,只是它已经撤销了某人的更改。 好吧,也就是说,除非存在合并冲突,否则 Git 会进行此提交,但我们在这里会忽略这种可能性。
git reset
是...好吧,很混乱,但我们可以使用它来丢弃提交
Git 的重置动词非常复杂。 在一种特定形式中,它最多做三件事。 对于其他形式,它做其他事情。 你特别问过的那个,git reset --hard HEAD~1
,告诉 Git :
- 使当前分支名称(无论是什么)指向当前提交的父级。
- 擦除当前索引(我们在这里没有描述,但索引、暂存区域甚至缓存实际上只是 Git 中同一事物的三个名称)并从步骤 1 中选择的提交中填写它。
- 在重置索引之前,请删除索引附带的所有工作树文件,并将它们替换为从步骤 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
中更改的任何内容。 提交I
到K
保持不变 - 可能略有中断,但这就是它们一直以来的真实情况 - 现在我们有一个新的提交L
来撤消我们在H
中所做的操作:
...--F--G--H--I--J--K--L <-- master
但是,如果H
是最近的,我们可以使用git reset --hard
. 我们会忘记我们曾经犯过那个错误。 没有必要告诉其他人。