- Git squash合并多个commit,把多个commit融合成一个
git merge squash 和 rebase 区别
merge 的几种形式
在 merge pr 的时候,默认是有三种选项的,分别是
- 普通的 merge
- rebase merge
- squash merge
这其实对应于我们在合并分支的时候的几种方式,所以我就以本地分支的形式来说说有啥区别。
一个简单的模型
假设我们一开始的 master 分支上已经有了几个提交,就像这样:
然后,我们切出一条开发的分支,进行了一些 Feature 的开发,然后我们的分支可能就是这种情况:
这种情况还好,也比较常遇到,但是,现在问题来了,如果在这个时候 master 有了一些新提交(可能是其他分支合并进来的),那么这个时候情形就成了这样:
这个情况很有趣,但是我们不讨论,因为这和我们今天的主题无关,以后可以另外开一个话题来说,今天要说的是第二个情况。
普通 Merge
说到合并分支,可能我们最熟悉的操作是这样的:
- 先切换到目标分支(master)
- 执行命令:
git merge devel
- 删除旧分支(可以在上面一同做):
git branch -D devel
- 提交到远程分支:
git push origin master
好像这样没啥问题的样子,但是这样操作之后,你知道结果是怎么样吗?假设合并之前的这样的:
我们这么一番操作之后,那么最后我们的分支的历史将会是这样的:
是的,看上去很不错,也是一条直直的 commit line,我们在 devel 分支中的 commit 也是一个不差得保留在了 master 中。但是,很多时候,我们并不需要那么多的 commit,假设你给一个开源项目提交一个 Bug Fixes,然后一个简单的修改因为你的粗心大意 pr 了十几个 commit 过去,如果作者给你 merge 了,这就在这个项目的历史长河中增加了十几个 commit 啊,以后的人看 commit history 估计都崩溃了吧;同时,对于你自己管理的项目来说,当你 merge 之后发现有问题,想回滚都蛋疼!
squash merge
在使用 git 的过程中,可能你遇到过想要合并多个 commit 为一个,然后很多人会告诉你用 git commit --amend
,然后你发现里面有你的多个 commit 历史,你可以通过 pick 选择,squash 合并等等。同样得,merge 的时候也可以这么干,你只需要这么简单的两步:
- 切换到目标分支:
git checkout master
- 以 squash 的形式 merge:
git merge --squash devel
你会发现,在 master 分支上居然有未提交的修改,然后你就需要在 master 上主动提交了修改,注意,这里是你 commit 的,也就是改变了 commit 的 author。结果是这样的:
这里好了,比前面普通的 merge 来说,我们只有一个 commit 了,不管在分支中 commit 了多少,这里都只有一个!
rebase merge
但是,作为处女座的程序员肯定是不能忍受目前的情况的,因为我们既想合并 commits,又想保留作者的信息,那么有没有什么好办法呢?肯定是有的啦,这个时候我们可以尝试一下 rebase,操作步骤是这样的:
- 先切换到 devel 分支(不一样咯):
git checkout devel
- 变基:
git rebase -i master
- 切换回目标分支:
git checkout master
- 合并:
git merge devel
这里完成了第二步之后我想你应该大概知道发生了什么事了,我们在 devel 里面对照 master 进行了变基,所谓的变基其实就是找到两个分支共同的祖先,然后在当前分支上合并从共同祖先到现在的所有 commit,所以我们在第二步的时候会选择怎么处理这些 commit,然后我们就得到了一个从公共commit 到现在的单个 commit,这个时候别人讲我们这个 commit 合并到 master 也只会在 master 上留下一个 commit 记录,就像这样:
虽然这个 commit history 线看上去很不错,而且也比较符合实际情况,但是我们需要注意到的有点就是分支上的开发者需要自己执行变基操作,从而导致他的原始 commit history 变化了(可以理解成被合并了)。
对比
相比一下前面三种方式,我们可以总结出一些东西:
- rebase 可以尽可能保持 master 分支干净整洁,并且易于识别 author
- squash 也可以保持 master 分支干净,但是 master 中 author 都是 maintainer,而不是原 owner
- merge 不能保持 master 分支干净,但是保持了所有的 commit history,大多数情况下都是不好的,个别情况挺好
链接:https://www.jianshu.com/p/684a8ae9dcf1
我有一个非常混乱的git历史。我想压制一堆旧的提交(不包括最后一个提交)。
我知道如何压制我最后的n
提交。但这是不同的。在这里,我连续提交n1
到n2
我想要压缩成一个,而在n2
我有一个提交历史后,我想保留到最后一个。
所以,如果我目前的历史是这样的:
---- n1 --- n2 -------- m
我想挤压n1
,n2
所以最终看起来像这样:
---- n1n2 -------- m
其中n1n2
是一个包含来自n1
to 的压缩内容的提交n2
。
我该怎么做?对历史的影响n2
是m
什么?
也就是说,每次提交的哈希都会因为我想做什么n2
而m
改变?
- 启动交互式rebase:
git rebase -i HEAD~n (where ’n’ is how far do you want to go back in history)
- 您的默认编辑器将打开。在顶部,
n
将以相反的顺序显示最新提交的列表。例如:
pick a5f4a0d commit-1
pick 19aab46 commit-2
pick 1733ea4 commit-3
pick 827a099 commit-4
pick 10c3f38 commit-5
pick d32d526 commit-6
- 为要压缩的所有提交指定
squash
(或快捷方式s
)。例如:
pick a5f4a0d commit-1
pick 19aab46 commit-2
squash 1733ea4 commit-3
squash 827a099 commit-4
pick 10c3f38 commit-5
pick d32d526 commit-6
Git将更改和更改直接应用于它之前,并使您将提交消息合并在一起。
- 保存并退出。
- Git将应用所有更改,并将再次打开您的编辑器以合并三个提交消息。您可以修改提交消息或保持原样(如果是这样,将连接所有提交的提交消息)。
- 你完成了!您选择的提交将全部与前一个提交相关。