git 在孤立分支中将多个提交合并为一个,每个提交都在前缀子目录中

git merge multiple commits into one in an orphan branch each commit in a prefix subdirectory

我需要将来自一个分支或远程仓库的超过 1 个提交合并到另一个分支中的单个提交中。

input branch#1: o--o- - -o   (C1)
                          \
input branch#2: o--o- - -o | (C2)
    :                     \|
input branch#N: o--o- - -o | (Cn)
                          \|
 output branch:   o--o- - -o (Cm)

我需要以一种特殊的方式来做,其中每个输入分支合并提交的源树是输出分支合并提交的源树中的前缀或子目录:

<C1>       <C2>       ...  <Cn>
 |          |               |
 +- c1.txt  +- c2.txt       +- cn.txt

<Cm>
 |
 +- C1/c1.txt
 |
 +- C2/c2.txt
 |
 :     :
 |
 +- Cn/cn.txt

此外,我需要更改合并提交的一些参数,如 author dateauthor email 等,并从所有输入分支的提交消息中生成提交消息离开合并的父级按原样提交,没有任何更改(包括合并提交中的父提交哈希列表)。

在互联网上挖掘我已经用最少的命令集找到了最通用的解决方案:

git merge --allow-unrelated-histories --no-edit --no-commit -s ours <input-branches-and-commits>
git read-tree --prefix=C1/ <C1-branch>
git read-tree --prefix=C2/ <C2-branch>
:
git read-tree --prefix=Cn/ <Cn-branch>
cat ... | git commit --no-edit --allow-empty --author="..." --date="..." -F -

但当输出分支是孤立分支时,它的工作方式有所不同。在那种情况下,输入分支的内容另外合并到输出分支提交的源树的根中:

<Cm>
 |
 +- C1/c1.txt
 |
 +- c1.txt

基本上当输入分支是唯一的输入分支时会发生这种情况(当输出分支是孤立分支时我没有用多个输入分支测试这种情况,因为我还没有那种情况,但我不要排除那个)。

我找到了发生这种情况的原因。因为 head 尚不存在并且不能存在,包括输出分支,所以合并命令在调用时创建它,同时使合并不完整,输出分支指向实际上使输出分支成为父分支的输入分支对自己。这会将输入分支的源代码树的内容带到输出分支提交的源代码树的根目录中,而无需用户通知。

我知道至少一种避免这种行为的方法,例如,在合并之前在输出分支中创建一个空提交,这使得孤立分支不是孤立分支,并将头部与对输出分支的引用一起初始化。

但我不希望这样做,因为我必须稍后以某种方式删除该提交,这实际上是 git.

的解决方法代码

是否存在一种广为人知的方法来处理 git 胆量,使所有事物按预期工作并合并在一起?

如果您要使用 git read-tree 来填充您正在构建的提交的索引——是的,这是为每个添加前缀的简单方法,就像你正在做的一样——你已经深入 Git 的内部,所以你不妨使用 git commit-tree 来构建提交 object.

换句话说,根本不要以 git merge 开头。只需用 git read-tree --empty 清空索引即可。然后读取每个提交 Ci, 1 ≤ i ≤ n。您的索引现在包含您打算放入此合并提交中的文件 Cm.

然后,使用 git write-tree 代替 git commit 将索引变成树 object,然后 git commit-tree 嵌入树 object在新的提交中。由于 git commit-tree 允许你指定每个 parent,你可以让你的 N-way 章鱼直接合并:

git read-tree --empty
git read-tree --prefix prefix1 C1
git read-tree --prefix prefix2 C2
...
git read-tree --prefix prefixn Cn

tree=$(git write-tree) || die ...
commit=$(cat ... | git commit-tree -p C1 -p C2 -p C3 ... -p Cn) || die ...

最后,将新的分支名称附加到生成的提交中:

git branch the-final-result $commit

你在这个新分支上有你的提交 Cm

编辑: apparently 我有点看错问题了,你也已经有一个现有的分支名称 B 其 tip 提交当前是提交 CB。如果你想保留它的文件,你应该首先阅读这棵树,而不是使用 git read-tree --empty,然后将该提交用作最终 git commit-tree 中的 parent 之一,然后简单地 fast-forward 新提交到现有分支名称 B。所以:

git read-tree Cm
git read-tree --prefix prefix1 C1
  .
  .
  .
git read-tree --prefix prefixn Cn

tree=$(git write-tree) || die ...
commit=$(cat ... | git commit-tree -p Cm -p C1 -p C2 ... -p Cn) || die ...
git push . $commit:refs/heads/B  # or git branch -f B $commit

根据实际需要的结果进行调整。