关于git:如何签出旧提交并使其成为新提交


How to checkout an old commit and make it a new commit

我想"跟进"关于这件事的另一个问题:签出旧提交并使其成为新提交

但他们说"不要这样做!"所以我似乎必须问一个新问题。(尽管我认为对我来说最好的答案是同一个问题,但并没有按预期工作……

如果我有承诺A-B-C-D-E-F(假设这些都是sha的)。我想使整个存储库与C完全相同,然后承诺创建'G',这与C完全相同。所以当我完成后,日志是A-B-C-D-E-F-G,即使C&G是相同的。

有一个答案表明樱桃精选似乎是完美的,但它让我解决了CF之间的每一个冲突。我看了一下文件,试了一下--force,然后推荐了--patch--patch是最接近的,但不是绝对的。那么,有没有一种方法可以让Cherry-Pick选择冲突的C版本呢?

另一个最接近的答案是签出C,但我找不到能让它保持在同一分支上的神奇词。我最近完成的培训说,用末尾的"魔法破折号"告诉git你想在当前的分支上使用这个,但不管我做什么,它都会创建一个"(无分支)"分支。

正如我确信你可以告诉我的,我对Git(和一般的命令行)还很陌生,但我试图自己解决这个问题。如果你能使用你推荐的详细版本,那么它会更好地留在我的脑海中,并且会非常感谢。(-a=--all-a = --annotate-a = --albuquerque?)

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


您介意生成:A-B-C-D-E-F-F'-E'-D',其中D'处的代码状态与C处的代码状态完全相同(所以G==D')?在这种情况下,只需恢复FED。如果您不想中间步骤,则revert但不适用,然后提交。即:

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

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

但不清楚为什么要保持中间提交。如果你想让他们留下来,那就做一些类似的事情:git branch old-stuffgit reset C,将你当前的分支设置回C,但是DEF,仍然可以在名为old-stuff的分支中查看。


1
git cherry-pick --strategy=recursive -X ours C

Git合并(1):

The recursive strategy can take the following options:

ours

This option forces conflicting hunks to be auto-resolved cleanly by favoring our version. [...]

"我们的"和"他们的"是指正在合并的两个承诺。但是,我不确定在git cherry-pick的哪个方面处理这些承诺,所以您可能需要-X theirs。你可以做一个新的分支,试试看。


我认为你需要使用git cherry-pick

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


看起来您希望C是Git存储库的最终状态。

以"A-B-C-D-E-F-G"为例,

您只需按顺序还原g、f、e、d,使用

江户十一〔31〕号

然后使用埃多克斯1〔32〕

在G、F、E、D全部恢复之后,存储库将保持与C相同的状态。

当地分支机构的另一种方法是:

埃多克斯1〔33〕

这将重置本地分支以提交C并删除D、E、F、G。


你能描述一下你想要的:"让你的工作树和C一样,然后提交它吗?"如果是这样的话,可以这样做:

1
2
3
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同样,对前面问题选择的答案解决了这个问题:首先删除所有内容,就没有冲突。

顺便说一句:让工作树在不改变头部的情况下提交C是很好的,但是我不认为有(瓷器)可以做到。如果您想要整个提交,git checkoutgit reset都会更改当前分支。git checkout可以在不移动当前分支的情况下将单个文件提取到工作树,但是您必须单独提取每个文件,而不是整个提交…

伊迪丝2哦,我从上一个问题的选定答案中看到,你可以这样做。我试过git checkout C --"*",但你需要git checkout C -- .,所以应该是:

1
2
git checkout C -- .    # working tree same as C, without moving branch (NB".")
git commit -a


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

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

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


如果你最终想要:埃多克斯1〔19〕(不确定,我对"D、E和F在哪里"感到困惑。)到那里最好的办法是把rebase -i挤在一起。