扔掉一个合并提交的子,保留另一个,重播所有其他提交

时间:2012-08-13 10:01:35

标签: git

摘要

我越是深入研究问题,我就越能得到一个更清晰的问题摘要:如何使用git获取DAG中实际上是我的应用程序域中的树的顶点的父节点基础设施?

读得很好。任何想法都是受欢迎的(特定于git)。

问题

我有一个存储库,其历史记录是由我通过半自动命令编写的脚本构建的:

enter image description here

因此,我可以根据需要自动将任何类型的信息放入历史记录中,例如,当执行命令时,那些标记为黄色的标记。

所有这一切的关键要求是,当合并冲突发生时(并且必须发生,因为脚本的目标是让用户意识到合并冲突,并与之联系其他用户为了找到问题的解决方案 - 如果发生合并冲突, 是一个问题,我希望脚本能够激发用户之间的沟通。)

现在,考虑到这一点,我们假设我想恢复合并提交。这适用于非冲突的合并,例如此图像中的install-def。我只需要使用git revert -m 1 install-def(例如)。

当我需要恢复install-abc时,该过程变得更加复杂,因为我想要准确地恢复那些提交,install-abc(合并提交)和来自remotes/hacklet/abc/master的“灰色”子项

换句话说,要获得仅profile/myfirstprofileremotes/hacklet/def/master的线性历史记录。

在半自动脚本中实现此目的的最简单,最安全的方法是什么,假设我可以使用任何标记对任何提交进行注释?最好不必做任何与 DAG 相关的事情(或者就此而言,递归),因为脚本是用bash编写的。

“revert”执行“卸载”。 git revert -m 1 ...适用于在install-def的情况下还原提交,但对于install-abc,它会给出:

error: could not revert 18cff49... using hacklet/abc/master in profile
myfirstprofile hint: after resolving the conflicts, mark the corrected
paths hint: with 'git add <paths>' or 'git rm <paths>' hint: and
commit the result with 'git commit'

注意profile分支可以包含许多此类install-提交,我想只删除其中一个。 “安装”和“配置文件”都不能嵌套。

注意:我知道“模块”和“子树”。请不要理会提及它们,我在这里做的事情是特定于应用程序域(git是应用程序的一部分),这不是关于在开发周期中使用git。我已经彻底分析了这两个功能,它们根本不适合我脚本的应用程序域。


附录1

我已经破解了包含这些信息,这让我更容易理解:

enter image description here

如果你认为这变得过于复杂,那就不是了,它基本上是一遍又一遍的模式:

enter image description here

此处,只有start-profileinstall-abcinstall-foo没有冲突。但树的结构确实看起来“相同”。

最后一个,包含所有冲突(第一个除外,install-abc),因为我喜欢gitk的彩色输出:

enter image description here

还有一个,但根本没有冲突:

enter image description here

注意:可能会在profile/myfirstprofile分支中标记未标记的单个提交。在这种情况下,应保留这些提交。真的没有别的东西应该“卸载”,但是那个提交属于install-THING


附录2

虽然我已经发现before-install-可以找到相应的git describe --tags $(git rev-list -1 install-abc)。但是:

根据标记install-def,如何找到提交 - 在最后一张图片中是before-install-ghi

考虑到最后的注意(中间没有标记的提交),如何找到这些“子和父”提交适合粘在一起,没有我想要的所需install-THING卸载“?

PS:我真的明白有向无环图指示这个词的意思,但必须有一些特定于git的方法,使用git基础设施和概念,我闻到了它

注意:我希望每次想要“卸载”某些东西时都不必迭代树。

2 个答案:

答案 0 :(得分:1)

我觉得这个解释很复杂,因为它没有对普通的git术语进行对冲,但据我所知,你问的是三个问题中的一个(很确定它是#3):

  1. “我如何重置为较早的分支提交?”
  2. “如何在不重置分支或检出历史记录中的早期提交的情况下,如何恢复到合并提交的父级?”
  3. “当以后的下游提交基于合并父级我正在还原时,如何还原合并提交?”
  4. 对于此解释,请考虑此图:

    G
    |
    F
    |\
    D E
    |/ 
    C
    |
    B
    |
    A
    

    首先,一些术语:

    提交'D'和'E'是提交'F'的“合并父”,这是一个合并提交。您看到的图表几乎总是有两个父母,您的术语中的“左”和“右”,git术语中的“第一”和“第二”,但合并可能具有的父母数量没有限制。从技术上讲,所谓的“章鱼合并”是可能的,可能是三个或更多的父母。它们在实践中很少使用。

    使用git中的^语法规范地描述了合并父项。请使用此语法,因为它可以让您更轻松地理解您的问题。第一个父级是^1,第二个父级是^2。换句话说:

    F^1 is equivalent to D
    F^2 is equivalent to E
    

    你问题中最令人困惑的方面是了解你想要达到的状态。让我们来解决三个可能的问题:


    “如何重置为较早的分支提交?”

    假设您尝试达到提交'D'并且不关心'E','F'或'G'引入的更改。使用Let_Me_Be的答案:

    git reset --hard D
    

    如果您想要达到'E',但根本不关心保留'D','F'或'G'引入的更改,那么同样可行。


    “如何在不重置分支或检出历史记录中的早期提交的情况下恢复到合并提交的父级?”

    假设您想要更复杂的东西:假设您想要在不丢失'E'或'G'的变化的情况下恢复'D'的变化。这也是一个微不足道的操作,只要'F'和'G'都不是基于'D'的变化。在那种情况下:

    git revert -m 2 'F'
    

    命令语法的意思是“恢复合并提交'F'并使第二个父级成为主线。”由此产生的历史(再次假设'G'不是基于'D')看起来好像'D'从未合并,但当然实际历史将如下所示:

    H
    |
    G
    |
    F
    |\
    D E
    |/ 
    C
    |
    B
    |
    A
    

    其中'H'是还原提交。如果您想恢复'F'并使用'D'作为主线,只需在命令中更改指定的父编号:git revert -m 1 'F'(注意使用1表示“第一个父级”。)< / p>


    “当以后的下游提交基于我正在恢复的合并父级时,如何还原合并提交?”

    好。这是最棘手的情况。如果你经常这样做,git不是你的问题的解决方案,无论其背景如何。如果可以的话,Git试图保持对冲突解决的完全不可知。这就是重点:如果合并冲突可以由软件解决,它就会这样做。如果它们在您的工作流程中经常出现,那么您的工作流程就是问题,而不是您的VCS。

    如果你想避免历史修订(即git rebase,即使我们可以完全自动完成也是疯狂的),解决你的恢复冲突的唯一方法是根据'D'代码恢复后续提交。换句话说:

    git revert G
    git revert -m 1 F
    

    如果你想使用'git cherry-pick G'(最初的'G'提交哈希),你可以重新申请'G',但是几率正在应用它不会顺利进行。您生成的历史记录如下所示:

    J - Reapplication of G
    |
    I - Revert of F
    |
    H - Revert of G
    |
    G
    |
    F
    |\
    D E
    |/ 
    C
    |
    B
    |
    A
    

    如果这个答案能够最好地描述你的情况,我强烈建议你在git以外的地方寻找你的申请。它不是为了轻松做你想做的事情而设计的,因为你正在做的事情将是一个相当破碎的版本控制工作流程。我对你的应用程序捕获所有边缘案例的能力持怀疑态度。即使冲突本身是可预测的,让它进行智能冲突解决也不是一个容易的问题。我当然可能是错的。

答案 1 :(得分:0)

这更像是一个猜测,然后是一个答案(因为我仍然对这个问题感到非常困惑)。

但我的猜测是你想要这样做:

git reset --hard start_myfirstprofile
git merge remotes/hacklet/def/master

如果要重置为合并前的状态。

git reset HEAD~1

在正确的分支上应该没问题。