7

我想“跟进”关于这个问题的另一个问题: Checkout old commit and make it a new commit

但他们说“不要那样做!” 所以看来我必须问一个新问题。(即使这是同一个问题,我认为最适合我的答案并没有按预期工作......

如果我有提交 A-B-C-D-E-F(想象这些都是 SHA)。我想使整个存储库完全一样C,然后提交它以创建'G'C. 这样当我完成时,即使 C 和 G 相同,日志也是 ABCDEFG。

C一个答案是cherry-pick,这看起来很完美,除了它让我解决了和之间的所有冲突F。我查看了文档,--force无论如何我都尝试了,然后推荐了--patch. --patch最接近,但不是绝对的。那么有没有办法让cherry-pick只选择C冲突的版本?

另一个最接近的答案是 checkout C,但我找不到将它保持在同一个分支上的神奇词。我最近完成的培训说在最后使用“魔术破折号”来告诉git你想要在当前分支上使用它,但无论我做什么,它都会创建一个“(无分支)”分支。

我相信你会说,我对 git(和一般的命令行)很陌生,但我试图自己弄清楚。如果您可以使用您推荐的详细版本,那么它会更好地留在我的脑海中,将不胜感激。( -a=--all-a = --annotate-a = --albuquerque?)

看起来很简单,这正是你可能想要用 git 做的事情——返回之前的提交,而不会丢失中间提交,以防你改变主意。

4

8 回答 8

5

你介意生成:A-B-C-D-E-F-F'-E'-D'代码的状态与D'它的状态完全相同C(所以G== D')?在这种情况下,只需还原FED。如果你不想要中间步骤,revert但不应用,然后提交。那是:

$ git revert -n HEAD
$ git revert -n HEAD~
$ git revert -n HEAD~2
$ git commit -m 'Revert D,E,and F'

在这之后,当前的 HEAD 是 G,两个提交 G 和 C 应该包含相同的代码树。您可以通过检查 和 的输出来git cat-file -p HEAD | grep ^tree验证git cat-file -p C | grep ^tree。这两个命令都应该给出相同的输出。

但目前还不清楚为什么要保留中间提交。如果您希望它们为后代使用,请执行以下操作:git branch old-stuffgit reset C 这会将您当前的分支设置回C,但是DE,并且F仍然可以在名为 的分支中查看old-stuff

于 2013-07-03T13:53:18.223 回答
1
git cherry-pick --strategy=recursive -X ours C

混帐合并(1)

递归策略可以采用以下选项:

我们的

此选项通过支持我们的版本来强制冲突的大块头被干净地自动解决。[...]

“我们的”和“他们的”是指被合并的两个提交。但是,我不确定哪种方式git cherry-pick处理提交,因此您可能需要-X theirs。你可以新建一个分支,试试看。

于 2013-07-03T18:54:29.073 回答
1

我认为您需要使用git cherry-pick.

https://www.kernel.org/pub/software/scm/git/docs/git-cherry-pick.html

于 2013-07-03T15:39:47.150 回答
0

从您对我的其他答案的评论来看(我没有删除,因为该解决方案比这更正确,但这似乎是您想要的),您可以commit-tree按如下方式使用管道命令:

git reset $( git commit-tree $tree -p F -m 'Revert D,E, and F' )

其中 $tree 是提交 C 底层树对象的哈希值。(的输出git cat-file -p C | grep ^tree)。这将创建一个新提交,其中底层树与提交 C 相同,但新提交的父级是提交 F。它还将当前分支设置为指向该新提交,使其成为新的 HEAD。

于 2013-07-03T18:01:35.470 回答
0

如果你想最终得到:( -G-D-E-F 不确定,我对“D、E 和 F 就在那里”感到困惑。)那么到达那里的最好方法是rebase -i, 并A-B-C挤在一起。

于 2013-07-03T16:18:15.203 回答
0

您能否将您想要的描述为:“使您的工作树与 C 相同,然后提交”?如果是这样,这是一种方法:

git checkout C     # working tree same as C. But also moves HEAD to C...
git reset F        # ...so move HEAD back to F, leaving working tree alone
git commit -a      # commit working tree

编辑这可能会产生一堆合并冲突,所以它不能解决你问题的那一部分。

EDIT3同样,您先前问题的选定答案解决了这个问题:通过首先删除所有内容,没有冲突。


顺便说一句:在不更改 HEAD 的情况下使工作树等于提交 C 会很好,但我认为没有(瓷器)可以做到这一点。如果您想要整个提交,请更改当前分支git checkout。可以在不移动当前分支的情况下将单个文件提取到工作树,但是您必须单独提取每个文件,而不是整个提交...git resetgit checkout

EDIT2哦,我从您上一个问题的选定答案中看到,您可以这样做。我试过了git checkout C -- "*",但你需要git checkout C -- .所以它只是:

git checkout C -- .    # working tree same as C, without moving branch (NB ".")
git commit -a
于 2013-07-03T18:23:49.547 回答
0

Looks like you want C to be the final state of your git repository.

Take "A-B-C-D-E-F-G" as an example,

You can just revert G,F,E,D in order using

git revert [commitID]

Then use git commit

After G,F,E,D are all reverted, your repository will be staying in the state same as C.

An alternative way for local branch only is:

git resert --hard HEAD~4

This will reset your local branch to commit C and drop D,E,F,G.

于 2013-07-03T20:53:32.187 回答
0

实际上,在我发现你的问题之前,我已经提交了自己的问题,但无论如何 - 你可以使用树哈希来做到这一点。

这个想法是每个提交都有一个与之关联的“树对象”,它描述了整个工作树,所以您需要做的就是创建一个与旧提交具有相同树对象的新提交,并将其添加到 HEAD。

这是一个执行此操作的脚本:

#/bin/bash
COMMITID=$1
git reset --hard $(git commit-tree -m "Revert to commit $COMMITID" -p $(git rev-parse HEAD) $(git rev-parse $COMMITID^{tree}))
于 2019-12-19T16:19:29.147 回答