在开发中,经常会遇到提交了多个commit,但每个commit实际上只改动了一点点东西的情况,这样分支看起来会非常杂乱,例如:
我们从master分支签出一个分支:
修改一次代码后提交:
const a = "master分支";
const b = '第一次commit'
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
2
3
就这样反复多次
const a = "master分支";
const b = '第一次commit'
const c = '第二次commit'
const d = '第三次commit'
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
2
3
4
5
6
7
可以看到三次commit实际上只更改了三行代码,那么是不是可以将这三个commit合并成一个呢?这样分支看起来就会更加简洁,下面讲一下如何将多个commit合并
提示
如果是在自己的开发分支,一般是不需要进行commit合并的,这样在后续review时,可以清晰看到每个小的修改逻辑,方便查看代码变更的完整过程。如果是在大型的团队开发中且对代码管理要求特别高的时候,才考虑合并,因为这样可以保持主分支提交历史的整洁。
通过 git rebase 合并
git rebase 是变基的意思,这里就不再过多的说明这个指令了,只举例如何使用这个指令来合并commit
就按上面的例子来说,当前有3个commit,那么要将3个commit都合并,可以使用
git rebase -i HEAD~3
输入完之后可以看到下面这个界面,前三行就是已经提交的commit信息,后面则是一些说明,通过输入 i 进入编辑模式(不同的系统可能不一样)
缩写 | 命令 | 说明 |
---|---|---|
p | pick | 需要保留的提交,且不做更改 |
r | reword | 需要保留的提交,但是仅仅修改commit说明,一般用于修改commit说明 |
e | edit | 需要保留的提交,且能修改文件内容和commit信息 |
s | squash | 将该提交和前面一个commit合并,且保存之后允许修改commit信息 |
f | fixup | 需要保留的提交,如果都使用的fixup,则不能修改commit说明,直接变基成功 |
squash
我们使用 squash 来合并提交,将后面两个commit的 pick 改成 squash,保留第一个 pick,编辑完之后按下 esc 退出编辑模式,然后 :wq 保存(不同的系统可能不一样)
squash不能在第一行,否则git会报下面这个错,也就是说将中间的 pick 改成 squash 也是不行的,出现错误后可以执行 git rebase --edit-todo 进行修改,但是需要在保存后执行一次 git rebase --continue 才能继续完成变基
由于我们使用的是squash,所以允许修改commit说明,保存之后会出现下面这个界面,新生成的commit会默认使用所有commit信息的组合,如果不需要某条commit说明,可以在说明前加一个 # 号,例如:
将第一个commit说明加上–edit1,第二个commit加上#隐藏,第三个commit加上–edit3
编辑完之后保存退出,可以看到显示变基成功
再来看看提交记录,刚刚的三个commit合并成了一个,且commit信息就是刚刚修改后的第一条和第三条的commit信息
fixup
fixup 和 squash 类似,区别是不能修改commit的信息,如果都使用fixup则会直接提示变基成功,但是只要有一个squash,则也会进入编辑commit说明模式,而fixup的部分则默认前面带#不显示该commit说明
reword
reword 不会合并commit,一般只用于修改commit说明,使用reword保存后,会依次修改需要更改commit说明的commit,变基完成之后不论修改与否,都会生成一个新的commit,例如仅修改第一个和第三个commit
edit
edit 也不会合并commit,它和reword的区别是edit不仅能修改commit说明,还能修改commit变更内容,使用后会依次编辑每一个commit的内容,例如:
保存之后会,会在每个需要edit的commit暂停,并进入编辑模式
如果有编辑文件,在编辑完之后需要使用 git add . 暂存后,再使用 git commit --amend(直接使用git rebase --continue也行)修改commit说明,修改完之后使用 git rebase --continue 进入下一个edit修改,直到结束为止,如果中间有冲突,则需要解决冲突后再继续
终止合并
如果在合并过程中想要终止,可以使用
git rebase --abort
git reset
通过 git reset 也可以实现同样的效果,git reset 可以将分支的HEAD指针(当前分支所在提交位置)重新定位到指定的commit
soft可以将在改变当前指针的同时,还会将改变的commit之间的改动放到暂存区,例如:
git reset --soft HEAD~3
可以看到三次commit的改动都被还原了
这时候只要重新设置一下commit说明就可以重新提交了,提交记录可以看到和 git rebase 一样的效果
相比于 git rebase ,git reset 能更快速的完成合并,但是 git rebase 会更灵活一点
注意
不管是 git rebase 还是 git reset 都会更改提交历史,如果当前分支已经合并到主分支,就不建议再使用了,否则可能引起冲突或者影响他人。