假设我的本地git log
显示:
739b36d3a314483a2d4a14268612cd955c6af9fb a
...
c42fff47a257b72ab3fabaa0bcc2be9cd50d5c89 x
c4149ba120b30955a9285ed721b795cd2b82dd65 y
dce99bcc4b79622d2658208d2371ee490dff7d28 z
我的远程git log
显示:
c4149ba120b30955a9285ed721b795cd2b82dd65 y
dce99bcc4b79622d2658208d2371ee490dff7d28 z
最简单的方法是什么(假设任意大量的本地提交):
527b5810cfd8f45f18ae807af1fe1e54a0312bce a ... x
c4149ba120b30955a9285ed721b795cd2b82dd65 y
dce99bcc4b79622d2658208d2371ee490dff7d28 z
答案 0 :(得分:16)
如果A到X之间的中间提交数量相对较少,那么使用interactive rebasing就可以了。
git rebase -i origin/master
然而,根据我的个人经验,在大量提交中使用交互式变基很慢。我一次在大约一百次提交上运行交互式rebase(在使用Git Bash的Windows机器上),并且msysgit花了很长时间生成交互式rebase提交编辑器,允许您选择要运行的操作在哪个提交上,因为,列表最终变得非常大。
在这种情况下,您有几种解决方法。
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之间差异的差异补丁,然后将补丁应用为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 in the comments所指出的,如果涉及二进制文件,这将不会起作用。除了文本文件之外,git diff --binary
可用于输出二进制文件的差异,但我不确定这些差异是否也可以用作补丁。
答案 1 :(得分:7)
一个选项是git rebase -i @{u}
。我经常使用它,因为它将它作为git freebase
别名(因为它适用于你可以自由变换的提交)。
如果您不熟悉,@{u}
是@{upstream}
的快捷方式,或“当前分支的上游”。
答案 2 :(得分:3)
“最简单”总是有点棘手。交互式变基将让你压缩所有东西,并且从某种程度来说是“容易的”。
另一种看似有点复杂的“简单”方法是使用“squash merge”(根本不是实际的合并,但使用与git merge
相同的底层代码,所以它已完成用同样的命令)。假设您在上游为devel
的分支origin/devel
上。首先,我们将devel
重命名为devel-full
,以表明它是具有完整提交序列的那个。然后我们将创建一个新的devel
跟踪origin/devel
和“squash-merge”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
和以前一样,我们将“完全开发”分支名称移开,并创建一个新的本地分支来进行新的提交。但是,这次而不是git merge --squash
,我们在git commit
之前使用了两个命令:
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
的提示树一样。”
(remove-and-re-create方法适用于merge --squash
没有的情况:具体来说,它适用于用另一个分支的尖端“替换”一个分支的尖端,即使两个分支也是如此是不相关的,因此不可合并。否则merge --squash
会缩短一步,绝对不会像看起来那样可怕!)
这两种“简单”(?)方式都为您提供了一个具有完整开发历史的分支。如果你想要它,太棒了!如果没有,你必须删除它。