0

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

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

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

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

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

但我不希望这样做,因为我必须稍后以某种方式删除该提交,这实际上是 git 的解决方法代码。

是否存在一种众所周知的方法来处理 git 的胆量以使所有事情都按预期工作并合并在一起?

4

1 回答 1

1

如果你打算用它git read-tree来填充你正在构建的提交的索引——是的,这是为每个提交添加前缀的简单方法,就像你正在做的那样——你已经深入 Git 的内部,所以你不妨用它git commit-tree来构建提交对象。

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

然后,而不是git commit,使用git write-tree将索引转换为树对象,然后git commit-tree将树对象嵌入到新的提交中。由于git commit-tree允许您指定每个父级,您可以直接让您的 N 路章鱼合并:

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

你在这个新分支上有你的提交 C m 。

编辑:显然我误读了这个问题,并且您已经有一个现有的分支名称B,其提示提交当前是提交 C B。如果要保留其文件,则应首先阅读此树,而不是使用git read-tree --empty,然后将该提交用作最终提交中的父级之一,git commit-tree并简单地将新提交快速转发到现有分支名称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

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

于 2020-01-12T19:09:09.420 回答