git merge 和 git rebase 的区别

概念

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分支,我们分别在此基础上执行 mergerebase 操作:

git checkout dev

执行 merge 操作

sql 复制代码
git merge master

如果master3dev2有冲突,则需要解决冲突,执行完上述命令后查看下提交记录:

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

我们可以看到 dev1dev2 的两次提交被放到了 master3 前面,另外还会额外的多出一条 merge 的提交记录。

执行 rebase 操作

我们先使用 git reflog,找到 dev2HashId,代码回退到 dev2

ini 复制代码
git reflog --pretty=oneline
git reset --hard 82385e3 

然后再执行 rebase 操作:

git rebase master

如果master3dev1dev2都有冲突的话,这里可能会需要解决两次冲突,解决办法就是可以将 dev 分支的多次提交合并成一次提交,然后再进行 rebase 操作,可参考git-rebase多次冲突产生原因及解决办法。执行完上述命令后查看下提交记录:

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

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

明明本地的就是最新的代码,为什么会有需要拉取的呢?我们仔细去看下原本的提交记录的截图,可以看到 dev1dev2commitId 在执行 rebase 操作前后不一样。所以远程 dev1dev2commitId 跟本地的 dev1dev2commitId不一样,因此 git 认为本地有2个需要拉取的。本地有三个新的 commitId,而远程没有,所以 git 认为本地有三个需要推送。

这个时候如果我们执行了拉取操作:

git pull

执行完上述命令后查看下提交记录:

可以看到果然如同原本我们的猜想,远程 dev1dev2 的那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>: 保留该commit
  • r, 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进入编辑模式,将dev5dev4pick改为s,将这两次提交合并到dev3上面:

按下Esc退出编辑模式,按下":"冒号,再输入wq保存并退出。

终端会再次出现让我们修改提交信息的界面,按照之前的操作,我们把dev5dev4的提交信息注释掉,将dev3的提交信息改为"合并dev5和dev4的dev3",然后保存并退出。

合并完成之后查看下我们的提交记录:

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

参考文章

1、git log和git reflog的区别

2、git-rebase多次冲突产生原因及解决办法

3、2张图分清git rebase和git merge

4、Git使用Merge和Rebase区别及心得技巧

5、我到底应该用git-merge还是git-rebase呢?

6、使用git rebase合并多次commit

相关推荐
我要洋人死1 小时前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
stewie61 小时前
在IDEA中使用Git
java·git
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9152 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼3 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍