在协同开发中,为了项目管理的方便,会采用各种各样的工作流,有关工作流方面的内容,可以到开头提供的第三个网址里学习。这里只简单介绍两个实用的技巧。
01 一个干净的push!
在Git中,我们很少会改变中央仓库的已有的提交历史。因为那样会使所有的开发者的本地仓库和中央仓库处在不一致的状态,所有开发者都需要因为这个改动调整自己的本地仓库来与中央仓库保持一致。如果改动很大,调整将会很复杂。只要不是及其严重的事情,我们都不会去调整中央仓库的提交历史。所以,推送上去是什么样的,以后就会是什么样的,对于中央仓库来说,第一印象会伴随一生。
所以,就像女生出门前要化妆一样,在推送本地分支到中央仓库前,要把本地分支整理干净。一般,我们会给每个功能新建一个分支,在功能开发完成后,可以用git rebase -i
命令删去那些无关紧要的提交,并将提交历史书写成我们逻辑中的样子。
比如我们接到了生产车门的任务,现在我们把它完成了,开发历史如下。
在我们把它推送到中央仓库前,我们用git rebase -i <commitID>
整理一下它的提交历史,commitID
参数用来指定我们要从哪个提交开始修改。由于我们是从BetterWheelCompleted
这个提交开始开发车门的,所以commitID
参数应该是这个提交的版本号,为99c7ed1578c0a16a0f1de58548883a8d6e8eeb8a
。执行以下指令。
$ git rebase -i 99c7ed1578c0a16a0f1de58548883a8d6e8eeb8a
之后,在命令行中会显示vi
文本编辑窗口。
在最前面的是各个提交,每个提交的前面是要对该提交执行的命令,默认是pick
采用提交,我们主要会用到squash
命令,squash
命令可以让我们把对应提交合并到上一次提交中,举个栗子,给69218a4
提交加上squash
命令就可以把它合并到f7a6770
提交上。我们开发车门主要有三个阶段,设计车架,设计车窗,加入手势开门功能,所以我们要分别合并第二、三、四次提交。我们把它编辑如下,然后保存并退出编辑窗口。
之后在合并第二、三、四次提交时,Git又会弹出一个文本编辑窗口,让我们合并三次提交的提交信息。
因为这一次合并提交的信息应该为“设计了车窗”,刚好是第二个提交的提交信息,所以我们在不要的信息前加#
把他们忽略即可。合并完成后,我们的提交历史就变成了如下模样。
然后就可以把他们推送到远端,由其他人检查代码,然后合并或者变基到主分支上了。如果在开发完后就已经决定要这条分支变基到主分支上,那也可以直接用git rebase -i <branch_name>
来修改提交历史并变基。
很多时候,我们会把一个功能的实现分为好几个功能点,然后给功能点新建一条分支,然后在这个功能点开发完后把修改整合回功能分支。这样做的好处是可以保持功能分支的整洁,不会在功能分支上生成很多备份代码的提交,但是如果用普通的merge
操作的话,合并生成的提交除了指向主功能分支外,也会指向功能点分支,那样还是会把功能点分支上的提交历史混合进来。
这时我们可以使用git merge --squash <branch_name>
,在merge
指令中加入--squash
后会把branch_name
分支上的所有提交压缩成一个加入到当前分支上来,并且这个提交不会指向被合并的分支。举个栗子,在上次我们把车门的分支推送上去给老大检查后,老大说要再加一个手势关门的功能!然后我们为了主分支的整洁,给这个小功能新建了一个分支进行开发。接着,经过了不懈努力,我们终于开发完了,现在提交历史如下。
我们用以下指令把feature-CloaseDoorwithGesture
分支的上的提交压缩成一个一条放到produceDoor
分支上。
$ git checkout produceDoor
$ git merge --squash feature-CloseDoorwithGesture
$ git commit -m "给车门加上了手势关门的功能"
执行完成后我们的提交历史就变成了下图所示模样。
可以看到新建的提交并没有指向feature-CloaseDoorwithGesture
分支。在合并完后,我们可以删除feature-CloaseDoorwithGesture
分支。
$ git branch -D feature-CloseDoorwithGesture
OK。老大说干的不错,让我们把分支合并到主分支上,然后推送到中央仓库来结束这个功能的开发。
$ git merge --no-ff produceDoor -m "车门生产完成"
$ git push origin master
执行完成后,提交历史如下。
Android Studio中的相应操作
用VCS->Git->Merge Changes
下的合并操作,并选中squash commit
选项,即可完成git merge --squash <branch_name>
操作。
git rebase -i <commitID>
可以用VCS->Git->Rebase
完成。点击后会弹出如下对话框,选中Interactive
选项,在Branch
选项中选中要变基的分支,在Onto
选项中,填写新基点(会从这个提交开始修改历史)。点击Rebase之后,会弹出如下对话框, 在这里你可以选择要对每个提交执行的命令。选完命令之后,按下Start Rebasing就会开始变基。
02 push失败?
·我们把前面开发车门那个同学叫做小明,在小明开发车门,引擎和轮子的同时,还有一个叫小刚的同学在开发制动系统。小刚现在刚把制动系统开发完毕,他现在的提交历史如下。
现在他要把master
分支推送到中央仓库上。小刚运行了如下指令。
$ git push origin master
但是Git告诉他,push
被拒绝了,因为中央仓库的master
分支里有本地还没有的改动,并让我们把改动集成到本地仓库中再进行push
。
所以在每次push
前,应该先抓取中央仓库上的更新,然后集成到本地,再提交。抓取更新的指令是git fetch <remote_name> <branch_name>
。现在小明用以下指令拉取了中央仓库上的更新。
$ git fetch origin master
拉取后,本地的提交历史变成了这样。其中,origin/master
表示的就是远程仓库上的master
分支,远程分支是只读的,不能修改。
现在小明要把远程仓库的master
分支上的改动集成到本地的master
上。根据之前学的知识,有两种方法,rebase
和merge
。
如果使用merge
,把本地origin/master
分支的内容合并到master
上,合并后提交历史如下。很乱,而且因为合并而新生成了一个节点。
如果用rebase
,把本地master
分支的内容变基到origin/master
上,合并后提交历史如下。清楚了很多,而且看起来像是串行开发的一样。一般把远端更新整合到本地会用rebase
指令。
注意:
rebase
操作会把可以快进的合并记录删掉,最后rebase
结果就像是合并时使用了快进一样。如果要保留合并记录,可以加入--preserve-merges
参数。
上面提到过git pull <remote_name> <branch_name>
操作,它其实是git fetch <remote_name> <branch_name>
和git merge
的结合。当然也可以转化为rebase
版本,在中间加个参数,git pull --rebase <remote_name> <branch_name>
。
其实上面的操作有更好的处理方法。在功能开发完成后,可以先把远端的更新用git pull --rebase
拉取下来,并把本地分支的更新变基到上面去。之后,把功能分支rebase
到主分支上,这个时候远端的更新就全部整合进功能分支了,这个时候我们可以测试一下合并之后代码还正不正确。最后再把功能分支合并到主分支并推送上去。
Android Studio中的相应操作
在Android Studio中,可以选择用merge
版本还是rebase
版本的pull
操作。按下下图中的按钮。在弹出的对话框中,选择拉取方式。右边的选项是存储当前工作区修改的方式。选择完成后,点击ok即可开始拉取。