【问题标题】:Remove spurious commit parent pointer删除虚假提交父指针
【发布时间】:2012-12-24 01:42:17
【问题描述】:

我将 Bazaar 存储库导入 Git(使用 git bzr),但生成的存储库包含虚假的提交父链接:

请注意,标记为1.02-6 的提交基于1.02-3 提交,但1.02-1 也不必要地标记为父提交。 (注意:这部分 repo 中的所有提交都被标记;显示的提交之间没有提交。)

我尝试了几种方式(在master 分支上:git rebase 1.02-3git rebase -i upstream-1.02git rebase --onto 1.02-1 1.02-3git rebase --root upstream-1.02 --onto=other_branch),但在每种情况下它都会因合并冲突而失败。这些似乎超出了必要的尝试;历史记录是正确的除了,在标记为1.02-6 的提交中记录了一个额外的父指针。

如何删除链接以线性化历史?有没有比手动按顺序挑选所有提交更好的方法?

【问题讨论】:

  • 这真的是“假的”吗?或者是否对1.02-1 进行了错误修复,这些错误修复被合并到1.02-6 但没有进入1.02-3(或通过不同的路径到达那里)?对bzr 不太熟悉,我假设上图已简化为仅标记版本(即它们之间还有其他提交/变更集,目前未显示)。
  • @twalberg:没有简化。这些都是提交。
  • 可能有用:git-scm.com/docs/git-commit-tree, git cat-file -p 1.02-6

标签: git merge git-rebase


【解决方案1】:

最简单的方法(在 git >= 1.6.5 中)是使用:

git replace --edit <sha>

并删除/添加/更改父:行。

一旦您对更改感到满意,您就可以重写提交以使更改永久生效:

git filter-branch --tag-name-filter cat -- --all

在某些情况下,只重写所涉及的提交而不是完整的历史记录会明显更快(感谢Michael 在 cmets 中提到这一点);例如仅重写当前分支上的提交:

git filter-branch --tag-name-filter cat -- <new parent sha>..head

如果您不确定,请使用--all,否则可能会导致其他分支/标签仍然引用临时替换对象。

【讨论】:

  • 简单直接,没有多个容易出错的命令。这应该是公认的答案!
  • 请注意,我们可以将第二个命令作为git filter-branch --tag-name-filter cat -- &lt;parent sha&gt;..head 运行,因此我们只重写需要的提交,而不是每个提交。此处记录了支持的参数kernel.org/pub/software/scm/git/docs/git-rev-list.html
  • 谢谢,这是将合并提交(2 个父级)重写为常规提交(1 个父级)的干净方法。迈克尔添加的不重写完整历史证明对我来说非常有用。我会建议对答案进行编辑
  • 如果你在最近一次提交上运行git replace --edit &lt;sha&gt;,你不需要第二个命令(git filter-branch ...
  • @JorgeLuque 这将是新的父级,但要注意该命令只会重写当前分支。如果您不确定最好使用--all(堆栈溢出用户无济于事地编辑了我的答案以删除所有提及--all,我现在已恢复该编辑)。
【解决方案2】:

这将纠正父母而不更改任何其他内容(例如提交者日期):

git filter-branch --tag-name-filter cat --parent-filter 'test $GIT_COMMIT = [sha of 1.02-6] &amp;&amp; echo "-p [sha of 1.02-3]" || cat' -- 1.02-1..master

您必须将括号中的文本替换为适当的提交 ID。如果你还有更多下游分支需要重写,把1.02-1..master改成--all,做好等待的准备。

当然,如果其他人从您要编辑的提交之后的任何提交中分支,请不要使用此解决方案或任何其他解决方案。他们会恨你的。

【讨论】:

  • 如果您不需要关心其他人(例如,在从 svn 或 bzr 转换为 git 之后以及在您推送到 git 服务器之前进行清理的情况下),那么这就是最佳答案,真的。我都试过了。
【解决方案3】:

您可以使用git commit-tree 内部命令手动完成。

我们想要编辑标记为1.02-6 的提交以删除虚假父指针(指向56a2f3b5948ab54c9239c2b384a6ea9eb1f410c4)。

首先,从现有的提交对象中读取信息:

user@host:/path/repo.git$ git cat-file -p 1.02-6 
tree c658aa1ebcf2bf2a607696c7868b875be72fb01f
parent 56a2f3b5948ab54c9239c2b384a6ea9eb1f410c4
parent 4e671bf1d2298729c9e5cfd8229051cfe2c40831
author James Damour (Suvarov454) <suvarov454@users.sourceforge.net> 1146319620 -0400
committer Bazaar Package Importer <james.westby@ubuntu.com> 1146319620 -0400

The "main/" in the Section line of debian/control should be assumed.

Extract the commit message using git log --format=%B -n 1 1.02-6.

现在创建一个具有相同内容的新提交(不包括虚假父链接和提交者信息):

git log --format=%B -n 1 1.02-6 | \
    GIT_AUTHOR_NAME="James Damour (Suvarov454)" \
    GIT_AUTHOR_EMAIL="suvarov454@users.sourceforge.net" \
    GIT_AUTHOR_DATE="1146319620 -0400" \
    git commit-tree c658aa1ebcf2bf2a607696c7868b875be72fb01f \
        -p 4e671bf1d2298729c9e5cfd8229051cfe2c40831

这创建了一个新的提交,并打印了它的哈希 (cc32e66...)。现在把它变成一个新的分支:

git checkout -b fixed_commit cc32e66

并将master 重新定位到新分支:

git checkout master
git rebase fixed_commit

我们完成了:

您可能想要删除旧分支并重新标记适当的提交。


实际上使用git filter-branch --parent-filter 可能更容易。我没试过。

【讨论】:

    【解决方案4】:

    你可以试试rebase。有一个例子有点下方(搜索--onto),我认为与您的情况相似。

    认为你需要这样做

    git rebase --onto 1.02-1 1.02-3
    

    应该将 1.02-3 之后的所有内容放到 1.02-1 上,这可能就是您想要的。

    请记住,与第一次更改的提交相比,所有内容的哈希值都会有所不同,但我假设您这样做是从 bzr 迁移的第一步,因此其他人不应该克隆它。

    【讨论】:

    • 这会产生一堆合并冲突。似乎试图做的比必要的更多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 2019-05-23
    • 1970-01-01
    相关资源
    最近更新 更多