git rebase总结及git使用规范
一、git规范
场景一:如果代码commit到本地库了,但是commit之前忘记pull了,远程代码也已更新,此时不能使用pull直接拉取远程代码(分支会产生merge的记录):
解决方法:commit之后,使用git fetch,拉取远程代码到缓存区,然后使用git rebase origin/dev,此时会产生冲突,解决冲突后即可提交,这样分支不会产生merge的记录
场景二:commit提交之前先使用pull总是没问题的,但如果pull不下来,是因为代码和远程代码冲突了,此时有两种解决办法:
1.使用git stash 保存本地修改的代码到缓存区,此时代码会还原为没修改之前的,此时在使用git pull拉取代码,然后使用git stash pop恢复缓存区的代码,此时解决冲突即可提交
2.先commit提交本地代码到本地库,这时候不要直接pull(分支会产生merge的记录),先使用git fetch,拉取远程代码到缓存区,然后使用git rebase origin/dev,此时会产生冲突,解决冲突后即可提交,这样分支不会产生merge的记录
场景三:dev代码修改后,test分支需要同步更新修改:
解决方法:dev修改后push代码,切换到test分支,pull保证与远程代码一致,此时不能merge dev,而是使用git rebase dev(或者git rebase origin/dev),此时会把dev的代码都更新到本地test分支,然后在push到远程test分支,这样分支不会产生merge的记录
场景四:新增测试不稳定的版本,在dev上切一个临时分支tmp:
解决方法:手动打包放在dev上测试,如果功能稳定没问题,那么合并代码,步骤1:git rebase origin/tmp;合并完临时分支,还要在合一次origin/dev,步骤二:git rebase origin/dev,此时才可以push
场景五:取消merging状态
git reset --hard HEAD (or sha_1)
场景五:多人同时开发一个分支:
1、git pull
如果pull不下来,说明本地代码和远程分支有冲突,代码更新不了
2、git commit -m "xxx"
将本地代码添加到本地缓冲区
git commit -a -m "xxx"
3、git stash
如果本地有不需要提交的代码,git stash把你的修改保存起来,恢复到和你没修改之前一致
4、git fetch
更新远程代码到本地暂存区
5、git rebase
把本地暂存区的代码更新到工作区
6、有冲突的话把冲突解决
7、git status
git add . 将解决完冲突的文件添加提交
8、git rebase --continue
9、git push origin dev 提交远程分支
10、git stash pop 恢复不需要提交的文件
场景六:回滚远程仓库版本
1.git reset --hard xxx(git提交版本号)
2.git push --force
二、tag标签
到达一定阶段后,在代码封版时,需要将稳定的代码发布成一个版本,使用git 创建一个tag ,这样一个不可修改的历史代码版本就像被我们封存起来一样,不论是运维发布拉取,或者以后的代码版本管理,都是十分方便的
创建tag标签: git tag -a 版本号 -m 'tag备注' git tag -a v2.0.0 -m 'version2.0.0' 查看tag: git tag 提交tag到远程: git push origin --tags 删除tag: git tag -d v2.0.0 删除远程tag: git push origin :refs/tags/v2.0.0
三、rebase总结
-
如果新参与一个项目,首先需要本地clone远程仓库。之后会有一个master(若不特殊说明,均指代本地master)和远端的master(以下称为remote/master)是关联的,即remote/master是本地master的up-stream。
-
做开发时,要新建一个分支,如dev1,作为你的开发分支。开发完新特性后,将工作区(work tree)的修改提交至暂存区(index),然后commit,此时会在dev1分支上生成一个新的commit单号。
-
切换回master分支,然后使用pull或者fetch+merge命令与remote/master同步一下(此时可能会有来自其他开发者的提交),再合并(merge)dev1分支的,如果有冲突,则解决冲突。最后推送代码到远端仓库。
这是我刚开始实习时,常用的一个开发,推送代码的流程。久而久之,我发现了几个问题:
-
在master分支上解决冲突,可能会有些风险。比如一不注意,冲突没有正确解决,导致本地修改与别人的修改混在一起,本来稳定的与远程保持同步的master分支被你改混乱了,且没有其他的备份了。
-
在master分支上merge开发分支,如果master分支上有dev1没有的commit单号,则会产生一个携带merge信息的提交单。这个commit你也要推送到远端。企业开发一般会有一个评审分支,主管会对commit单号进行review,然后submit合并进remote/master分支中。merge信息的提交单也会让reviewer做一次review,然而没什么必要的。
-
一个人提交时,会有一个merge commit,那么10个人提交,就会有10个merge commit,此时分支树看起来会很混乱。零零散散的merge commit信息穿插在你的特性commit之间。
因此,我考虑使用另外一种本地分支管理策略,来改善这样的问题。
使用rebase命令。
先简要说下rebase命令的作用。
比如在dev1分支上,你提交了2个单,c1和c2。然后你在dev1分支上将master分支rebase到当前分支git rebase master
。此时,如果master分支已经与remote/master做了同步,更新了2个来自其他人的提交,c3和c4。
rebase会做如下操作:
- 把dev1分支上的c1和c2“拆”下来,并临时保存成c1'和c2'。git里将其称为patch
-
将master分支上更新的提交c3和c4合并进dev1分支上。
- 将c1'和c2',再按顺序接在c3和c4的提交后面,如果没有冲突,则rebase成功。此时c1'和c2'虽然和c1和c2的修改完全一样,但却已经不是原来的提交了,commit id已经变化了。
此时dev1分支包含master分支的所有commit,并且超前了两个commit。如果你现在切换至master分支,并执行git merge dev1
操作,由于没有不同于dev1的提交,merge操作就不会产生merge commit了。此时推送代码,也只会有两个commit。同时,master分支树笔直前进,分支很清晰地展示一个个提交。并且,上述的更新和提交代码的过程,是在dev1分支上修改冲突的,相对来说会比在master分支上修改更安全,如果不小心改混了,也能通过切换回master分支来找到稳定代码。
基于上述内容,可以使用如下流程来提交代码:
- 在dev1分支上进行开发,然后commit提交,在dev1分支上生成一个提交单。
- 切换到master分支,与remote/master分支同步。
- 切换回dev1分支,将master分支rebase到dev1分支上,如果有冲突,修改冲突。rebase操作的冲突修改与merge不一样,修改完冲突后,保存进index,然后直接
git rebase --continue
即可,不同再多做一次提交。 - 切换回master分支,合并dev1分支,此时合并会非常顺畅。然后push。
rebase的风险
之前提到,rebase会将当前分支的新提交拆下来,保存成patch,然后合并进其他分支新的commit,最后将patch接进当前分支。这是rebase对多条分支的操作。对于单条分支,rebase还能够合并多个commit单号,将多个提交合并成一个提交。
git rebase -i [commit id]
命令能够合并(整改)commit id
之前的所有commit单。加上-i
选项能够提供一个交互界面,分阶段修改commit信息并rebase。
但这里就会出现一个问题:如果你合并多个单号时,一不小心合并多了,将别人的提交也合并了,此时你本地的commit history和远程仓库的commit history不一样了,无论你如何push,都无法推送你的代码了。如果你并不记得rebase之前的HEAD指向的commit的commit ID的话,git reflog都救不了你。
tips: 你可以push时带上-f参数,强制覆盖远程commit history,你这样做估计会被打,因为覆盖之后,团队的其他人的本地commit history就与远程的不一样了,都无法推送了。
因此,请保证仅仅对自己私有的提交单进行rebase操作,对于已经合并进远程仓库的历史提交单,不要使用rebase操作合并commit单。