Git权威指南(txt+pdf+epub+mobi电子书下载)


发布时间:2020-09-01 07:49:38

点击下载

作者:蒋鑫

出版社:机械工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Git权威指南

Git权威指南试读:

前言

版本控制是管理数据变更的艺术,无论数据变更是来自同一个人,还是来自不同的人(一个团队)。版本控制系统不但要忠实地记录数据的每一次变更,还要能够帮助还原任何一次历史变更,以及实现团队的协同工作等。Git就是版本控制系统中的佼佼者。

我对版本控制系统的兴趣源自于我的个人知识管理实践,其核心就是撰写可维护的文档,并保存于版本控制系统中。可维护文档的格式可以是DocBook、FreeMind、reStructuredText等。我甚至还对FreeMind加以改造以便让其文档格式更适合于版本控制系统,这就是[1]我的第一个开源实践:托管于SourceForge上的FreeMind-MMX项目。文档书写格式的问题解决之后,就是文档的存储问题了。通过版本控制系统,很自然地就可以实现对文档历史版本的保存,但是如何避免因为版本控制系统瘫痪而导致数据丢失呢?Git用其崭新的分布式的版本控制设计提供了最好的解决方案。使用Git,我的知识库不再只有唯一的版本库与之对应,而是可以通过克隆操作分发到不同的磁盘或主机上,克隆的版本库之间通过推送(PUSH)和拉回(PULL)等操作进行同步,数据安全得到了极大的提升。在版本控制系统的忠实呵护下,我的知识库中关于Git的FreeMind脑图在日积月累中变得越来越翔实,越来越清晰,最终成为本书的雏形。

版本控制能决定项目的成败,甚至是公司的生死,此言不虚。我在推广开源项目管理工具和为企业提供咨询服务的过程中看到,有很多团队因为版本控制系统管理的混乱导致项目延期、修正的Bug重现、客户的问题不能在代码中定位……无论他们使用的是什么版本控制系统(开源的或是商业的)都是如此。这是因为传统的集中式版本控制系统不能有效地管理分支和进行分支间合并。集中管理的版本库只有唯一的分支命名空间,需要专人管理,从而造成分支创建的不自由;分支间的合并要么因为缺乏追踪导致重复合并、引发严重冲突,要么因为版本控制系统本身蹩脚的设计导致分支合并时效率低下和陷阱重重。Git凭借其灵活的设计让项目摆脱分支管理的梦魇。

我的公司也经历过代码管理的生死考验。因为公司的开发模式主要是基于开源软件的二次开发,所以最早在使用SVN(Subversion)做版本控制时,很自然地使用了SVN卖主分支模型来管理代码。随着增加和修改的代码越来越多,我们开发的软件与开源软件上游的偏离也越来越远,当上游有新版本发布时,最早可能只用几个小时就可以将改动迁移过去,但是如果对上游的改动多达几十甚至上百处时,迁移的过程就会异常痛苦,基本上和重新做一遍差不多。那时似乎只有一种选择:不再与上游合并,不再追踪上游的改动,而这与公司的价值观“发动全球智慧为客户创造价值”相违背。迷茫之中,分布式版本控制系统飘然而至,原来版本控制还可以这么做。

我最先尝试的分布式版本控制系统是Hg(Mercurial),当发现Hg和MQ(Hg的一个插件)这一对宝贝儿的时候,我如获至宝。逐渐地,公司的版本库都迁移到了Hg上。但随着新的开发人员的加入,问题又出现了,一个人使用Hg和MQ很好,但多个人使用时则会出现难以协同的问题。于是我们大胆地采用了Git,并在实践中结合Topgit等工具进行代码的管理。再一次,也许是最后一次,我们的代码库迁移到了Git。

最早认识分布式版本控制,源自于我们看到了众多开源项目的版本控制系统大迁移,这场迁移还在进行中。

·MoinMoin是我们关注的一个开源的维基软件,2006年,它的代[2]码库从SVN迁移到了Hg。

·Mailman同样是我们关注的一个开源邮件列表软件。2007年,它[3]的代码库从SVN迁移到了Bazaar。

·Linux采用Git作为版本控制系统(一点都不奇怪,因为Git就是Linus Torvalds开发的)。

·Android是目前最为流行的开源项目之一,因为潜在市场巨大,已经吸引了越来越多的开发者进入这个市场,而Android就是用Git维护的。

当开源软件纷纷倒向分布式版本控制系统大旗(尤其是Git)的时候,很多商业公司也在行动了,尤其是涉及异地团队协同和Android核心代码定制开发的公司。对于那些因保守而不敢向Git靠拢的公司,Git也可以派上用场,因为Git可以与现在大多数公司部署的SVN很好地协同,即公司的服务器是SVN,开发者的客户端则使用Git。相信随着Git的普及,以及公司在代码管理观念上的改进,会有更多的公司拥抱Git。本书的组织

本书共分为9篇,前8篇是正文,一共41章,第9篇是附录。

第1篇讲解了Git的相关概念,以及安装和配置的方法,共3章。第1章介绍了版本控制的历史。第2章用十几个小例子介绍了Git的一些闪亮特性,期待这些特性能够让你爱上Git。第3章则介绍了Git在三种主要操作系统平台上的安装和使用。在本书的写作过程中,我70%的时间使用的是Debian Linux操作系统,Linux用户可以毫无障碍地完成本书列举的所有实践操作。在2010年年底,当得知有出版社愿意出版这本书后,我向妻子阿巧预支了未来的部分稿费购买了我的第一台MacBook Pro,于是本书就有了较为翔实的如何在Mac OS X下安装和使用Git的内容,以及在本书第22章中介绍的关于Topgit在Mac OS X上的部署和改进相关的内容。在本书的编辑和校对过程中因为要使用Word格式的文稿,所以本书后期的很多工作是在运行于VirtualBox下的Windows虚拟机中完成的,即使是使用运行于资源受限的虚拟机中的Cygwin,Git依然完美地完成了工作。

第2篇和第3篇详细讲解了Git的使用方法,是本书的基础和核心,大约占据了全书40%的篇幅。这两篇的内容架构方式是我在进行SVN培训时就已经形成的习惯,即以“独奏”指代一个人的版本控制所要讲述的知识点,以“和声”指代团队版本控制涉及的话题。在第2篇“Git独奏”中,本书将Git的设计原理穿插在各章之中讲解,因为唯有了解真相(Git原理),才有可能自由(掌握Git)。在第3篇“Git和声”中,本书讲解了团队版本控制必须掌握的里程碑和分支等概念,以及如何解决合并中遇到的冲突。

第4篇细致地讲解了Git在实际工作中的使用模式。除了传统的集中式和分布式使用模式之外,第22章还介绍了Topgit在定制开发中的应用,这也是我公司在使用Git时采用的最主要的模式。这一章还讲解了我对Topgit所做的部分改进,相关的具体介绍最早出现在我公司[4]的博客上。第23~25章介绍了多版本库协同的不同方法,其中第25章介绍的一个独辟蹊径的解决方案是由Android项目引入的名为repo的工具实现的,我对其进行改造后可以让这个工具脱离Gerrit代码审核服务器,直接操作Git服务器。第26章介绍了git-svn这一工具,该工具不但可以实现从SVN版本库到Git版本库的迁移,还可以实现以Git作为客户端向SVN提交。

第5篇介绍了Git服务器的架设。本篇是全书最早开始撰写的部分,这是因为我给客户做的Git培训讲义的相关内容不够详细,于是应客户要求针对Gitolite等服务器的架设撰写了详细的管理员手册,即本书的第30章。第32章介绍了Android项目在Git管理上的又一大创造,即Gerrit,它实现了一个独特的集中式Git版本库管理模型。

第6篇讲解了Git版本库的迁移。其中第34章详细介绍了从CVS版本库到Git版本库的迁移,其迁移过程也可以作为从CVS到SVN迁移的借鉴。本篇还介绍了从SVN和Hg版本库到Git的迁移。对于其他类型的版本库,介绍了一个通用的需要编程来实现的方法。在本篇的最后还介绍了一个Git版本库整理的利器,可以理解为一个Git库转换为另外一个Git库的方法。

第7篇是关于Git的其他应用,其主要内容介绍了我在etckeeper启发下开发的一款备份工具Gistore,该工具可以运行于Linux和Mac OS X下。

第8篇是Git杂谈。其中第40章的内容可供跨平台的项目组借鉴。第41章介绍了一些在前面没有涉及的Git的相关功能和特性。

第9篇是附录。首先介绍了完整的Git命令索引,然后分别介绍了CVS、SVN、Hg与Git之间的比较和命令对照,对于有其他版本控制系统使用经验的用户而言,这一部分内容颇具参考价值。适用读者

本书适合所有翻开它的人,因为我知道这本书在书店里一定是放在计算机图书专柜。本书尤其适合以下几类读者阅读。1.被数据同步困扰的“电脑人”

困扰“电脑人”的一个常见问题是,有太多的数据需要长久保存,有太多的电脑设备需要数据同步。可能有的人会说:“像Dropbox一样的网盘可以帮助我呀”。是的,云存储就是在技术逐渐成熟之后应运而生的产品,但是依然解决不了如下几个问题:多个设备上同时修改造成的冲突;冗余数据传输造成的带宽瓶颈;没有实现真正的、完全的历史变更数据备份。具体请参见本书第7篇第39章的内容。

Git可以在数据同步方面做得更好,甚至只需借助小小的U盘就可以实现多台电脑的数据同步,并且支持自动的冲突解决。只要阅读本书第1篇和第2篇,就能轻易掌握相关的操作,实现数据的版本控制和同步。2.学习计算机课程的学生

我非常后悔没有在学习编程的第一天就开始使用版本控制,在学校时写的很多小程序和函数库都丢失了。直到使用了CVS和SVN对个人数据进行版本控制之后,才开始把每一天的变更历史都保留了下来。Git在这方面可以比CVS和SVN等做得更好。

在阅读完本书的前3篇掌握了Git的基础知识之后,可以阅读第5篇第33章的内容,通过Github或类似的服务提供商建立自己的版本库托管,为自己的数据找一个安全的家。3.程序员

使用Git会让程序员有更多的时间休息,因为可以更快地完成工作。分布式版本控制让每一个程序员都能在本地拥有一个完整的版本库,所以几乎所有操作都能够脱离网络执行而不受带宽的限制。加之使用了智能协议,版本库间的同步不但减少了数据传输量,还能显示完成进度。

Git帮助程序员打开了进入开源世界的大门,进而开阔视野,提升水平,增加择业的砝码。看看使用Git作为版本控制的开源软件吧:Linux kernel、Android、Debian、Fedora、GNOME、KDevelop、jQuery、Prototype、PostgreSQL、Ruby on Rails……不胜枚举。还有,不要忘了所有的SVN版本库都可以通过Git方式更好地访问。

作为一个程序员,必须具备团队协同能力,本书第3篇应该作为学习的重点。4.Android程序员

如果你是谷歌Android项目的参与者,尤其是驱动开发和核心开发的参与者,必然会接触Git、repo和Gerrit。对于只是偶尔参考一下Android核心代码的Android应用开发人员而言,也需要对repo有深入的理解,这样才不至于每次为同步代码而耗费一天的时间。

repo是Android为了解决Git多版本库管理问题而设计的工具,在本书第4篇第25章有详细介绍。

Gerrit是谷歌为了避免因分布式开发造成项目分裂而开发的工具,打造了Android独具一格的集中式管理模式,在本书第5篇第32章有详细介绍。

即使是非Android项目,也可以使用这两款工具为自己的项目服务。我还为repo写了几个新的子命令以实现脱离Gerrit提交,让repo拥有更广泛的应用领域。5.定制开发程序员

当一个公司的软件产品需要针对不同的用户进行定制开发时,就需要在一个版本库中建立大量的特性分支,使用SVN的分支管理远不如使用Git的分支管理那么自然和方便。还有一个应用领域就是对第三方代码进行维护。当使用SVN进行版本控制时,最自然的选择是卖主分支,但随着定制开发的逐渐深入,与上游的偏离也会越大,于是与上游代码的合并也将越来越令人痛苦。

第4篇第22章介绍Topgit这一杀手级的工具,这是这个领域最佳的解决方案。6.SVN用户

商业软件的研发团队因为需要精细的代码授权,所以不会轻易更换现有的SVN版本控制系统,这种情况下Git依然大有作为。无论是出差在外,或是在家办公,或是开发团队分处异地,都会遇到SVN版本控制服务器无法访问或速度较慢的情况。这时git-svn这一工具会将Git和SVN完美地结合在一起,既严格遵守SVN的授权规定,又可以自如地进行本地提交,当能够连接到SVN服务器时,可以在悠闲地喝着绿茶的同时,等待一次性批量提交的完成。

我有几个项目(pySvnManager、Freemind-MMX)托管在SourceForge的SVN服务器上,现在都是先通过git-svn将其转化为本地的Git库,然后再使用的。以这样的方式访问历史数据、比较代码或提交代码,再也不会因为网速太慢而望眼欲穿了。

本书第4篇第26章详细介绍了Git和SVN的互操作。7.管理员

Git在很大程度上减轻了管理员的负担:分支的创建和删除不再需要管理员统一管理,因为作为分布式版本控制系统,每一个克隆就是一个分支,每一个克隆都拥有独立的分支命名空间;管理员也不再需要为版本库的备份操心,因为每一个项目成员都拥有一个备份;管理员也不必担心有人在服务器上篡改版本库,因为Git版本库的每一个对象(提交和文件等)都使用SHA1哈希值进行完整性校验,任何对历史数据的篡改都会因为对后续提交产生的连锁反应而原形毕露。

本书第7篇第37章介绍了一款我开发的基于Git的备份工具,它使得Linux系统的数据备份易如反掌。本书第5篇介绍的Git服务器搭建,以及第6篇介绍的版本库迁移方面的知识会为版本控制管理员的日常维护工作提供指引。8.开发经理

作为开发经理,你一定要对代码分支有深刻的理解,不知本书第18章中的“代码管理之殇”是否能引起你的共鸣。为了能在各种情况下恰当地管理开发团队,第4篇“Git协同模型”是项目经理应该关注的重点。你的团队是否存在着跨平台开发,或者潜在着跨平台开发的可能?本书第8篇第40章也是开发经理应当关注的内容。排版约定

本书使用的排版格式约定如下:

1.命令输出及示例代码

执行一条Git命令及其输出的示例如下:$ git --versiongit version 1.7.4

2.提示符($)

命令前面的$符号代表命令提示符。

3.等宽字体(Constant width)

用于标示屏幕输出的字符、示例代码,以及正文中出现的命令、参数、文件名和函数名等。

4.等宽粗体(Constant width bold)

用于表示由用户手工输入的内容。

5.占位符(

用尖括号扩起来的内容,表示命令中或代码中的占位符,读者应当用实际值将其替换。在线资源

·官方网站:http://www.ossxp.com/doc/gotgit/

在本书的官方网站上,大家可以了解到与本书相关的最新信息,查看本书的勘误,以及下载与本书相关的资源。官网是以Git方式维护的,人人都可以参与其中。

·新浪微博:http://weibo.com/GotGit

欢迎大家通过新浪微博与作者交流,也欢迎大家通过新浪微博将你们的宝贵意见和建议反馈给作者。致谢

感谢Linus Torvalds、Junio C Hamano和Git项目的所有贡献者,是他们带给我们崭新的版本控制体验。

本书能够出版要感谢机械工业出版社华章公司,华章公司对中文原创计算机图书的信任让中国的每一个计算机从业者都有可能圆自己出书的梦想。作为一个新人,拿着一个新的选题,遇到同样充满激情的编辑,我无疑是幸运的。这个充满激情的编辑,就是华章公司的杨福川编辑。甚至没有向我索要样章,在看过目录之后就“冒险”和我签约,他的激情让我不敢懈怠。同样要感谢王晓菲编辑,她的耐心和细致让我吃惊,也正是因为她的工作本书的行文才能更加流畅,本书也才能够更快问世。还有张少波编辑,感谢她在接到我的电话后帮我分析选题并推荐给杨福川编辑。

本书的部分内容是由我的Git培训讲义扩展而来的,在此感谢朝歌数码的蒋宗贵,是他的鼓励和鞭策让我完善了本书中的与服务器架设的相关章节。还要感谢王彦宁,正是通过她的团队我才认识了Android,才有了本书关于repo和Gerrit的相关章节。

感谢群英汇的同事们,尤其要感谢王胜,正是因为我们在使用Topgit 0.7版本时遇到了严重的冲突,才使我下定决心研究Git。

感谢上海爱立信研发中心的高级技术专家蔡煜,他对全书尤其是git-svn和Gitolite相关章节做了重点评审,他的意见和建议修正了本书的很多不当之处。因为时间的关系,他的一些非常好的观点没有机会在这一版中体现,争取在改版时弥补遗憾。

中国科学院软件研究所的张先轶、比蒙科技的宋伯润和杨致伟、摩博科技的熊军、共致开源的秦红胜,以及王胜等人为本书的技术审校提供了帮助,感谢他们的宝贵意见和建议。来自中国台湾的PyLabs团队纠正了本书在对Hg的认识上的偏颇,让本书附录中的相关内容更加准确和客观,在此向他们表示感谢。

因为写书亏欠家人很多,直到最近才发现女儿小雪是多么希望拥有一台儿童自行车。感谢妻子阿巧对我的耐心和为家庭的付出。感谢岳父、岳母这几年来对小雪和我们整个家庭的照顾,让我没有后顾之忧。还要感谢我的父母和妹妹,他们对我事业的支持和鼓励是我前进的动力。在我写作本书的同时,老爸正在富春江畔代表哈尔滨电机厂监督发电机组的制造,而且也在写一本监造手册方面的书,抱歉老爸,我先完成了。:)蒋鑫(http://www.ossxp.com/)2011年4月[1] http://freemind-mmx.sourceforge.net/[2] http://moinmo.in/NewVCS[3] http://wiki.list.org/display/DEV/Home[4] http://blog.ossxp.com/第1篇 初识Git

Git是一款分布式版本控制系统,有别于CVS和SVN等集中式版本控制系统,Git可以让研发团队更加高效地协同工作,从而提高生产率。使用Git,开发人员的工作不会因为频繁地遭遇提交冲突而中断,管理人员也无须为数据的备份而担心。经过Linux这样的庞大项目的考验之后,Git被证明可以胜任任何规模的团队,即便团队成员分布于世界各地。

Git是开源社区奉献给每一个人的宝贝,用好它不仅可以实现个人的知识积累、保护好自己的数据,而且还能与他人分享自己的成果。这在其他的很多版本控制系统中是不可想象的。你会为个人的版本控制而花费高昂的费用去购买商业版本控制工具吗?你会去使用必须搭建额外的服务器才能使用的版本控制系统吗?你会把“鸡蛋”放在具有单点故障、服务器软硬件有可能崩溃的唯一的“篮子”里吗?如果你不会,那么选择Git,一定是最明智的选择。

本篇我们首先用一章的内容来回顾一下版本控制的历史,并以此向版本控制的前辈CVS和SVN致敬。第2章会通过一些典型的版本控制实例向您展示Git独特的魅力,让您爱上Git。在本篇的最后一章会介绍Git在Linux、Mac OS X及Windows下的安装和使用,这是我们进一步研究Git的基础。在这里有必要纠正一下Git的发音。一种错误是按照单个字母来发音,另外一种更为普遍的错误是把整个单词读作“技特”,实际上Git中字母G的发音与下列单词中的G类似:GOD、GIVES、GREAT、GIFT。因此Git正确的发音应该听起来像是“歌易特”。本书的英文名为《Got Git》,当面对这样的书名时您还会把Git读错吗?第1章 版本控制的前世和今生

除了茫然未知的宇宙,几乎任何事物都是从无到有,从简陋到完善。随着时间车轮的滚滚向前,历史被抛在身后逐渐远去,如同我们的现代社会,世界大同,到处都是忙碌和喧嚣,再也看不到已经远去的刀耕火种、男耕女织的慢生活岁月。

版本控制系统是一个另类。虽然其历史并不短暂,也有几十年,但是它的演进进程却一直在社会的各个角落重复着,而且惊人的相似。有的人从未使用甚至从未听说过版本控制系统,他和他的团队就像停留在黑暗的史前时代,任由数据自生自灭。有的人使用着有几十年历史的CVS或其改良版Subversion,让时间空耗在网络连接的等待中。以Git为代表的分布式版本控制系统已经风靡整个开源社区,正等待你的靠近。1.1 黑暗的史前时代

谈及远古,人们总爱以“黑暗”来形容。黑暗实际上指的是秩序和工具的匮乏,而不是自然。如果以自然环境而论,由于工业化和城市化对环境的破坏,现今才是最黑暗的年代。对于软件开发来说也是如此,在C语言一统天下的日子里我们的选择很简单,如今面临Java、.Net和脚本语言时,我们的选择变得复杂起来,但是从工具和秩序上讲,过去的年代是黑暗的。

回顾一下我经历的版本控制的“史前时代”吧。在大学里,代码分散地拷贝在各个软盘中,最终我被搞糊涂,不知道哪个软盘中的代码是最优的,因为最新并非最优,失败的重构会毁掉原来尚能运作的代码。在我工作的第一年,代码的管理并未得到改善,还是以简单的目录拷贝进行数据的备份,三四个程序员利用文件服务器的共享目录进行协同,公共类库和头文件在操作过程中相互覆盖,痛苦不堪。很明显,那时我尚不知道版本控制系统为何物。我的版本控制史前时代一直延续到2000年,那时CVS已经诞生了14年,而我在那时对CVS还一无所知。

实际上,即便是在CVS出现之前的“史前时代”,也已经有了非常好用的用于源码比较和打补丁的工具:diff和patch,它们今天生命力依然顽强。大名鼎鼎的Linus Torvalds先生(Linux之父)也对这两个工具偏爱有加,在1991~2002年之间,Linus一直顽固地使用diff、patch和tar包管理着Linux的代码,虽然不断有人提醒他有CVS的存在[1]。

那么来看看diff和patch,熟悉它们将对理解版本控制系统(差异存储)和使用版本控制系统(代码比较和冲突解决)都有莫大的好处。

1.用diff命令比较两个文本文件或目录的差异

先来构造两个文件:(注:此处是故意将“字”写成“子”,以便两个文件进行差异比较。)

对这两个文件执行diff命令,并通过输出重定向,将差异保存在diff.txt文件中。$ diff -u hello world > diff.txt

上面执行diff命令的-u参数很重要,使得差异输出中带有上下文。打开文件diff.txt,会看到其中的差异比较结果。为了说明方便,为每一行增添了行号。 1 --- hello 2010-09-21 17:45:33.551610940 +0800 2 +++ world 2010-09-21 17:44:46.343610465 +0800 3 @@ -1,4 +1,4 @@ 4 -应该杜绝文章中的错别子。 5 +应该杜绝文章中的错别字。 6 7 但是无论使用 8 * 全拼,双拼 9 @@ -6,6 +6,7 @@10 11 是人就有可能犯错,软件更是如此。12 13 -犯了错,就要扣工资!14 -15 改正的成本可能会很高。16 +17 +但是“只要眼球足够多,所有Bug都好捉”,18 +这就是开源的哲学之一。

上面的差异文件,可以这么理解:

·第1行和第2行分别记录了原始文件和目标文件的文件名及时间戳。以三个减号(---)开始的行标识的是原始文件,以三个加号(+++)开始的行标识的是目标文件。

·在比较内容中,以减号(-)开始的行是只出现在原始文件中的行,例如:第4、13、14行。

·在比较内容中,以加号(+)开始的行是只出现在目标文件中的行,例如:第5行和16-18行。

·在比较内容中,以空格开始的行,是在原始文件和目标文件中都出现的行,例如:第6-8、10-12和第15行。这些行是用作差异比较的上下文。

·第3-8行是第一个差异小节。每个差异小节以一行差异定位语句开始。第3行就是一条差异定位语句,其前后分别用两个@进行标识。

·第3行定位语句中-1,4的含义是:本差异小节的内容相当于原始文件的从第1行开始的4行。而第4、6、7、8行是原始文件中的内容,加起来刚好是4行。

·第3行定位语句中+1,4的含义是:本差异小节的内容相当于目标文件的从第1行开始的4行。而第5、6、7、8行是目标文件中的内容,加起来刚好是4行。

·因为命令diff是用于行比较的,所以即使改正了一个字,也显示为一整行的修改(参见差异文件第4、5行)。Git对diff进行了扩展,并且还提供一种逐词比较的差异比较方法,参见本书第2篇的第11.4.4小节。

·第9-18行是第二个差异小节。第9行是一条差异定位语句。

·第9行定位语句中-6,6的含义是:本差异小节的内容相当于原始文件的从第6行开始的6行。第10-15行是原始文件中的内容,加起来刚好是6行。

·第9行定位语句中+6,7的含义是:本差异小节的内容相当于目标文件的从第6行开始的7行。而第10-12、15-18行是目标文件中的内容,加起来刚好是7行。

2.命令patch相当于diff的反向操作

有了hello和diff.txt文件,可以放心地将world文件删除或用hello文件将world文件覆盖。用下面的命令可以还原world文件:$ cp hello world$ patch world < diff.txt

也可以保留world和diff.txt文件,删除hello文件或用word文件将hello文件覆盖。用下面的命令可以恢复hello文件:$ cp world hello$ patch -R hello < diff.txt

命令diff和patch还可以对目录进行比较操作,这也就是Linus在1991~2002年用于维护Linux不同版本间差异的办法。在没有版本控制系统的情况下,可以用此命令记录并保存改动前后的差异,还可以将差异文件注入版本控制系统(如果有的话)。

标准的diff和patch命令存在一个局限,就是不能对二进制文件进行处理。对二进制文件的修改或添加会在差异文件中缺失,进而丢失对二进制文件的改动或添加。Git对差异文件格式提供了扩展支持,支持二进制文件的比较,解决了这个问题。这一点可以参考本书第7篇第38章的相关内容。[1] Linus Torvalds于2007年5月3日在Google 的演讲:http://www.youtube.com/watch?v=4XpnKHJAok81.2 CVS——开启版本控制大爆发[1]

CVS(Concurrent Versions System)诞生于1985年,是由荷兰阿姆斯特丹VU大学的Dick Grune教授实现的。当时Dick Grune和两个学生共同开发一个项目,但是三个人的工作时间无法协调到一起,迫切需要一个记录和协同开发的工具软件。于是Dick Grune通过脚本语言对RCS(一个针对单独文件的版本管理工具)进行封装,设计出有史以来第一个被大规模使用的版本控制工具。Dick教授的网站上介绍[2]了CVS的这段早期历史。“在1985年的一个糟糕的秋日里,我在校汽车站等车回家,脑海里一直纠结着一件事——如何处理RCS文件、用户文件(工作区)和Entries文件的复杂关系,有的文件可能会缺失、冲突、删除,等等。我的头有些晕了,于是决定画一个大表,将复杂的关联画在其中,看看出来的结果是什么样的……”

1986年Dick通过新闻组发布了CVS,1989年Brian Berliner用C语言将CVS进行了重写。

从CVS的历史可以看出,CVS不是设计出来的,而是被实际需要“逼”出来的,因此根据“实用为上”的原则,借用了已有的针对单一文件的版本管理工具RCS。CVS采用客户端/服务器架构设计,版本库位于服务器端,实际上就是一个RCS文件容器。每一个RCS文件以“,v”作为文件名后缀,用于保存对应文件的每一次更改历史。RCS文件中只保留一个版本的完全拷贝,其他历次更改仅将差异存储其中,使得存储变得非常有效率。我在2008年设计了一个SVN管理[3]后台pySvnManager,实际上也采用了RCS作为SVN授权文件的变更记录的“数据库”。

图1-1展示了CVS版本控制系统的工作原理,可以看到作为RCS文件容器的CVS版本库和工作区目录结构的一一对应关系。

CVS这种实现方式的最大好处就是简单。把版本库中任意一个目录拿出来就可以成为另外一个版本库。如果将版本库中的一个RCS文件重命名,工作区检出的文件名也会相应地改变。这种低成本的服务器管理模式成为很多CVS粉丝至今不愿离开CVS的原因。

CVS的出现让软件工程师认识到了原来还可以这样工作。CVS成功地为后来的版本控制系统确立了标准,像提交说明(commit log)、检入(checkin)、检出(checkout)、里程碑(tag)、分支(branch)等概念在CVS中早就已经确立。CVS的命令行格式也被后来的版本控制系统竞相模仿。图1-1 CVS版本控制系统示意图

在2001年,我正为使用CVS激动不已的时候,公司领导要求采用和美国研发部门同样的版本控制解决方案。于是,我的项目组率先[4]进行了从CVS到该商业版本控制工具的迁移。虽然商业版本控制工具有更漂亮的界面及更好的产品整合性,但是就版本控制本身而言,商业版本控制工具存在着如下缺陷。

·采用黑盒子式的版本库设计。让人捉摸不透的版本库设计,最主要的目的可能就是阻止用户再迁移到其他平台。

·缺乏版本库整理工具。如果有一个文件(如记录核弹引爆密码的文件)检入到版本库中,就无法再彻底移除它。

·商业版本控制工具很难为个人提供版本控制解决方案,除非个人愿意花费高昂的许可证费用。

·商业版本控制工具注定是小众软件,新员工的培训成本不可忽视。

而上述商业版本控制系统的缺点恰恰是CVS及其他开源版本控制系统的优点。但在经历了最初的成功之后,CVS也尽显疲态:

·服务器端松散的RCS文件导致在建立里程碑或分支时效率不高,服务器端文件越多,速度越慢。

·分支和里程碑不可见,因为它们被分散地记录在服务器端的各个RCS文件中。

·合并困难重重,因为缺乏对合并的追踪,从而导致重复合并,引发严重冲突。

·缺乏对原子提交的支持,会导致客户端向服务器端提交不完整的数据。

·不能优化存储内容相同但文件名不同的文件,因为在服务器端每个文件都是单独进行差异存储的。

·不能对文件和目录的重命名进行版本控制,虽然直接在服务器端修改RCS文件名可以让改名后的文件保存历史,但是这样做实际上会破坏历史。

CVS的成功导致了版本控制系统的大爆发,各式各样的版本控制系统如雨后春笋般诞生了。新的版本控制系统或多或少地解决了CVS版本控制系统存在的问题。在这些版本控制系统中,最典型的就是Subversion(SVN)。[1] http://www.nongnu.org/cvs/[2] http://dickgrune.com/Programs/CVS.orig/[3] http://pysvnmanager.sourceforge.net[4] 于是就有了这篇文章:http://www.worldhello.net/doc/cvs_vs_starteam1.3 SVN——集中式版本控制集大成者[1]

Subversion,由于其命令行工具名为svn,因此通常被简称为SVN。SVN由CollabNet公司于2000年资助并开始开发,目的是创建一个更好用的版本控制系统以取代CVS。SVN的前期开发使用CVS做[2]版本控制,到了2001年,SVN已经可以用于自己的版本控制了。

我开始真正关注SVN是在2005年,那时SVN正经历着后端存储上的变革,即从BDB(简单的关系型数据库)到FSFS(文件数据库)的转变。相对于BDB而言,FSFS具有稳定、免维护和实现的可视性高等优点,于是我马上就被SVN吸引了。图1-2展示了SVN版本控制系统的工作原理。图1-2 SVN版本控制系统示意图

SVN的每一次提交,都会在服务器端的db/revs和db/revprops目录下各创建一个以顺序数字编号命名的文件。其中,db/revs目录下的文件(即变更集文件)记录了与上一个提交之间的差异(字母A表示新增,M表示修改,D表示删除)。在db/revprops目录下的同名文件(没有在图1-2中体现)则保存着提交日志、作者、提交时间等信息。这样设计的好处有:

·拥有全局版本号。每提交一次,SVN的版本号就会自动加一。这为SVN的使用提供了极大的便利。回想CVS时代,每个文件都拥有各自独立的版本号(RCS版本号),要想获得全局版本号,只能通过手工不断地建立里程碑来实现。

·实现了原子提交。SVN不会像CVS那样出现文件的部分内容被提交而其余的内容没有被提交的情况。

·文件名不受限制。因为服务器端不再需要建立和客户端文件相似的文件名,于是,文件的命名就不再受服务器操作系统的字符集和大小写的限制。

·文件和目录重命名也得到了支持。

SVN最具有特色的功能是轻量级拷贝,例如将目录trunk拷贝为branches/v1.x只相当于在db/revs目录中的变更集文件中用特定的语法做了一下标注,无须真正的文件拷贝。SVN使用轻量级拷贝的功能,轻松地解决了CVS存在的里程碑和分支的创建速度慢又不可见的问题,使用SVN创建里程碑和分支只在眨眼之间。

SVN在版本库授权上也有改进,不再像CVS那样依赖操作系统本身对版本库目录和文件进行授权,而是采用授权文件的方式来实现。

SVN还有一个创举,就是在工作区跟踪目录下(.svn目录)为当前目录中的每一个文件都保存一份冗余的原始拷贝。这样做的好处是部分命令不再需要网络连接,例如文件修改的差异比较,以及错误更改的回退等。

正是由于这些闪亮的功能特性,才使得SVN在CVS之后诞生的诸多版本控制系统中脱颖而出,成为开源社区一时的新宠,也成为当时各个企业进行版本控制的最佳选择之一。

但是,相对于CVS,SVN在本质上并没有突破,都属于集中式版本控制系统。即一个项目只有唯一的一个版本库与之对应,所有的项目成员都通过网络向该服务器进行提交。这样的设计除了容易出现单点故障以外,在查看日志和提交数据等操作时的延迟,会让基于广域网协同工作的团队抓狂。

除了集中式版本控制系统固有的问题外,SVN的里程碑和分支的设计也被证明是一个错误,虽然这个错误的设计使得SVN拥有了快速创建里程碑和分支的能力,但是这个错误的设计也导致了如下的更多问题。

·项目文件在版本库中必须按照一定的目录结构进行部署,否则就可能无法建立里程碑和分支。

我在项目咨询过程中见过很多团队,直接在版本库的根目录下创建项目文件。这样的版本库布局,在需要创建里程碑和分支时就无从下手了,因为根目录是不能拷贝到子目录中的。所以SVN的用户在创建版本库时必须遵守一个古怪的约定:先创建三个顶级目录/trunk、/tags和/branches。

·创建里程碑和分支会破坏精心设计的授权。

SVN的授权是基于目录的,分支和里程碑也被视为目录(和其他目录没有分别)。因此每次创建分支或里程碑时,就要将针对/trunk目录及其子目录的授权在新建的分支或里程碑上重建。随着分支和里程碑数量的增多,授权愈加复杂,维护也愈加困难。

·分支太随意从而导致混乱。SVN的分支创建非常随意:可以基于/trunk目录创建分支,也可以基于其他任何目录创建分支,因此SVN很难画出一个有意义的分支图。再加上一次提交可以同时包含针对不同分支的文件变更,使得事情变得更糟。

·虽然SVN在1.5版之后拥有了合并追踪功能,但这个功能会因为混乱的分支管理而被抵消。

2009年年底,SVN由CollabNet公司交由Apache社区管理,至此[3]SVN成为了Apache组织的一个子项目。这对SVN到底意味着什么?是开发的停滞?还是新的开始?结果如何我们将拭目以待。[1] http://subversion.apache.org/[2] http://svnbook.red-bean.com/en/1.5/svn.intro.whatis.html#svn.intro.history[3] http://en.wikipedia.org/wiki/Apache_Subversion1.4 Git——Linus的第二个伟大作品

Linux之父Linus是坚定的CVS反对者,他也同样地反对SVN。这就是为什么在1991-2002这十余年间,Linus宁可以手工修补文件的方式维护代码,也迟迟不愿使用CVS的原因。我想在当时要想劝说Linus使用CVS只有一个办法:把CVS服务器请进Linus的卧室,并对外配以千兆带宽。

2002年至2005年,Linus顶着开源社区精英们口诛笔伐的压力,选择了一个商业版本控制系统BitKeeper作为Linux内核的代码管理工[1]具。BitKeeper不同于CVS和SVN等集中式版本控制工具,而是一款分布式版本控制工具。

分布式版本控制系统最大的反传统之处在于,可以不需要集中式的版本库,每个人都工作在通过克隆建立的本地版本库中。也就是说每个人都拥有一个完整的版本库,查看提交日志、提交、创建里程碑和分支、合并分支、回退等所有操作都直接在本地完成而不需要网络连接。每个人都是本地版本库的主人,不再有谁能提交谁不能提交的限制,加上多样的协同工作模型(版本库间推送、拉回,以及补丁文件传送等)让开源项目的参与度有爆发式增长。

2005年发生的一件事最终导致了Git的诞生。在2005年4月,Andrew Tridgell(即大名鼎鼎的Samba的作者)试图对BitKeeper进行反向工程,以开发一个能与BitKeeper交互的开源工具。这激怒了BitKeeper软件的所有者BitMover公司,要求收回对Linux社区免费使[2]用BitKeeper的授权。迫不得已,Linus选择了自己开发一个分布式[3]版本控制工具以替代BitKeeper。以下是Git诞生过程中的大事记:

·2005年4月3日,开始开发Git。

·2005年4月6日,项目发布。

·2005年4月7日,Git就可以作为自身的版本控制工具了。

·2005年4月18日,发生第一个多分支合并。

·2005年4月29日,Git的性能就已经达到了Linus的预期。

·2005年6月16日,Linux内核2.6.12发布,那时Git已经在维护Linux核心的源代码了。

Linus以一个文件系统专家和内核设计者的视角对Git进行了设计,其独特的设计让Git拥有非凡的性能和最为优化的存储能力。完成原型设计后,在2005年7月26日,Linus功成身退,将Git的维护交[4]给另外一个Git的主要贡献者Junio C Hamano,直到现在。

最初的Git除了一些核心命令以外,其他的都用脚本语言开发,而且每个功能都作为一条独立的命令,例如克隆操作用git-clone,提交操作用git-commit。这导致Git拥有庞大的命令集,使用习惯也和其他版本控制系统格格不入。随着Git的开发者和使用者的增加,Git也在逐渐演变,例如到1.5.4版本时,将一百多个独立的命令封装为一个git命令,使它看起来更像是一个独立的工具,也使Git更贴近于普通用户的使用习惯。

经过短短几年的发展,众多的开源项目都纷纷从SVN或其他版本控制系统迁移到Git。虽然版本控制系统的迁移过程是痛苦的,但是因为迁移到Git会带来开发效率的极大提升,以及巨大的效益,所以很快就会忘记迁移的痛苦过程,而且很快就会适应新的工作模式。在Git的官方网站上列出了几个使用Git的重量级项目,每一个都是人们耳熟能详的,除了Git和Linux内核外,还有Perl、Eclipse、Gnome、KDE、Qt、Ruby on Rails、Android、PostgreSQL、Debian、X.org,当然还有GitHub的上百万个项目。

Git虽然是在Linux下开发的,但现在已经可以跨平台运行在所有主流的操作系统上,包括Linux、Mac OS X和Windows等。可以说每一个使用计算机的用户都可以分享Git带来的便利和快乐。[1] http://en.wikipedia.org/wiki/BitKeeper[2] http://en.wikipedia.org/wiki/Andrew_Tridgell[3] http://en.wikipedia.org/wiki/Git_%28software%29[4] http://marc.info/?l=git&m=112243466603239第2章 爱上Git的理由

本章将通过一些典型应用展示Git作为版本控制系统的独特用法,不熟悉版本控制系统的读者可以通过这些示例对版本控制拥有感性的认识。如果是有经验的读者,示例中Git和SVN的对照可以让您体会到Git的神奇和强大。本章将列举Git的一些闪亮特性,期待能够让您爱上Git。2.1 每日工作备份

当我开始撰写本书时才明白写书真的是一个辛苦活。如何让辛苦的工作不会因为笔记本硬盘的意外损坏而丢失?如何防范灾害而不让一个篮子里的鸡蛋都毁于一旦?下面就介绍一下我在写本书时是如何使用Git进行文稿备份的,请看图2-1。图2-1 利用Git做数据的备份

如图2-1所示,我的笔记本在公司局域网里的IP地址是192.168.0.100,公司的Git服务器的IP地址是192.168.0.2。公司使用动态IP上网因而没有固定的外网IP,但是公司在数据中心有托管服务器,拥有固定的IP地址,其中一台服务器用作Git服务器镜像。

我的写书习惯大概是这样:在写完一个小节或是画完一张图后,我会执行下面的命令提交一次。每一天平均提交3-5次。提交是在本地完成的,因此在图中没有表示出来。$ git add -u # 如果创建了新文件,可以执行 git add -i 命令。$ git commit

下班后,我会执行一次推送操作,将我在本地Git版本库中的提交同步到公司的Git服务器上。相当于图2-1中的步骤①。$ git push

因为公司的Git服务器和异地数据中心的Git服务器建立了镜像,所以每当我向公司内网服务器推送的时候,就会自动触发从内网服务器到外网Git服务器的镜像操作。这相当于图2-1中的步骤②,步骤②是自动执行的,无须人工干预。图2-1中标记为mirror的版本库就是Git镜像版本库,该版本库只向用户提供只读访问服务,而不能对其进行写操作(推送)。

从图2-1中可以看出,我的每日工作保存有三个拷贝,一个在笔记本中,一个在公司内网的服务器上,还有一个在外网的镜像版本库中。鸡蛋分别装在了三个篮子里。

关于如何架设可以实时镜像的Git服务器,会在本书第5篇第30章中详细介绍。2.2 异地协同工作

为了能够加快写书的进度,熬夜是必须的,这就出现了在公司和家里两地工作同步的问题。图2-2用于说明我是如何解决两地工作同步问题的。图2-2 利用Git实现异地工作协同

我在家里的电脑IP地址是10.0.0.100(家里也有一个小局域网)。如果在家里有时间工作的话,首先要做的就是图2-2中步骤③的操作:将mirror版本库中的数据同步到本地。只需要一条命令就好了:$ git pull mirror master

然后在家里的电脑上继续编写书稿并提交。当准备完成一天的工作后,就执行下面的命令,相当于图2-2中步骤④的操作:将在家中的提交推送到标记为home的版本库中。$ git push home

为什么还要再引入另外一个名为home的版本库呢?使用mirror版本库不好么?不要忘了mirror版本库只是一个镜像库,不能提供写操作。

当一早到公司,开始动笔写书之前,先要执行图2-2中步骤⑤的操作,从home版本库将家里做的提交同步到公司的电脑中。$ git pull home master

公司的小崔是我这本书的忠实读者,每当有新章节完成,他都会执行图2-2中步骤⑥的工作,从公司内网服务器获取我最新的文稿。$ git pull

一旦发现文字错误,小崔会直接在文稿中修改,然后推送到公司的服务器上(图2-2中步骤⑦)。当然他的这个推送也会自动同步到外网的mirror版本库。$ git push

而我只要执行git pull操作就可以获得小崔对文稿的修订(图2-2中的步骤⑧)。采用这种工作方式,文稿竟然分布在5台电脑上拥有6个拷贝,真可谓狡兔三窟。不,比狡兔还要多三窟。

在本节中,出现在Git命令中的mirror和home是和工作区关联的远程版本库。关于如何注册和使用远程版本库,请参见本书第3篇第19章中的内容。2.3 现场版本控制

所谓现场版本控制,就是在客户现场或在产品部署的现场进行源代码的修改,并在修改过程中进行版本控制,以便在完成修改后能够将修改结果甚至修改过程一并带走,并能够将修改结果合并至项目对应的代码库中。

1.SVN的解决方案

如果使用SVN进行版本控制,首先要将服务器上部署的产品代码目录变成SVN工作区,这个过程不仅不简单,而且会显得很繁琐,最后将改动结果导出也非常不方便,具体操作过程如下。(1)在其他位置建立一个SVN版本库。$ svnadmin create /path/to/repos/project1(2)在需要版本控制的目录下检出刚刚建立的空版本库。$ svn checkout file:///path/to/repos/project1 .(3)执行添加文件操作,然后执行提交操作。这个提交将是版本库中编号为1的提交。$ svn add *$ svn ci -m "initialized"(4)开始在工作区中修改文件并提交。$ svn ci(5)如果对修改结果满意,可以通过创建补丁文件的方式将工作成果保存并带走。但是SVN很难针对每次提交逐一创建补丁,一般用下面的命令与最早的提交进行比较,以创建出一个大补丁文件。$ svn diff -r1 > hacks.patch

上面用SVN将工作成果导出的过程存在一个致命的缺陷,就是SVN的补丁文件不支持二进制文件,因此采用补丁文件的方式有可能丢失数据,如新增或修改的图形文件会丢失。更为稳妥但也更为复杂的方式可能要用到svnadmin命令将版本库导出。命令如下:$ svnadmin dump --incremental -r2:HEAD \ /path/to/repos/project1/ > hacks.dump

将svnadmin命令创建的导出文件恢复到版本库中也非常具有挑战性,这里就不再详细说明了。还是来看看Git在这种情况下的表现吧。

2.Git的解决方案

与SVN将产品部署目录转化为SVN的工作区相比,Git要更为简单,而且使用Git导出提交历史也更为简单和实用,具体操作过程如下。(1)创建现场版本库。直接在需要版本控制的目录下执行Git版本库初始化命令。$ git init(2)添加文件并提交。$ git add -A$ git commit -m "initialized"(3)为初始提交建立一个里程碑:“v1”。$ git tag v1(4)开始在工作区中工作——修改文件并提交。$ git commit -a(5)当对修改结果满意并想将工作成果保存带走时,可以通过下面的命令将从v1开始的历次提交逐一导出为补丁文件。转换的补丁文件都包含一个数字前缀,并提取提交日志信息作为文件名,而且补丁文件还提供对二进制文件的支持。下面命令的输出摘自本书第3篇第20章中的实例。$ git format-patch v1..HEAD0001-Fix-typo-help-to-help.patch0002-Add-I18N-support.patch0003-Translate-for-Chinese.patch(6)通过邮件将补丁文件发出。当然,也可以通过其他方式将补丁文件带走。$ git send-email *.patch

Git创建的补丁文件使用了Git扩展格式,因此在导入时为了避免数据遗漏,要使用Git提供的命令而不能使用GNU的patch命令。即使要导入的不是Git版本库,也可以使用Git命令,具体操作请参见本书第7篇第38章中的相关内容。2.4 避免引入辅助目录

很多版本控制系统都要在工作区中引入辅助目录或文件,如SVN要在工作区的每一个子目录下都创建.svn目录,CVS要在工作区的每一个子目录下都创建CVS目录。

这些辅助目录如果出现在服务器上,尤其是Web服务器上是非常危险的,因为这些辅助目录下的Entries文件会暴露出目录下的文件列表,让管理员精心配置的禁止目录浏览的努力全部白费。

还有,SVN的.svn辅助目录下还存在文件的原始拷贝,在文件搜索时结果会加倍。如果你曾经在SVN的工作区用过grep命令进行内容查找,就会明白我指的是什么。

Git没有这个问题,不会在子目录下引入讨厌的辅助目录或文件(.gitignore和.gitattributes文件不算)。当然,Git还是要在工作区的顶级目录下创建名为.git的目录(版本库目录),不过,如果你认为唯一的一个.git目录也过于碍眼,可以将其放到工作区之外的任意目录。一旦这么做了,在执行Git命令时,就要通过命令行(--git-dir)或环境变量GIT_DIR为工作区指定版本库目录,甚至还要指定工作区目录。

Git还专门提供了一个git grep命令,这样在工作区根目录下执行查找时,目录.git也不会对搜索造成影响。

关于辅助目录的详细讨论请参见本书第2篇第4.2节中的内容。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载