概念
git merge是将一个分支的更改合并到另一个分支的命令,它会把合并的结果生成一个新的提交节点。
git rebase也可以将一个分支的更改合并到另一个分支,它会重新设置基线,将你当前的分支重新设置开始点。假如现在有一个master分支,提交了两次代码,然后从master拉取一个新分支dev,则dev的基准点是master的第二次提交,dev分支也提交两次代码,master再次提交一次代码,这个时候在dev分支执行git rebase master,则dev的基准点会变成master的第三次提交,dev的两次提交会被放到master的第三次提交后面。这里有点绕,可以看下面的例子进行理解。git rebase还可以合并提交记录。
实践操作
我们先来新建一个项目,主分支为 master 分支,提交两次代码:
sql
git commit -am "master1"
git commit -am "master2"
git push
再拉取一个 dev 新分支,也提交两次代码:
sql
git commit -am "dev1"
git commit -am "dev2"
git push
来看下 dev 分支的提交记录:

再切换到 master 分支,提交一次代码:
sql
git commit -am "master3"
来看下 master 分支的提交记录:

然后切换到dev分支,我们分别在此基础上执行 merge 和 rebase 操作:
git checkout dev
执行 merge 操作
sql
git merge master
如果master3和dev2有冲突,则需要解决冲突,执行完上述命令后查看下提交记录:

通过提交树更直观的看下:

我们可以看到 dev1 和 dev2 的两次提交被放到了 master3 前面,另外还会额外的多出一条 merge 的提交记录。
执行 rebase 操作
我们先使用 git reflog,找到 dev2 的 HashId,代码回退到 dev2
ini
git reflog --pretty=oneline
git reset --hard 82385e3
然后再执行 rebase 操作:
git rebase master
如果master3和dev1、dev2都有冲突的话,这里可能会需要解决两次冲突,解决办法就是可以将 dev 分支的多次提交合并成一次提交,然后再进行 rebase 操作,可参考git-rebase多次冲突产生原因及解决办法。执行完上述命令后查看下提交记录:

通过提交树更直观的看下:

我们可以看到 master3 被放到了 dev1 前面。打开 vscode 的源代码管理,显示有2个需要拉取的,有3个需要推送的:

明明本地的就是最新的代码,为什么会有需要拉取的呢?我们仔细去看下原本的提交记录的截图,可以看到 dev1 和 dev2 的 commitId 在执行 rebase 操作前后不一样。所以远程 dev1 和 dev2 的 commitId 跟本地的 dev1 和 dev2 的commitId不一样,因此 git 认为本地有2个需要拉取的。本地有三个新的 commitId,而远程没有,所以 git 认为本地有三个需要推送。
这个时候如果我们执行了拉取操作:
git pull
执行完上述命令后查看下提交记录:

可以看到果然如同原本我们的猜想,远程 dev1 和 dev2 的那2条提交记录被拉取下来了,但是还多了一条 merge 的提交记录,这是为什么呢?
通过提交树更直观的看下:

会产生一个 merge 的提交记录,是因为 git pull 等于 git fetch + git merge。
其实远程的两次提交记录的内容跟本地的两次提交记录内容是一样的,所以我们不应该执行git pull,直接强制提交即可:
scss
// 先把上步的pull回退
git reset --hard HEAD~1
// 再强制提交
git push -f
总结
merge 操作会按照提交时间顺序保留所有的提交记录,commit Id也不会变,合并的结果会额外的产生一条merge提交记录,可以让我们知道代码是什么时候合并的,是从哪个分支合并进来的。
rebase操作会改变分支的基准点,所以commit Id都会改变,但是它不会产生额外的提交记录,会让我们的提交树呈现一条直线,比较清晰。
公共分支建议使用 merge,可以完整的保留提交记录及合并记录,方便溯源。
自己的开发分支建议使用 rebase,如果公共分支更新频繁的话,多次使用merge会产生很多的合并记录,对自己的分支产生一定的污染,使用rebase可以很清晰的看到自己的分支每一次代码提交。
最佳实践
开发完成后需要把自己分支的代码合并的主分支,则可以按以下步骤操作:
1、先在自己的开发分支进行git rebase master,如果冲突过多的话可以先将自己开发分支的多次提交合并成一次提交。
2、在master进行git merge my-branch,将自己的开发分支合并到主分支。
git rebase 合并提交记录
git rebase也可以将多次提交记录合并成一次,比如说对于我们拉取出来的开发分支,可能会在开发过程中多次提交测试,会产生很多不规范的、不必要的提交,这类提交将会造成我们的分支污染,当出现问题需要回退时,将增加难度,所以我们可以将其合并起来:
arduino
// 前开后闭,不输endCommitId的话默认是当前分支HEAD所指向的commit
git rebase -i <startCommitId> <endCommitId>
// 下面这种写法也支持,n是要合并的个数
git rebase -i HEAD~n
我们先查看下提交记录:
ini
git log --pretty=oneline

我们要把最后三个提交记录合并成一个提交记录:
css
git rebase -i 39323944c92f0dd28a0453ba5211e0473331cba5 f1b88c437420196c74e78625c9ceb31a9751f966
// 或者只输入起点
git rebase -i 39323944c92f0dd28a0453ba5211e0473331cba5
// 或者使用下面这边也可以
git rebase -i HEAD~3
然后会出现以下界面:

p, pick <commit>: 保留该commitr, reword <commit>: 保留该commit,但是要修改提交信息e, edit <commit>: 保留该commit,但是要停下来修改提交信息和内容s, squash <commit>: 保留该commit,并合并到前一个commit中f, fixup [-C | -c] <commit>: 和squash作用相同,但是不保留提交信息x, exec <command>: 执行shell命令b, break: 中断合并d, drop <commit>: 不要该commit
我们使用s,按下i进入编辑模式,将dev5和dev4的pick改为s,将这两次提交合并到dev3上面:

按下Esc退出编辑模式,按下":"冒号,再输入wq保存并退出。
终端会再次出现让我们修改提交信息的界面,按照之前的操作,我们把dev5和dev4的提交信息注释掉,将dev3的提交信息改为"合并dev5和dev4的dev3",然后保存并退出。
合并完成之后查看下我们的提交记录:

dev4和dev5的提交记录已经没有了,只有我们刚刚改了提交信息的合并dev5和dev4的dev3。