【问题标题】:How to add additional parents to old git commits?如何向旧的 git 提交添加额外的父级?
【发布时间】:2012-06-04 05:16:07
【问题描述】:

我有一个包含两个分支的项目:master 和 gh-pages。它们本质上是两个不同的项目,其中 gh-pages 项目依赖于主项目(反之亦然)。将其视为“master 包含源代码,gh-pages 包含从这些源文件构建的二进制文件”。定期,我将在 master 中累积的更改并使用提交消息“与 master commit xxxxxxxx 保持一致”对 gh-pages 分支进行新的提交。

这样做了一段时间后,我意识到如果 gh-pages 提交“与主提交 xxxxxxxx 保持一致”实际上在 git 存储库中将 xxxxxxxx 作为其父级,那就太好了。像这样(糟糕的 MSPaint 艺术):

有没有办法让存储库看起来像上面的第二张图片?我知道如何按照这种模式进行新提交:我可以执行“git merge -s ours master”(设置其他空提交的父级),然后执行“git commit --amend adv550.z8”(其中 adv550.z8是实际发生变化的二进制文件)。但是 git 是否可以轻松地回到过去并将新父级添加到旧提交中?

一旦我的本地存储库看起来正确,我非常愿意“git push -f”并删除我的 Github 存储库的当前历史记录。问题是,可以让我的本地仓库看起来正确吗?


编辑多年后添加:我最终放弃了让 gh-pages 的 git 历史看起来像这样的尝试;我认为零增益工作量太大。我的新做法是积极地将提交压缩到gh-pages,因为在我的情况下保存这些提交消息真的无关紧要。 (这只是一长串“与主提交保持一致......”,其中没有一个在历史上很有趣。)但是,如果我需要再次这样做,我会听那些说的答案

git merge $intended_parent_1 $intended_parent_2
git checkout $original_commit -- .
git commit --amend -a -C $original_commit

【问题讨论】:

    标签: git git-rewrite-history


    【解决方案1】:
    git replace --graft $original_commit \
        $(git show --pretty=%P $original_commit) \
        $additional_parent
    

    说明: git replace --graft 允许重新指定给定提交的父级。这是通过编写一个带有相应更改的新提交并编写一个替换引用来实现的,这会导致 git 在访问时查找新提交而不是原始提交。

    git show --pretty=%P $original_commit 只列出了原始父母。

    【讨论】:

    • 这[几乎]是正确答案。几乎,因为我不得不使用git show --pretty=%P $original_commit | head -n1——我的 git (2.11.1) 确实输出了很多不仅仅是父行。
    • 这是来自世界末日未来的消息,说你们让我的生活变得简单多了,我非常感激
    • 使用git show --pretty="format:%P" --no-abbrev-commit --no-patch ${original_commit} 避免head -n 1
    • 请注意,以这种方式添加的父母不会出现在 Github 上
    • @wrongusername 你知道有没有办法让它们出现在 github 上?
    【解决方案2】:

    这些是你的魔法词:

    git merge origin/master --strategy ours
    

    gh-pages 分支中执行此操作,以记录与master 的关系,而无需实际更改任何内容。当我将gh-pages 分支创建为--orphan 时,这对我有用,如this answer

    你甚至可以在之后--amend这个空的合并提交,以便引入你真正想要对你的“页面”进行的更改。有关 GitHub 上的实际示例,请参阅 https://github.com/krlmlr/pdlyr/network

    【讨论】:

    • 这与在编辑中添加到问题中的信息相结合(re git commit --amend -a -C ... 等)证明对我很有用。谢谢。
    【解决方案3】:

    您无法及时返回并更改现有提交。即使您执行git commit --amend 之类的操作,您实际上并没有更改提交;您正在树中的同一位置创建一个新的,其中包含原始内容的所有内容(加上您的更改)。您会注意到提交哈希在--amend 之后发生了变化,而原始哈希仍然存在于您的存储库中——您可以使用git reflog 回到它。

    另一方面,您可以回到过去并创建一个替代宇宙。本质上,您将返回您的 gh-pages 分支点并重新创建整个事物(例如 git cherry-pick 或其他东西)作为并行分支。哈希值会改变,因为提交是不同的对象。

    (我很好奇为什么你将你的 repo 设置为单独的分支,而不是同一分支中的单独目录。看起来代码 -> 构建过程会变得乏味。)

    【讨论】:

    • “你会注意到提交哈希在 --amend 之后发生了变化”:只有当内容发生变化时,对吧?更改提交日志消息似乎不会更改哈希,我希望更改父母也不会。 “为什么要分开分支”:这就是 GitHub 的“GitHub 页面”功能,我用它来托管 quuxplusone.github.com/Advent/play-550.html。您创建一个名为“gh-pages”的新分支,其中的任何内容都会自动镜像到实时网页。这有点奇怪,但我认为在这种情况下它有一定的意义。
    • “基本上,你会回到你的 gh-pages 分支点并重新创建整个事情”:我可以接受。但是如何保留原始提交的时间戳?我不想失去他们。这就是为什么我问如何回到过去。
    • 即使commit --amend -C 的索引为空,我也会得到一个新的哈希值。
    • 现在我想多了,如果您只是想保留祖先信息,那么跳过合并父项的想法并改用标签是否有意义?您可以在 gh-pages 分支上使用带有 master 提交父级的注释标记进行旧提交,并使用您的合并/修改系统继续前进。
    • 提交哈希涵盖了与该提交有关的所有内容。内容、父参考、评论、日期/时间。如果您在提交中更改单个位,则会生成一个新的哈希。
    【解决方案4】:

    @rumpel 给出了正确的答案。请允许我分享我使用的 sn-ps。

    首先,我创建了 orphan master 分支:

    git checkout --orphan master
    git commit --allow-empty -m 'Init'
    

    我的用例略有不同,我想要的站点位于develop 分支上的_site 目录中。所以我这样做:

    git checkout master
    git rm -rf .
    git read-tree develop^{tree}:_site
    git checkout -- .
    git commit -m "Update from develop SHA1:$(git rev-parse --short develop) ($(git log -1 --format=%cd develop))"
    git replace --graft @ $(git show --pretty=%P @ | head -n1) develop
    

    请注意,在最后一个命令中,我必须按 | head -n1 过滤,因为我的 git 输出的不仅仅是父母。

    【讨论】:

    • 使用git show --pretty="format:%P" --no-abbrev-commit --no-patch ${original_commit} 避免head -n 1
    【解决方案5】:

    您可以重写提交,但它需要从第一个需要额外父级的提交开始重写整个分支,直到分支提示。是(就像 rebase 一样)一个新的分支,它只是具有相同的内容。

    诀窍是使用提交树,它获取所有内容(提交者/作者信息、时间戳、提交消息、父母列表),尽管以一种相当不切实际的方式。然后编写一些脚本就可以完成这项工作。

    【讨论】:

    • 如果你的第二段不是那么奇怪的模糊,你会得到一个赞成票。
    • 我知道。但这有点工作,我现在不需要那个脚本。
    猜你喜欢
    • 2020-07-30
    • 2011-12-01
    • 2021-03-22
    • 1970-01-01
    • 2016-11-09
    • 1970-01-01
    • 1970-01-01
    • 2019-07-10
    • 2017-10-22
    相关资源
    最近更新 更多