首先,这篇分享不是git命令操作大全,不是某代码托管服务的硬广, 只是希望激发仍然在使用中世纪时期版本管理系统的同学们,能够放弃你手里的SVN,转向更先进的思路。
所以,大家不会看到非常多的Command Line 教你Step By Step Git init :) , 请放心像读故事一样,慢慢理解为什么要拥抱Git,以及玩转Git你需要做什么?
版本管理的发展历史
为什么Git能如此的火爆
不要用SVN去思考Git
玩转Git与协作
学习Git的一些参考资料
什么是版本管理, 当你在儿童时期玩游戏的时候,在打Boss之前的存档, 就是最原始版本管理的目的。
那么我们先来看看版本管理的历史, 我想引用coolshell.cn里的一些内容,先来看从网上找到的一张版本管理时代变迁图 :
这张图上分成了四个时期:
史前时期:1982年的RCS。现在你可能还能在Unix的发布包中找到它。
古典时期:1990年的CVS,1985年的PVCS,1992年的clearcase(价格贵,功能复杂,当然,今天还有人在用),微软的VSS(Welcome to Hell),90年代中期的Perforce(P4,这个工具今天都还在被广泛地使用,尤其是那些中等大小却有着大量开发团队的公司,现在是Google内部最大的代码管理器)。
中世纪时期:SVN,AccuRev(强力支持branch和merge,其扮演了一个很重要角色帮助社区脱离clearcase和CVS),
文艺复兴时期:BitKeeper——Sun的内部管理工具,Linux的内核代码2002年也用这个工具,其实,很多开源工程都在用这个工具,2005年这个工具的东家BitMover对大家对BitKeeper逆向工程很不满,于是停止支持开源,于是出现了Git。
Git 诞生于一个极富纷争大举创新的年代。
可以看到同期出现了其他不少创新的版本管理系统:
Mercurial (Hg) 第一次出现在2005年4月,也是因为BitKeeper不免费了。Hg可以和Git在一起使用,见: HG_GIT 。但是Hg和Git在设计上不一样,他们对提交/变更的概念是一样的,只不过Git用tree来实现,而Hg则是用扁平的文件和目录来实现(revlog)
Darcs (Darcs Advanced Revision Control System)是另一个让你摆脱Subversion和CVS的工具,2002年开始,今年是2.5版。它的优势是性能,以及他与众不同的历史版本管理——管理patches而不是snapshot(提交/修改),当然,这样一来,历史改变看上去很不好懂。
Bazaar (bzr) 是另一个开源的 DVCS,它试图给SCM的世界里带来一些新的东西。其由Canonical开发(Ubuntu的那个公司),在2008年成为GNU。
Plastic在2006年出现,强力地支持branch和merge,其还提供了强大的图示,包括3D的版本树,Plastic主要是为了让中等开发团队使用,介于大型的团队(ClearCase)和小型的团队(Subversion)之间。
Team Foundation Server (TFS),微软的新一代SCM工具,主要是为了VSS的失败负责,但是他还不是版本管理上还是很强,只不过,他集成了一大堆各种各样的工具,比如:issue tracking,test management等。
我认为:首先,Git 很牛逼, 然后,基于Git的产品很牛逼 。
Git 产生的背景相信大家或多或少都听过,Linux 内核开源项目在全世界有无数人在参与,因为人太多了,所以他们在1991~2002年间绝大多数的维护工作都花在了提交补丁和保存归档的繁琐事务上。 到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。
到了 2005 年,开发 BitKeeper 的公司和 Linux 内核开源社区解除了合作关系,不免费给他们用了。 基于和BitKeeper的残酷经验, 逼迫他们必须自己开发一个版本管理系统, 于是按照下面几个要求开始:
Git的第一个版本是Linux之父Linus Torvalds亲手操刀设计和实现的(据说只用了一个周末),Linus不仅仅给出一个原始设计(简单的、干净的、天才的),同时,他也用自己那独一无二的风格催生了这个项目(请参看: http://codicesoftware.blogspot.com/2007/05/linus-torvalds-on-git-and-scm.html 还是被墙)。
在Linus介绍Git的著名的演讲中,他强烈地批评(应该算是侮辱)了CVS,SVN,和Perforce:“Subversion是史上最毫无意义的项目,从项目开始就是这样了”,“如果你喜欢CVS,那么你现在应该在某个精神病研究中心或是别的地方”,“别在用Preforce了,它是十分糟糕和可悲的,这绝对绝对是真的”。无论是反对还是喜欢,Linus的确是改变了历史——中世纪已经过去了,现在的世界由分布式系统主宰,以及消除branch和merge的恐惧。
Git 基于 DAG 结构 (Directed Acyclic Graph),其运行起来相当的快。在Git发布后的来年,世界上所有的大型的开源项目全部从Subversion迁移到了Git上。
为什么说基于Git的产品很牛逼, 因为Github真是很大,从Linux内核迁移到Github 上开始,经历了七八年到现在,已经成为全世界公认的开源代码,开源社区的地方。 无数程序员们都是先知道Github, 才知道Git。所以,学习Git, 网络上无数的命令大全,上手指南,高阶学习的帖子无处不在。
所以,Git可能并不是最简单的,但它已经是现在的主流.
说完上面那么多的历史,我不想再费力气说明为什么Git 优与SVN了, 什么因为是C写的所以运行更快我,因为是分布式的比中心化得更方便, 元数据方式存储比文件存储更优等等, 因为SVN,已经是上一个时代的产物了。
那么, 这个章节想要说明的是,不要用SVN的思想和操作方式 去理解Git 。
首先, Git 到底是什么? 很多人说,git是分布式,git本质是一个 key/value 的文件管理系统,git是对blob二进制数据, tree最好的解释。
我个人最认可对git的理解是: git is a directory content, management system, tree history, storage system , stupid content tracker and a toolkit
Git 和 SVN 思想最大的差别有四个:
去中心却集中
直接记录快照,而非差异
不一样的分支概念
三个工作区,三个文件状态
每个开发者拉取(pull)并推送(push)到origin。但除了这种集中式的推送拉取关系,每个开发者也可能会从其他的开发者处拉取代码的变更,从而形成一个子团队。例如,当与两个或更多的开发者协同工作于一个大的新特性时,在将工作代码推送到持久的origin之前,这可能很有用。在上图中,Alice和Bob,Alice和David,以及Clair和David,分别构成了子团队。
从技术上讲,这只不过意味着Alice定义了一个名为bob的Git的remote,它指向了Bob的软件仓库。反之亦然。
Git 第二个思想也是和SVN最大的差异化,即每一个版本都是直接记录快照,而非文件的差异。 下面两个对比图在网上是广为流传大家应该熟悉:
SVN :
!
因为不是记录文件差异,Git的分支本质是一个指向提交快照的指针,是从某个提交快照往回看的历史。
当创建/切换分支的时候,只是变换了指针指向而已,所以会发现创建分支非常的快,同样当git init 之后就会有一个默认的分支, 我们通常叫master ,但这实际上不是主干,没有所谓的主干和分支的差别, master只是命名为master的一个分支, 是我们为了版本管理的习惯叫法而已。 ,
SVN创建一个分支, 是的的确确的复制了一份文件, 而如刚才说SVN的每一次Version 是记录了文件差异, 因此假入你commit了一个1M的文件到SVN服务器, SVN Server的存储增加了1M,当你删除了这个1M大小的文件并Commit的时候,SVN Server的存储又增加了1M。 所以,你是不会想知道像BAT这三家公司内部的SVN Server今天占用了多大存储空间的-。-
在 Git 中,存在三个工作区域:
同时,文件有三种状态:
下图说明了这三个区域和文件状态的差别:
Git 常用的命令这里不解释了, 但是我们都是在公司内的程序员,即便是个人开发者也会有在社区的伙伴们,所以代码管理一定要有协作的部分, 你一定不是一个人在战斗。
因此我个人的理解,什么是一个好的代码库管理: 库不大, 分支少,tag多, 分支或CI log 和Issue关联,分支开发master发布, 小步迭代, 版本树清晰漂亮,功能分支永远和Master离的很近, 所有开发熟悉rebase和遵循Merge request习惯
话有点糙,实际上就是敏捷开发中的一部分概念, 即便一定要拉出功能分支,也一定要快速合并,否则不如变成两个代码库,两个应用。
所以玩转git协作, merge
, rebase
和 cherry-pick
是你一定要理解的三个概念。
首先, Merge , 合并代码,人人都能理解, 在Git中存在 fast forward 和no fast fast forward branch merging , 并没有好坏之分, 差别在于,fast forward 实际上不会产生代码合并, 比如你改了a文件,我改了b文件,这两个分支Commit的文件不存在任何的合并, 所以可以进行ff merge,从而使版本树是一条线; 而产生了merge 之后的版本树会有多一个merge节点产生, 版本树是两条线合并到一条线。
Rebase 和 Cherry-pick 这里要多讲两句了 ~
A-B-C-D / E-F-G
git cherry-pick <commit id of F>
, 就会把分支上的 F
拿过来,变成下面的效果: A-B-C-D-F‘ / E-F-G
cherry-pick可以自动生成commit,这时候本分支上会有一个新的commit id,但是提交内容 F’=F。你再本分支上看到的log将是赏心悦目的直线。
如果cherry-pick的是一个分支呢?同样的例子, git cherry-pick <branch name>
会将另一分支上的每个提交按顺序加到本地,如果有冲突会提醒,解决冲突后提交即可。
A-B-C-D A-B-C-D-E-F-G / ==> / E-F-G E-F-G
A-B-C-D-H A-B-C-D-H / ==> / E-F-G E-F-G
因此:
cherry-pick
和 rebase
的区别是,一个是把别人的拿来,别人的提交会在HEAD上加入。rebase是把自己的根本切换,别人的提交会在自己的尾部。
cherry-pick
和 rebase
的好处是log简洁,是一条直线。适用于自己本地的分支管理,让自己的提交尽量是一条直线。
merge
会产生一次提交,适用场景是多人合作的场景,不同或者同一分支开发时。会产生有交点的多条线, 除非是ff的merge。
推荐一堆没用, 其实学习Git, 就两本书足以成为使用上面的专家。 Pro Git 简体中文版 ,这个上手可以。 还有一本 《Git权威指南
》 这个可以作为工具书了,因为里面讲的比较深,讲到了Git 一些底层命令(类似 git rev-parse
, git ls-tree
这种), 如果你是SCM 或者公司内的版本管理者,是需要有这本书的。
另外如果你是linux 或者 mac 用户, 命令行将是Git 最好的伙伴。 如果是Windows ,也建议直接用 Git bash 来进行操作。
不过,如果为了使用更加的方便, 建议装 SourceTree 或者 Github 客户端 这两个客户端工具。 不要装小乌龟或者别的工具了, 太难用。
当然 , Git 和SVN的思想差异, Git 中需要学习的地方很多, 上面提到的都只是我个人认为比较重要的点而已。 希望这篇文章能让大家从SVN等其他版本管理工具中迁移到Git上来。
之后针对代码管理, 我们还会推出 《如何从SVN迁移到GIT》
,会包含如何从一个svn repo Step By Step 迁移到Git repo 的命令教程。
以及 《玩转云Code最佳实践》
,会讲在 https://code.aliyun.com 上进行代码协作的最佳实践 。
以及 《云上持续交付最佳实践》
, 会包含如何使用 https://crp.aliyun.com 及阿里云Docker 容器服务 如何进行持续集成,到持续交付的实践教程, 敬请期待。