rebase与merge其实都是完成相似的工做的,可是背后的工做方式却有很大的不一样,让咱们用如下两张图来讲明一下二者的区别git
第一张图是merge的工做原理,当咱们在mywork分支中调用git merge origin
,git就会根据c2,c4,c6这三个点来计算合并出一个新的commitc点c7。3d
第二张图则是rebase的工做原理,当咱们在mywork分支中调用git rebase origin
时,c5变成了c5',c6变成了c6',而且父commit指向了c4,而原来的c5和c6就没有用了。指针
从上面就能够看出,在执行git rebase后会将两条分支有变成在同一条commit链上,而merge则是依然能够看到分叉的commit链,背后呢其实就是等于rebase修改了git的提交历史,把以前分支上的commit一个一个接到另外一条分支上code
固然rebase的过程当中也同样有可能会产生冲突,这个时候只要咱们碰到一个冲突,就须要解决它,而后执行git add
添加,接着执行git rebase --continue
去转接下一个commit,直到全部commit都转接到了另外一条分支上。cdn
在rebase进行中的任什么时候刻,咱们均可以经过执行git rebase --abort
将分支恢复到rebase以前的状态。对象
rebase的好处就是会让咱们的commit链变得整洁好看,可是对于使用rebase须要注意的点就是不要在master分支上执行,由于一般咱们在master分支上的是相对稳定的代码,咱们不会想要去修改到它的历史,另外,不要对已经推送到远程的分支进行rebase,一般只对本地还未推出的分支作,由于远程的分支可能其余的用户已经将它们拉取到各自的本地版本库,咱们不但愿他们由于咱们执行了rebase而要更改本身本来的提交历史,这即使对git来讲也很难处理,会出现一些意想不到的状况。若是是上述这些场景仍是应该使用merge来完成。blog
git rebase --skip
直接的使用根基分支的修改咱们能够先经过git reflog找到以前rebase的那条记录id ip
git reset commit-id --hard
就能够回到rebase前的状态了。 另外在每次作rebase前,git都会为咱们建立一个ORIG_HEAD记录rebase前的状态让咱们不须要reflog就能够快速切回去,因此就能够直接执行git reset ORIG_HEAD --hard
开发
git rebase -i commit-id
进入互动模式
当非文字的文件产生冲突时,其实咱们是没有办法决定具体哪一行出了问题的,因此这时候咱们能够执行git checkout 文件名 --theirs
来使用别人的文件或者执行git checkout 文件名 --ours
来使用咱们的文件it
跟上面同样第2个同样咱们先进入互动模式,将pick改成s或者squash,这样连在一块儿的s就会与他们上一个pick合并成一个commit了,接着咱们保存退出,git依旧是循环让咱们去修改咱们改动的commit,完成以后咱们就作到了合并的效果了
同样是进入互动模式,接着将pick改成e或者edit,接着保存退出,这里咱们就会看到git提示咱们能够开始去修改commit,而且在结束后执行rebase continue的动做,其实这里git就暂时将咱们转至咱们要修改的那个commit
git reset HEAD^
,看过以前的文章就知道这里默认其实就是mixed模式,因此当前commit先后的修改就被转至工做区,接着咱们就能够按咱们想要的commit形式从新把工做区的文件添加到暂存区而且提交,最后执行rebase continue,咱们就成功完成了拆分commit的动做 其实这个跟上面的场景几乎差很少,惟一的区别是咱们不须要再使用git reset,而是直接添加提交咱们要的修改就能够了
调整顺序的话咱们只须要在互动模式中直接把pick的每条commit调成咱们要的顺序便可,而对于删除commit,咱们也只须要把想删掉的pick直接删除就能够了。固然在作这些操做以前都要思考清楚,毕竟像调整顺序的话若是你把对一个文件的修改commit放到了这个文件的建立commit以前,那就会出事了
git提供了一个filter-branch指令帮咱们批量的对commit进行改动,假设咱们的帐号信息放在了config/database.yml中,咱们能够执行git filter-branch --tree-filter "rm -f config/database.yml"
,这样git会对每一条commit都执行删除的操做,那若是我后悔了想要恢复回来怎么办呢,咱们能够执行git reset refs/original/refs/heads/master --hard
就可已恢复刚才的删除操做了,这个refs/original/refs/heads/master其实就是咱们以前说的ORIG_HEAD,是git在咱们作一些危险操做时备份出来的一个指针以便咱们撤回以前的操做。因此这里若是咱们真的要完全删除还须要把refs/original/refs/heads/master也一并删除掉。接着还有一个找的回来的地方就是reflog,因此咱们也要清理掉reflog,它默认是30天清除一次,咱们能够经过执行git reflog expire --all --expire=now
让git立刻清除掉。最后咱们在这一系列操做以后就会有一堆闲置没有用的对象了,咱们能够经过git fsck --unreachable
列出来这些对象,固然由于他们已经没有用了,咱们就能够直接执行git gc --prune=now
把它们即刻清理掉,这样咱们就完全的把帐号信息从git中删除了。从这里也看得出来其实对于git来讲要真正意义上删除记录是很是困难的,因此咱们都常常说git永远有后悔药吃,由于老是有办法能够找回原来的历史记录。
前面咱们介绍过了reset和rebase,那咱们先来讲一下revert,咱们能够经过执行git revert commit-id
来还原某一个commit,可是这个还原不是删除那个commit,其实revert是加了一个新的commit而后把内容修改成咱们想要revert的那个commit以前的状态(这里若是在revert时想用默认的commit信息不去修改的话能够加上--no-edit选项)。
因此能够看出reset和rebase是会改变历史记录的,而revert则不会
reset多用于尚未推送到远程的commit,咱们能够将它还原至某个咱们指定的commit
rebase也多用于没有推送到远程的commit,固然他的能力更强大一些,无论你想作新增,修改或是删除等均可以作到,很适合用来整理本地的commit历史
revert的话就比较适合已经推出去commit,或者一些团队开发规范下不许使用reset和rebase的状况