合并或变基任意数量的提交

9

假设我的本地 git log 显示:

739b36d3a314483a2d4a14268612cd955c6af9fb a
...
c42fff47a257b72ab3fabaa0bcc2be9cd50d5c89 x
c4149ba120b30955a9285ed721b795cd2b82dd65 y
dce99bcc4b79622d2658208d2371ee490dff7d28 z

我的远程git log显示:

c4149ba120b30955a9285ed721b795cd2b82dd65 y
dce99bcc4b79622d2658208d2371ee490dff7d28 z

假设有任意数量的本地提交记录,最简单的方式是什么()?

527b5810cfd8f45f18ae807af1fe1e54a0312bce a ... x
c4149ba120b30955a9285ed721b795cd2b82dd65 y
dce99bcc4b79622d2658208d2371ee490dff7d28 z

可能是重复的问题Git -合并多个提交并推送之前 - John Zwinck
@JohnZwinck 不一定是完全重复,原帖留下了任意数量的中间提交的空间,而交互式变基无法很好地处理它(速度非常慢)。 - user456814
4
不是重复内容。我熟悉交互式变基。关键在于有任意数量的本地提交(正如@Cupcake所指出的那样)。 - theanine
@Cupcake的回答可以用“git reset --soft $y; git commit”概括,这是目前最简单、高效的方法。 - jthill
3个回答

18

有时候,你可能不想使用交互式变基

如果在A到X之间的中间提交数量相对较小,那么你可以通过使用交互式变基来完成:

git rebase -i origin/master

然而,根据我的个人经验,在大量提交时使用交互式变基是比较慢的。我曾经在一个Windows机器上使用Git Bash一次性对大约100个提交进行了交互式变基,msysgit花费了很长时间生成交互式变基提交编辑器,以便您可以选择要对哪个提交运行哪些操作,因为列表最终变得非常大。
在这种情况下,您有几个解决方法。
替代方案#1:git reset 可使用混合重置和软重置来修改您的工作树或暂存区(分别)以聚合/收集A和Y两个提交之间的所有更改,之后您可以一次性将所有修改作为单个提交提交。
我只会给出软重置的示例,因为它已经为您留下了所有暂存内容,而如果您使用混合重置,则仍需为修改项进行暂存:
# You don't need to create the temp branch A if you
# copy the commit sha A down somewhere so that you can remember it.
git branch temp A

git reset --soft Y
git commit -m "Squash commits A through X"

# Verify that this new commit is equivalent to the final state at A
git diff A

备选方案二:使用补丁

另一个选择是简单地使用补丁。只需生成从 A 到 Y 之间的差异 diff 补丁,然后将该补丁作为新提交应用于 Y 的顶部:

git diff y a > squash.patch
git checkout -b squash-branch y
git apply squash.patch
git commit -m "Squash commits A through X"

# Verify that this new commit is equivalent to the final state at A
git diff A

根据评论中@A-B-B指出的,如果涉及二进制文件,则不适用。可以使用git diff --binary来输出文本文件和二进制文件的差异,但我不确定这些差异是否也可以用作补丁。

7

一种选择是使用git rebase -i @{u}命令。我经常使用这个命令,因此我将其设置为别名git freebase(因为它适用于您可以自由地变基的提交)。

如果您不熟悉,@{u}@{upstream}的快捷方式,表示“当前分支的上游分支”。


3
“最简单”的方式有些棘手。交互式变基将让您压缩所有内容,并根据某种标准来说是“简单”的。
另一种“简单”的方法,看起来有点复杂,是使用“压缩合并”(实际上并不是真正的合并,但确实使用与git合并相同的底层代码,因此可以使用相同的命令)。 假设您在分支devel上,其上游是origin/devel。 首先,我们将将devel重命名为devel-full,以表示它是具有完整提交序列的分支。 然后,我们将创建一个新的devel跟踪origin/devel,并“压缩合并”devel-full
git branch -m devel devel-full
git checkout --track origin/devel
git merge --squash devel-full
git commit

你需要单独执行 git commit,因为 --squash 参数会抑制 git merge 通常所做的提交。第三种方法是检出顶端版本并进行提交,这种方式稍微有点令人害怕。同样地,假设我们使用了之前提到过的 devel 分支名称,我们将 "full develoment" 分支名移动到一旁,并创建一个新的本地分支来进行新的提交。不过这次,在执行 git commit 命令之前,我们需要使用两个命令,而不是使用 git merge --squash
git branch -m devel devel-full
git checkout --track origin/devel
git rm -rf .                      # assumes you're in the top directory
git checkout devel-full -- .
git commit
git rm -rf .命令会在索引/暂存区中预定删除每个单独的文件,但git checkout devel-full -- .则告诉git重新填充索引/暂存区,并使用devel-full分支顶部的每个文件。这意味着“为下一次提交创建的树与devel-full分支顶部的树完全相同”。
(删除并重新创建的方法适用于merge --squash无法解决的一种情况:即将一个分支的末尾替换为另一个分支的末尾,即使这两个分支不相关并且因此不能合并。否则,merge --squash更简单,至少看起来不那么可怕!)
这些其他“容易”的方式都让你拥有完整的开发历史记录的分支。如果需要,那很好!如果不需要,则必须将其删除。

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