英文:
Commit the state of a previous commit again on top of the head commit
问题
我们有一些更改,其中包含许多提交,但没有成功。我们希望保留这些更改的历史记录,但现在我们想要将一个较旧的提交状态放在这些更改的顶部:
当前状态:
A -> B1 -> B2 -> ... -> Bn
目标状态:
A -> B1 -> B2 -> ... -> Bn -> A
我们的目标是保留B的更改,并使代码看起来像只有A时的样子。有办法实现这个目标吗?
英文:
We had some changes with a large number of commits that did not work out. We want to keep those in the history, but now we want to get the state of an older commit to be on top of these:
Current:
A -> B1 -> B2 -> ... -> Bn
Goal:
A -> B1 -> B2 -> ... -> Bn -> A
The goal is to keep the Bs and that the code looks like when we had A only. Is there a way to achieve this?
答案1
得分: 5
这样做的方法是使用恢复(restore)命令,现在的做法是这样的:
git restore --source=A --worktree --staged -- .
git commit -m "将其恢复到 A 的状态"
然后你会得到如下的历史记录:
A <- B1 <- B2 <- ... <- Bn <- A'
我将箭头反转是因为在 git
中,子节点指向父节点,而不是反过来。
英文:
The way to do that is with a restore, these days:
git restore --source=A --worktree --staged -- .
git commit -m "Taking it back to how A was"
Then you get a history like this:
A <- B1 <- B2 <- ... <- Bn <- A'
I reversed the arrows because in git
, children point to parents, not the other way around.
答案2
得分: 2
commit-tree命令可能看起来有点复杂,但你的情况可能是它的理想用例。
从你的分支开始,从一个干净的工作树开始,在一个命令中:
(这里的A
当然需要替换为实际的哈希值或指向它的引用)
git reset --hard $(git commit-tree -m "我们返回到状态A,因为XYZ" -p $(git rev-parse HEAD) $(git rev-parse A^{tree}))
现在我们逐个部分来看,使其更清晰:
我们使用命令签名git commit-tree [-m <message>] -p <parentCommit> <tree>
,其中tree
是要创建新提交的树引用。
$(git rev-parse A^{tree})
从A
获取树对象引用。
-p $(git rev-parse HEAD)
将HEAD设置为父提交。
最后,我们将新提交作为参数发送给顶级的git reset --hard
命令,使分支指向刚创建的新提交。
现在,你的分支上有一个新的提交,文件的状态与A
中的状态完全相同。
作为一个额外的福利,对于那些(每个人?)不想键入(或完全记住)所有这些内容的人,可以使用"reset-by-commit-tree"别名:
git config alias.rbct '!f() { git reset --hard $(git commit-tree -m "(reset-by-commit-tree) 返回到 $1" -p $(git rev-parse HEAD) $(git rev-parse $1^{tree})); }; f
# 然后,只需调用它
git rbct <commit-to-return-to>
英文:
The commit-tree command might look slightly convoluted, but your situation is maybe its ideal use-case.
From your branch, starting with a clean working tree, in a single command:<br>
(A
has of course to be replaced here by its actual hash or any ref pointing to it)
git reset --hard $(git commit-tree -m "We returned to state A because XYZ" -p $(git rev-parse HEAD) $(git rev-parse A^{tree}))
Now to make it clearer by looking at it bit by bit:
We use the command signature git commit-tree [-m <message>] -p <parentCommit> <tree>
where tree
is the tree ref for the new commit to be created.
$(git rev-parse A^{tree})
gets the tree object ref from A
.
-p $(git rev-parse HEAD)
sets HEAD as the parent commit.
Finally, we send the new commit as an argument to the top-level git reset --hard
command to make the branch point to the new commit just created.
You now have a new commit on top of your branch, and the state of the files is exactly the same as it was in A
.
As a bonus for those (everyone?) who do not want to type (or exactly remember) all this, the "reset-by-commit-tree" alias :
git config alias.rbct '!f() { git reset --hard $(git commit-tree -m "(reset-by-commit-tree) Return to $1" -p $(git rev-parse HEAD) $(git rev-parse $1^{tree})); }; f
'
# then, to invoke it, simply
git rbct <commit-to-return-to>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论