如何使用git filter-branch将文件添加到特定的提交中?

3
我们需要在特定过去的提交中添加一些文件,并准备重写历史,以便影响到从该提交的子代分支分叉出来的所有分支。我们试图使用git filter-branch进行此操作,使用add命令,因为一旦我们将文件添加到提交中,它就不会添加到子代中,但是我们找不到正确的参数,以便停止影响到某些并行分叉提交。请参见图像以了解详情。
我们正在针对红色提交使用此命令,但是文件出现在紫色提交上 - 为什么? - 它也出现在绿色提交上,我们不希望它影响该代码路径,只想让它出现在红色提交上,然后通过所有子提交和合并继承。 git filter-branch --index-filter "cp C:/Users/asdf/Documents/IdeaProjects/git-crypt-tests/.gitattributes . && git add .gitattributes" 72c7e292ddd134c04285f3530afc829fd38de428..HEAD 我理解错了什么吗?
谢谢。

enter image description here


这里可能会变得棘手,因为“要返回的新数据”提交的父提交实际上是一个合并提交,这意味着您的分支有两个可见的祖先。这可能会成为重置的绊脚石,并且筛选分支也可能会遇到问题。 - Tim Biegeleisen
2个回答

2
看起来您认为当您将提交范围写为A..B时,它会包括边界。但实际上并不是这样的。这种表示法是B ^A的缩写,即所有导致B的内容,但不包括导致A的内容。这从范围中删除了“下限”A。解决方法是写A~,它的意思是“A的祖先”:A~..B
此外,由于您确切地知道要向哪些提交添加文件,以及哪些提交不想将它们添加,因此可以将修订遍历器限制为仅列出所需的提交。
git filter-branch --index-filter "cp C:/Users/asdf/Documents/IdeaProjects/git-crypt-tests/.gitattributes . && git add .gitattributes" -- HEAD --not 72c7e29~ ":/Fix Prestapp"

也就是说,您想要所有在HEAD之前的提交记录,但不包括72c7e29~以及提交信息以Fix Prestapp开头的提交记录。请注意保留HTML标签。

我很难理解这些参数。似乎没有良好的文档记录它们。是否有任何参考资料,以便我不必再在SO上发布基本问题?谢谢。 - Áxel Costas Pena
@ÁxelCostasPena 请查看gitrevisions; text search, ancestry。我仅使用了文本搜索变量,因为您没有显示提交的SHA1,并且从图像中很明显您指的是哪个提交。 - j6t
谢谢,这非常有用。但为什么不只关注一个提交呢?我阅读了文档并尝试了一下,为什么这个命令也会将提交添加到两个分支中?手册说这就像选择一个单独的提交。git filter-branch --index-filter "cp C:/Users/asdf/Documents/IdeaProjects/git-crypt-tests/.gitattributes . && git add .gitattributes" -- HEAD 72c7e292ddd134c04285f3530afc829fd38de428^^! - Áxel Costas Pena
1
@ÁxelCostasPena A^!A ^A^1 ^A^2 ^A^3... 的缩写,即包括 A 本身和排除其所有父级;结果是一个单独的提交。但是当你说 B A^! 时,除了排除 A 及其之前的所有父级,还会选择导致 B 的所有提交。因此,如果 BA 的后代(就像你的例子一样),则可以获取从 AB(包括 AB)的所有提交。 - j6t
谢谢!这仍然给我带来一些烦恼,但我刚刚发现了“rev-list”,所以现在我可以调试整个提交列表了。 - Áxel Costas Pena

0

这是另一种通用的方法,可以在任何提交树中重写单个提交,而无需构建复杂的rev-list。

  1. 如果不存在,创建提交的标签 - my-tag

  2. 执行以下命令:

    git filter-branch --index-filter "cp \"<local-path-to-file>\" \"<sourcetree-path-to-dir>\" && git update-index --add \"<sourcetree-path-to-file>\"" -- my-tag --not my-tag^@
    

    或者使用简写代替 my-tag --not my-tag^@
    my-tag^!

  3. 如果在步骤(1)中添加了标签,则删除标签。

这样做会更快,因为不需要了解提交树结构。

如果您想重写一个提交以及它的子提交,那么您可以使用 git replace (git replace --graft <commit> [<parent>…​]) 加上 git filter-repo 脚本来传播更改到子提交中:

更新所有子分支

git replace --graft <commit-child-1> <commit-child-1-parents>
git replace --graft <commit-child-2> <commit-child-2-parents>
...
git replace --graft <commit-child-N> <commit-child-N-parents>

提交更改并清理

git filter-repo --force
git for-each-ref --format="delete %(refname)" refs/replace | git update-ref --stdin

其中:

  • <commit-child-*> - 重写提交的子级。
  • <commit-child-*-parents> - 包含您重写提交的提交列表。

因此,在单个子级的简单情况下,它只需要一个调用和一个提交:

git replace --graft <purple-commit> <rewritten-red-commit>
git filter-repo --force
git for-each-ref --format="delete %(refname)" refs/replace | git update-ref --stdin

注意:

如果您正在尝试替换一个文件,并且它在下一个子提交中有更改,例如 changelog.txt 文件,则必须在每个下一个子提交中重新编写它,否则下一个提交将保留旧文件。在这种情况下,最好使用带有文件文本搜索和替换的 git filter-repo,而不是文件添加/替换或在调用 git replace --graft ... 之前手动重写每个下一个子提交。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接