假设我从这样的历史开始:
A---B---C
\
\-D---E
然后,我 merge 了两个分支,解决了过程中的一些冲突:
A---B---C---F
\ /
\-D---E-/
但之后,我想重写历史,看起来像这样:
A---B---C---EF
\ /
\-D-----/
换句话说,我想从分支提交
E
移动更改 merge 提交 F
.好像我已经在 E
中进行了更改同时通过 merge 手动解决冲突 D
进入 C
.(我的实际原因是
E
只是一个 merge 准备提交,使预期的冲突更容易解决。它本身没有多大值(value),我不希望它污染历史。我不'不想将 E 压缩到它的前身中,因为它的变化在逻辑上是分开的。)我试过:
git rebase -i --rebase-merges HEAD~10
但这给了我:
...
pick 1069500 ...
merge -C 44c0a69 ...
# s, squash <commit> = use commit, but meld into previous commit
# ...
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c <commit> to reword the commit message.
所以看起来提交可以是 merge 提交,或者
git rebase -i
可以将其压缩为它的前任,但不能同时压缩。和 git rebase
会 make me reapply my manual conflict resolutions无论如何,这有点臭。还有另一种方法吗?
最佳答案
TL; 博士
使用 git commit-tree
使用 F
进行新的 merge 提交的快照和您选择的 parent ,然后强制您的分支名称指向那里:
newcommit=$(git commit-tree -p HEAD^ -p HEAD^2^ HEAD^{tree} -F /tmp/msg)
哪里
/tmp/msg
包含您要使用的提交消息。然后使用 git reset
移动当前分支/提交到 $newcommit
,在检查这个提交看起来像你想要的样子之后(例如 git log --graph --oneline $newcommit
)。(这里假设一个类 Unix 的 shell,shell 变量设置为
var=value
, $(...)
以运行命令,依此类推。)长
提交不存储更改。他们存储快照。
对于 merge 提交也是如此:它们的快照是快照。它们以每个文件的 merge 形式保存每个文件的完整副本。
所以假设你有:
A--B--C--F
\ /
D----E
(这是您的原始绘图,我只是将提交推到了我更喜欢的样式)并且希望拥有:
A--B--C--G
\ /
D----'
(我刚刚在这里使用了一个全新的字母 merge
G
,但这与您的 EF
相同)。 merge 提交的快照 G
必须与 merge 提交的现有快照完全匹配 F
. merge 提交的第一个父级 G
必须提交 C
— 与现有提交的第一个父级相同 F
——第二个父级必须提交 D
, 跳过提交 E
,但最重要的是 G
的快照应该与您已经使用 merge 提交制作的快照完全相同 F
.现在,您可以轻松生成此图:
___
/ \
A--B--C--F G
\ / /
D----E /
\_____/
从您已有的图表中,通过进行新的提交
G
. git commit-tree
命令正是这样做的。您必须向
git commit-tree
提供三样东西:C
和 D
,您可以使用 HEAD^1
拼写和 HEAD^2^1
分别。每个1
可以省略。这些是 -p
论据。 HEAD
提交 F
它的树就是你想要的,HEAD^{tree}
指定。 -m
或 -F
或来自标准输入。 提交树程序写出新的提交对象并打印其哈希 ID,我们希望使用 shell 变量以人类可读的形式捕获它。
完成后,我们只需要更新当前分支名称。由于此分支当前已 check out ,我们将使用
git reset
:git reset $newcommit
我们可以
git reset --soft
避免更改当前索引,或 git reset --hard
更改索引和工作树,但如果当前索引和工作树与现有 merge 提交中的快照匹配 F
, merge 中的快照 G
无论如何都是完全一样的,所以没有真正需要任何一个标志。默认--mixed
reset 将重置索引,使工作树不受干扰。 (不过,如果你仔细地安排了一些东西,你可能想要 --soft
。)提交
F
将继续存在,但没有名字找到它,你不会看到它——最终,一旦保存它的 reflog 过期,Git 的垃圾收集器将抛出提交 F
和 E
.
关于git - 我可以与它的前辈之一压缩 merge 提交吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61906251/