Project Description
【分享实录】git很流行,但你未必真懂!
2017年07月24日 WeX5移动开发云 起步软件
华为大厂来了!码农基地火了!
7月18日晚上8点,来自华为的美女程序媛潘静做客码农基地,分享了自己对git工具的认识和理解。这并不是码农基地首次邀请女程序员前来分享,但是当晚的人气还是让我们大吃一惊,1900多名程序员参与了线上活动!码农基地负责人热泪盈眶地说,今后还是要想办法多邀请女程序员来做分享!
闲话少说,下面奉上当晚分享活动的文字实录:
大家好,我是潘静,今天很高兴和大家一起交流关于GIT工具的原理和使用,希望对大家有所帮助。
在讲解GIT之前,我们先聊聊版本控制系统。在一个研发团队中,最核心的产物就是代码,它在开发的过程中越来越多,不断地增长,如何去存储、追踪这些文件或者是目录的变化,避免文件丢失或者是相互间的覆盖,主要靠的就是版本控制系统。
版本控制系统主要分为两类,集中式和分布式。集中式版本控制的特点是只有一台中央服务器,它存放着所有的数据,包括代码和提交记录。而其他客户端机器上,保存的是中央服务器最新版本的快照,它不包括那些logo,所以开发人员想工作的话,它必须要联网。
分布式版本控制系统的特点是,每台PC上都是代码仓库的完整镜像,它没有所谓的中央服务器和客户端之说。但是有人可能会问,我们公司用的也是GIT,那为什么也有中央服务器啊?大家也会说,把代码传到服务器上。其实这个所谓的服务器,仅仅是为了约定大家多人写作,是人为指定的,它和其它的机器本质上是相同的,任何一台客户端机器,都可以胜任这份工作。
集中式和分布式版本控制系统各有优缺点。对集中式版本控制系统来说,它的操作比较简单,使用没有难度,可以轻松地上手,并且对客户端机器的配置要求不高,因为它不用存储整套的代码。当然缺点也是显而易见的,中央服务器如果单点产生故障的话,整个服务器宕机,所有人都没有办法工作了。并且在没有备份的情况下,磁盘一旦被损坏,所有的数据都将丢失。
分布式版本控制系统的优点是,版本库是本地化的,包括所有的标签、分支、版本记录等等,它更适合的是一个跨地域的协同开发。它的缺点当然也可以看出,它学习成本比较高,不容易上手,只能针对整个仓库去创建分支,没有办法更细地根据一个文件夹去创建分支。
集中式版本控制系统常见的有CVS、VSS、SVN、ClearCase等,而分布式版本控制系统一般有GitHub、Mercurial 和 Bazaar。这两类中比较有代表性的就是SVN和GIT,他们两个都有一个比较好的使用群体,优缺点也经常被比较,但在现在敏捷开发已经成为主流、迭代交互越来越快的情况下,我觉得GIT的趋势越来越好,被越来越多的人所使用。
GIT诞生于2002年,由linux之父和他的团队开发并不断完善,它秉承了linux的开源精神,为我们开发团队带来了一个非常棒的而且完全免费的版本控制体验。
在上图中可以看到GIT的工作模式,一次代码被成功地提交到远端的仓库,要经历四个阶段:本地工作区、暂存区、版本库和远端版本库,文件在这四个状态中是不同的。
在本地工作区包括三个类型的文件,新增文件、被修改的文件和被删除的文件,如果你用GIT STATUS命令去查看的话,可以看到有两种类型,一种是untracked files,它表示的是未被跟踪的文件,一般指的是你新添加的文件;还有一种是Changes not staged for commit,这种类型的文件一般包括的就是 modified和deleted。
对已经修改的文件,我们进行GIT add或者GIT REMOVE操作的话,文件就到了暂存区。暂存区其实就是一个文件索引的目录树,它记录了所有的文件名、文件状态信息。如果把暂存区的文件,进行commit提交操作的话,文件就到了本地的版本仓库,这时候如果再进行git push操作的话,就会把本端版本库的内容push到远端版本仓库,完成了一次版本库的提交。
让我们来思考一下,版本控制系统它应该如何来记录每次提交呢?我觉得正常的思维肯定是记录差异,也就是说记录修改文件有什么不同。的确,大多数版本控制系统就是这么做的,比如我们熟悉的CVS、SVN啊,但是GIT却不是这样,它每次提交时,把全部的文件做了一个快照,并且保存了指向这个快照的一个索引。
这种保存其实有很多好处,比如说切换版本的时候,我直接引用指向目标版本的索引就行了,就不需要差异存储的那样,我还需要版本之间的merge,所以速度就会提升许多。下面我将讲到的轻量级的分支切换,就是因为这个原理。
GIT仓库的默认分支是master分支,分支的本质是一个指向commit的指针,它会随着每次提交不停地向前移动。而git创建一个分支的本质就是创建一个指向最后一次提交的可变指针,所以git分支的创建不是复制整个版本库的内容,仅仅是新建了一个指针,它以40个字符长的长度保存在文件里,这是一个几乎令人难以想象的轻量级。
如何去识别当前的分支是在哪里呢?这个就需要head指针来识别,就像上图所示的那样,分支切换前head指针指向的是master,当前的分支就是master,切换之后,head指针指向的是testing,当前的分支就是testing分支。
有分支的存在就必然会存在分支合并的情况,并且我们日常使用中出现的问题,一般也是分支合并引起的问题。无论哪种工作流,都会涉及到分支合并。它就是说把一个分支的修改整合到当前分支,一般有两种方法,一种是merge,一种是rebase。
这两种方法经常让我们使用的非常不顺手,或者是有一些误区。我们现在就通过对一种场景进行不同的操作产生的两种效果,来看这两种方法的区别。
上图中的场景是master分支上新增了一个C4的节点,hotfix分支上新增了一个C3的节点,我们要将hotfix分支合并到master分支上,什么时候容易有这种情况呢?通常是我们生产环境上出现了一个问题,需要拉出一个hotfix分支,然后解决以后,合入到master分支或者是develop分支,会有这种情况。
Merge的结果从log上看,它包括hotfix的新增节点C3,master的新增节点C4,以及两者的共同祖先节点C2,这种合并操作非常简单,但是有了一个新增的节点C5,它使整个合并的log上形成了一个环形,所以版本的可读性非常差。
而rebase的操作呢,它先将master分支新增的节点C4,以补丁的形式保存在.git rebase这个目录下,然后再去同步hotfix分支的最新代码,最终再将这个补丁C4 ’去应用到这个分支上,这种log看上去非常地清晰。
有合并就会有冲突,一般没有冲突的地方,系统会自动将他们合并到一起。而有一些冲突是需要我们手动来解决的。一般有两种冲突,第一种就是说两个合并分支它修改了同一行代码,这种冲突会在整个文件中以这种尖括号、等号的形式将它括起来。
就如上图中可以看到的,head所指的那一部分,等号和左尖括号中间的那一部分,指的是当前分支修改的内容,而等号下面的那一部分,是原分支的修改的内容,一般这些需要你人为地去确认,哪一些代码需要留下来,哪一些代码需要删掉,人为地解决之后,然后再把这个代码去提交。
还有一种类型的冲突,是文件被重命名为不同的名字,这种我们需要去确认哪一个名字是正确的,然后把错误的名字去删除,再提交修改。
主持人:感谢老师精彩的分享,下面进入第二环节,老师答疑部分,有问题的伙伴直接把问题打在讨论区即可,最后会根据优质问题,选择三位伙伴分别获得《代码不朽》书籍一本。
静待日出:git与svn的区别有啥呢?
潘静:git和svn作为分布式与集中式的代表,他们本质上是工作模式的不同。Git本地有一套整个的代码仓库,离线也可以工作和查看log,而svn必须联网。然后二者在安全性能上也是有不同的,git是一个分布式的系统,它每个用户相当于对版本仓库的一个备份,通过hash保证了数据的完整性,防止其他人的恶意篡改。而svn容易发生单点的故障,服务器端历史数据如果被篡改的话,客户端很难去发现。
荣信:我现在用的是svn,想迁移到git,成本高么?
潘静:svn转到git的迁移成本并不高,因为git有一个git svn clone的命令,可以把整个服务器迁移,后面加上服务器的URL填上就可以迁移过来了,但是迁移过之后可能涉及到一个git学习成本的问题,和一个使用习惯的问题。
康:使用git的时候一般常用工作流有哪些?
潘静:git有几个工作流。工作流也可以说是一个分支策略。第一个有一个集中式工作流,集中式工作流比较适合刚从svn迁移到git的使用群体。因为它一般只使用一个master分支,类似于svn上的trunk,可以做一个svn向git的过渡。它的特点是只有一个master分支,每个开发人员都要在这个分支的基础上拉取一个最新的代码,然后有冲突的话,rebase之后,然后再提交。
还有的就是功能分支的工作流。它的思路就是你每个功能都要单拉出来一个分支,在这个分支上去做功能的开发,然后再提交到master。
还有一个比较好的工作流,就是git flow工作流,它适用于一个大型的项目。它会有几个分支,有两个长期分支,分别是master分支和develop分支。Master分支版本的稳定性较强,一般是供生产环境的部署使用,这个分支一般只从其他分支上合并代码,而不能从这个分支去直接提交。而develop分支是一个主开发分支,它用来集成、测试一些最新的开发成果,包含发布到下一个release的代码。
还有一些短期分支,就是开发人员本地的一个feture分支,我要做一个特性开发,我就拉取一个feture,然后整个自己本地测试完成之后,合到develop。
芒果泥:请问学习git需要安装什么客户端?
潘静:一般喜欢使用图形化界面的,建议你安装TortoiseGit、SourceTree、git cola或者是EGit,如果比较习惯使用命令的话,可以直接在git base的终端敲入命令行就行了。
星晨:fetch+rebase和pull本质区别是什么?
潘静:git pull等于git fetch加git merge,而您说的fetch+rebase它就相当于merge和rebase的区别了。
站在伦敦桥上看船:我在铺上的时候和别人冲突的,push失败了。接下来什么操作可以解决这个问题?
潘静:首先需要看您这个push失败的原因是什么,如果是您本地没有更新最新的代码而导致的仓库push不成功,那首先要pull一下,把最新代码拉到本地再修改。如果是因为您和远端的仓库修改了相同的代码行,导致了一个冲突,那就需要手动地去修改。
逄新利:git可以从某个文件夹创建分支吗?
潘静:git只可以针对整个代码仓库创建分支,它不能基于某个文件进行分支创建。
主持人:好的,感谢潘静老师分享答疑,由于时间关系,伙伴们没有回答的问题,稍后加群,老师也会在,大家可以继续提问。恭喜康、星晨、荣信三位获奖者,请联系主持人并提供联系地址,以便我们发放奖品。
本期活动由码农基地主办,WeX5移动开发云和华为联合主办。感谢老师今晚精彩的分享,感谢大家积极参与,感谢各位合作伙伴的支持!