概念
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>
: 保留该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
进入编辑模式,将dev5
和dev4
的pick
改为s
,将这两次提交合并到dev3
上面:
按下Esc
退出编辑模式,按下":"冒号,再输入wq
保存并退出。
终端会再次出现让我们修改提交信息的界面,按照之前的操作,我们把dev5
和dev4
的提交信息注释掉,将dev3
的提交信息改为"合并dev5和dev4的dev3",然后保存并退出。
合并完成之后查看下我们的提交记录:
dev4
和dev5
的提交记录已经没有了,只有我们刚刚改了提交信息的合并dev5和dev4的dev3
。