在我的资料库中,我目前有这段历史:
$ git log --oneline
<commit-id-1> Merge commit '<merged-commit-id-1>' into <branch>
<commit-id-2> Merge commit '<merged-commit-id-2>' into <branch>
...
其中<merged-commit-id-1>
和<merged-commit-id-2>
是从我之前创建当前branch
的另一个分支合并而来的。现在我想以某种方式加入那些合并提交:
$ git log --oneline
<commit-id> My message about huge successful merge here...
...
我试过
$ git rebase --preserve-merges -i HEAD~2
p
和s
,但收到此错误:
Refusing to squash a merge: ...
(我也在一个新的最简单的存储库中复制它)有没有办法解决它?
我真正想要的是能够通过原始分支(已更改)的ID合并大量提交,同时逐步解决冲突并且不引入数十个合并提交(merge --squash
不是一个选项要么,因为我需要保留历史。)
示例:
$ git init
$ echo 1 > 1; git add 1; git commit -m 1
$ git branch branch
$ echo 2 > 2; git add 2; git commit -m 2
$ echo 3 > 3; git add 3; git commit -m 3
$ git checkout branch
$ echo 4 > 4; git add 4; git commit -m 4
$ git log master --oneline
8222... 3
1f03... 2
... 1
$ git merge 1f03
# in real project here goes some work
$ git merge 8222
# and here, can't merge 8222 right away, because it can be difficult
# now need to clean up merge commits OR merge incrementally in a different way
$ git rebase -i --preserve-merges HEAD~2
p ...
s ...
Refusing to squash a merge: a199... (merge commit for 8222)
torek的解决方案:
git update-ref refs/heads/branch `git commit-tree -p branch~N -p 8222 branch^{tree} -m 'Commit message for a new merge commit'`
答案 0 :(得分:4)
首先是一些背景......在git中,合并有两个功能:
它比较&#34;合并基础&#34;两个提示的两行开发,然后使用半智能 1 算法组合这些变化。也就是说,git merge other
首先计算$base
, 2 然后计算git diff $base HEAD
和git diff $base other
。这两个差异告诉git你要对$base
做什么,例如,在foo.txt
添加一行,从bar.tex
删除一行,然后在xyz.py
中更改一行。然后Git尝试保留所有这些更改,如果添加到foo.txt
的行在每个分支中添加相同的方式,或者同一行是从bar.tex
删除了,只需保留一份的更改。
为了将来的使用,它记录了两条开发线汇集在一起的事实,并且两条线的所有所需更改现在都出现在&#34; merge commit&#34;中。因此,结果提交适合作为新的合并库。我们马上就会看到这样的事情(不完全是这样)。
这两个功能以完全不同的方式实现。第一个是通过合并工作树中的差异来完成的,留下任何太难的情况让您手动处理。第二个是在您进行合并提交时完成的,方法是列出两个 3 &#34;父提交ID&#34;在新的(合并)提交。
您需要决定的是以多少方式保留这些内容。我认为在这些情况下,绘制(部分)提交图是非常有用的。
在这个示例中,您从一个公共基础开始,我将其称为B
(对于基础),然后在master
上再提交两个提交,在branch
上再提交一个:< / p>
B - C - D <-- master
\
E <-- branch
现在在branch
上,您首先合并提交C
:
B - C - D <-- master
\ \
E - F <-- branch
这是否需要手动干预取决于从B
到C
的更改以及从B
到E
的更改,但无论如何这都会导致新的合并提交F
。
然后请求git在提交D
中合并。但是,这不再使用commit B
作为合并基础,因为向后遵循新的合并F
,git会发现合并基础 - branch
和{之间的最新提交{1}} - 现在提交master
。因此,比较为C
至C
,D
至C
。 Git结合了这些(可能不得不停下来得到帮助);当某人(你或git)提交结果时,我们得到:
F
现在,你要问(我认为)&#34;壁球&#34; F和G合并为单个合并提交。鉴于git的工作方式,你只能通过创建一个 new 合并提交来实现这一点,然后让B - C - D <-- master
\ \ \
E - F - G <-- branch
指向它(删除对branch
的引用,因此只有参考到G
也是如此。
进行此新合并提交时需要考虑两件事:
你想要什么树(附加到提交的文件/目录集,将由索引/登台区域组成)?你只能有一个!
您要列出哪些父提交?要在提交F
之后立即使用它,你需要它的第一个父亲&#34;是E
。要使其成为合并提交,它必须至少有一个其他父级。
要选择的树很明显:提交E
具有所需的合并结果,因此请使用G
中的树。
使用的第二个(也许是第三个)父母不太明显。可以列出G
和 C
,并提供章鱼合并D
:
O
但是这并没有做任何特别有用的东西,因为B - C = D <-- master
\ \ \
E --- O <-- branch
是C
的祖先。如果你做了一个更正常的合并提交D
,它只指向M
(作为第一个父级)和E
)(作为第二个父级),你得到的这样:
D
这将是&#34;同样好&#34;作为章鱼合并:它具有相同的树(我们每次都从提交B - C - D <-- master
\ \
E ---- M <-- branch
中挑选树)并且它具有相同的第一父(G
)。它只有一个其他父级(即E
),但这导致D
位于其历史记录中,因此它不需要直接与C
建立显式连接。 / p>
(可以有一个明确的联系,如果你想要一个,但没有特别好的理由给它一个。)
这留下了最后一个问题:实际产生此合并的机制(C
或M
,无论您喜欢哪种方式)并让O
指向它。
有一个&#34;管道&#34;假设你现在处于这种状态,直接创建它的命令:
branch
此时你可以运行(注意,这些是未经测试的):
B - C - D <-- master
\ \ \
E - F - G <-- branch
其中$ commit=$(git commit-tree -p branch~2 -p master branch^{tree} < msg)
是包含所需提交消息的文件。这会创建commit msg
(只有两个父项,第一个是提交M
,即E
,第二个是提交branch~2
,即D
})。然后:
master
可以使用普通的git&#34;瓷器&#34;来创建$ git update-ref refs/heads/branch $commit
。命令,但它需要更多的技巧。首先,我们要保存所需的树,即M
:
branch^{tree}
然后,在分支$ tree=$(git rev-parse branch^{tree})
上(因为branch
将使用当前分支),告诉git备份两个提交。我们在这里使用软复位,硬复位还是混合复位并不重要,我们暂时只是重新标记git reset
标签:
branch
然后,告诉git我们正在合并$ git reset HEAD~2
,但无论如何都不承诺。 (这只是为了让git设置master
和.git/MERGE_MSG
- 您可以直接编写这些文件,而不是执行此部分。)
.git/MERGE_HEAD
然后清除工作树和索引中的所有内容,将其全部替换为commit $ git merge --no-commit master
中保存的树:
G
(为此,您需要位于工作树的顶级目录中)。这为新提交准备了索引,现在只有最后一次提交的提交:
$ git rm -rf .; git checkout $tree -- .
这使用我们在执行$ git commit
之前获取其ID的树来提交合并。 git reset
使git reset
的提示提交为提交branch
,因此我们的新提交是merge-commit E
,就像我们使用了低级管道命令一样
(注意:M
不能做到这一点,它不够聪明,不知道什么时候可以 - 只有当我们在第一次设置好的时候的地方。)
1 它并不是特别聪明,但它可以很好地处理这些简单案例。
2 您可以使用git rebase -i -p
和git merge-base
自行计算。然而,对于一些(不常见但并不像人们希望的那样罕见)的情况,可能存在多个合适的合并基础。默认的合并算法(&#34;递归&#34;)将合并两个合并库以提供一个&#34;虚拟基础&#34;并使用它。因此,如果您尝试手动执行此操作,则存在一些细微差别。
3 可以有两个以上的父母:&#34;合并提交的定义&#34;是任何具有多个父级的提交。尽管如此(使用&#34;章鱼合并&#34;策略),多向合并(&#34;章鱼和#34;合并)的方式也不同。所以,在大多数情况下,我忽略了这些。