蒋 鑫
该用户没有分享资料
由蒋 鑫撰写的日志
《GotGit》附录D Git 和 Hg 面对面
3月10日
附录D Git 和 Hg 面对面
D.1 面对面访谈录
Git:你好 Hg,我发现我们真的很像。
Hg:是啊,人们把我们都归类为分布式版本控制工具,所以我们之间的相似度,要比和 CVS、SVN 的相似度高得多了。
Hg:我是用 Python 和少部分的 C 语言实现的,你呢?
Git:我的核心当然是使用 C 语言了,因为 Linus Torvalds 最爱用 C 语言了。我的很多命令还使用了 Shell 脚本和 Perl 语言开发,Python 用的很少。
Hg:大量的使用 C 语言,是你的性能比我高的原因么?
Git:当然不是了,你不也在核心模块使用 C 语言了么?问题的关键在于我的对象库设计的非常优秀。你不要忘了我是谁发明的,可是大名鼎鼎的 Linux 之父 Linus Tolvars,他对 Linux 文件系统可是再熟悉不过的了,所以他能够以文件系统开发者的视角实现我的核心。
Git:还有我的网络传输过程非常直观,可以显示实时的进度,好像我从你那里没有看到。之所以我能够有这样的实现,是因为我使用了“智能协议”。在网络传输的两端都启用了相应的辅助程序,实现差异传输及传输进度的计算和显示。
Hg:是啊,非常的惭愧。当克隆一个比较大的 Hg 版本库时,会出现假死状态,用户不知道克隆操作的进展。(感谢来自台湾的 Willie Wu,指出我的错误)
Hg:实际上我也支持进度显示,不过是通过Progress插件实现的,需要通过修改配置文件启用该插件。
Hg:我有一个特点是 SVN 用户非常喜欢的,就是我的顺序数字版本号。
Git:你的顺序数字版本号只在本地版本库中有效。也就是说,你不能像 SVN 那样将顺序数字版本号作为项目本身的版本号,因为换成另外一个版本库的克隆,那个数字版本号就会不一样了。
Hg:我觉得你的暂存区(stage)的概念太古怪了。我提交的时候,改动的文件会直接提交而不需要什么注册到暂存区的操作。
Git:让读者来作评判吧。如果读者读过本书的第2篇,一定会说 Git 的暂存区帅呆了。
Hg:我只允许用户对最近的一次提交进行回滚撤销,而你(Git)怎么能允许用户撤销任意多次历史提交呢?那样安全么?
Git:这就是的我对象库和引用设计的强大之处,我可以使用 git reset 命令将工作分支进行任意的重置,丢弃任意多的历史。至于安全性,我的重置命令有一个保险,就是 reflog ,我随时可以参照 reflog 的记录来弥补错误的重置。
Hg:我们的 revert 命令好像不同?
Git:你 Hg 的 hg revert 命令和 SVN 的 svn revert 命令相似,是取消本地修改,用原始拷贝覆盖。你的这个操作在我这里是用 git checkout 命令实现的。我也有一个 git revert 命令,但是这个命令是针对某个历史提交进行反向操作,以取消该历史提交的改动的。
Hg:我执行日志查看能够看到文本显示的分支图,你呢?
Git:我需要在日志显示时添加参数,即用命令 git log –graph 。我支持通过建立别名实现简洁的调用,例如建立一个名为 glog 的别名。
Git:我听说你 Hg 不支持分支?
Hg:坦白的说,是的。我虽然也有分支命令,但是分支不是一个独立显示的提交空间,而是各个分支都显示在一起,相当于在版本库中同时拥有多个头,选择哪个分支就相当于把帽子戴在哪个头上面而已。所以我尽量要求我的用户使用克隆来当做分支。(感谢来自台湾的 Willie Wu,指出我的错误)
Hg:你说的是昨天的我,现在有了Bookmarks插件 ,我也拥有和你类似的分支实现。不过传统来讲我还是以克隆来实现分支的。
Git:实际上我的每一个克隆的版本库也相当于独立的分支,但是因为我有强大的分支功能,因此很多用户还没有意识到。使用 Topgit 的用户就应该使用版本库克隆作为 Topgit 本身的分支管理。
Git:还有,因为我对分支的完整支持,使得我可以和 SVN 很好的协同工作。我可以将整个 SVN 转换为本地的 Git 库,但是你 Hg,显然只能每次转换一个分支。
Hg:是的,我要向你多学习。
D.2 Hg 和 Git 命令对照
《GotGit》附录C Git 和 SVN 面对面
3月10日
附录C Git 和 SVN 面对面
C.1 面对面访谈录
Git:我的提交历史本身就是一幅美丽的图画 —— DAG(Directed Acylic Graph, 有向非环图),可以看到各个分支之间的合并关系。而你 SVN,你的提交历史怎么是一条直线呢?要是在重症监护室看到你,还以为你挂掉了呢?
SVN:我觉得挺好,至少我每次提交会有一个全局的版本号,而且我的版本号是递增的。你的版本号不是递增的吧!
Git:你说的对,我的版本号不是一个简单递增的数字,而是一个长达40位的十六进制数字(哈希值),但是可以使用短格式,只要不 冲突。虽然我的提交编号看起来似乎是无序的,但实际上我每一个提交都记录了父提交甚至是双亲或多亲提交,因此可以很容易的从任意一个提交开始建立一条指向 历史提交的跟踪链。
SVN:是啊,我的一个提交和前一个提交有时根本没有关系,例如一个提交是发生在主线/trunk 中的,下一个提交可能就发生在/branches/1.3.x 分支中。你要知道要想画出一个像你那样的分支图,我要做多少工作么?我不容易呀。
Git:我一直很奇怪,你的分支和里程碑怎么看起来和目录一样?我的分支和里程碑名字虽然看起来像是目录,实际上和工作区的目录完全没有关系,只是对提交ID的一个记号而已。
SVN:我一开始觉得我用轻量级拷贝的方式实现分支和里程碑会很酷,也很快。但是我发现很多人在使用我的时候,直接在版本库的根目录下创建文件而不是把文件创建在/trunk 目录下,这就导致这些人无法再创建分支和里程碑了,因为是无法将根目录拷贝到子目录的呀!
Git:那么你是如何对分支合并进行跟踪的呢?我因为有了 DAG 的提交关系图,是很容易看出来分支之间的合并历史,但是你是怎么做到的呢?
SVN:我用了一点小技巧,就是通过属性(svn:mergeinfo)记录了合并的分支名和版本范围,这样再合并的时候,我会根据相关属性确定是否要合并。但是如果经常在子目录下合并,有太多的svn:mergeinfo 属性等待我检查,我会很困扰。还有我的这个功能是在 1.5 以后版本才提供的,因此老版本会破坏这个机制。
SVN:对了,我的属性能干很多事哦,我甚至可以把我的照片作为属性附加在文件上。
Git:这点我承认,你的属性非常强大。其实我也支持属性,只不过实现方式不同罢了。而且我可以通过评注的方式为任意对象(提交、文件、里程碑等)添加评注,也可以实现把照片做为评注附加在文件上,可是这个功能有什么实际用处么?
SVN:我有轻量级拷贝,而我的分支和里程碑就是通过拷贝实现的,很强大哦。
Git:我根本就不需要轻量级拷贝,因为我对文件的保存是和文件路径无关的,我只关心内容。所以相同内容的文件无论它们的文件名相差有多大,在我这里只保存一份。而你 SVN,如果用户忘了用轻量级拷贝,版本库是不是负担很重啊。
SVN:听说你不能针对目录授权,这可是我的强项,所以公司无论大小都在用我作为版本控制系统。
Git:不要说你的授权了,简直是一团糟。虽然这本书的作者为你写了一个图形化的授权管理工具 (http://www.ossxp.com/doc/pysvnmanager/user-guide/user-guide.html)会有所改善, 但是你糟糕的分支和里程碑的实现,会导致授权在新的分支和里程碑中要一一设置,工作量其大无比。虽然泛路径授权是一个解决方案,但是官方并没有提供啊。
Git:说说我的授权吧。如果你认真的读过本书服务器架设的相关章节,你会为我能够提供的按照分支,以及按照路径进行写操作授权而 击掌叫好的。当然我的读操作授权还不能做到很精细,但是可以将版本库拆分成若干小的版本库啊,再用参照本书介绍的各种多版本库协同模式,会找到一个适合的 解决方案的啊。
Git:我的工作区很干净。只在工作区的根目录下有一个 .git 目录,此外再无其他。
SVN:我要在工作区的每一个目录下都放置一个.svn 目录,这个目录在 Linux 下可是隐藏的哦。这个目录下不但有跟踪工作区文件状态的跟踪文件,而且还有每一个文件的原始拷贝呢。这样有的操作就可以脱离网络执行了,例如:差异比较,工作区文件的回滚。
Git:嗯,你要是像我一样再保存多一点内容(整个版本库)就更好了。像你这样在每个工作区子目录下都有一个 .svn 目录,而且每个 .svn 目录下都有文件原始拷贝,在进行内容搜索的时候会搜索出来两份吧,太干扰了。而且你这么做和 CVS 一样有安全风险,造成本地文件名的信息泄漏,千万不要在 Web 服务器上用 SVN 检出哦。
Git:我的操作可以不需要网络。因为我在本地拥有完整的版本库,几乎所有操作都是在本地完成。
SVN:正如前面说到的,我有部分命令可以不需要网络,但是其他绝大多数命令还是要依赖网络的。
CVS:你怎么没有更新(update)命令?还有你为什么老是要执行检出命令(checkout)?对我而言,检出命令只在工作区创建时一次完成的。
Git:你的这个问题怎么和 CVS 问的一样。你的更新(update)命令执行的很慢对么?首先你要用检出命令(checkout)建立工作区,然后你要经常的执行更新(update)命令进行更新,否则容易造成你的更改和他人更改发生冲突。
Git:之所以你需要更新是因为你的版本库在远程啊。别忘了我的版本库是在本地,我的每一步操作工作区和版本库都是同步的,所以更 新操作就没有存在的必要了。而我的检出(checkout)操作一般是用户切换分支,或者从本地版本库检出丢失的文件或覆盖本地错误改动的文件时用到。如 果我没记错的话,你切换分支用的是 svn switch 命令对么?
Git:实际上我也有一个比较耗时的网络操作命令叫做 git fetch 或 git pull ,这两个操作是从远程版本库获取他人改动。一般使用我(Git)做团队协作的时候,会部署一个集中共享的版本库,我就从这个共享的版本库执行拉回操作。也 也许你(SVN)会觉得 git fetch 或 git pull 和你的 svn update 命令更像吧。至于你的检出命令(svn checkout),实际上和我克隆命令(git clone)很相似,只不过我的克隆命令不但创建了本地工作区,而且在本地还复制了和远程版本库一样的本地版本库。
SVN:为什么你的检入命令(commit)命令执行的那么快?
Git:是的,我的检入命令飞一般就执行完了,也是因为版本库就在本地。也许你(SVN)会觉得我的推送命令(git push)和你的检入命令(svn commit)更相像,其实这是一个误会。如果我不做本地提交,是不能通过推送命令(git push)将我的本地的提交共享给(推送给)其他版本库的。你(SVN)每一次提交都要和版本库进行网络通讯,而我可以在本地版本库进行多次提交,直到我 的主人想喝咖啡了才执行一次 git push,将我本地版本库中新的提交推送给远程版本库。
SVN:我能一次检出一个目录,你好像不能吧?
Git:所以我有子模组,以及 repo 等第三方工具,可以帮助我把一个大的版本库拆开多个版本库组合来使用啊。
SVN:我能添加空目录,你好像不能吧!
Git:是的,我现在还不能记录空目录,但是用户往往在空目录下创建一个隐含文件,并将该隐含文件添加到版本库中,也就实现了空目录添加的功能。
C.2 SVN 和 Git 命令对照
《GotGit》附录B Git 与 CVS 面对面
3月10日
附录B Git 与 CVS 面对面
B.1 面对面访谈录
Git:我的提交是原子提交。每次提交都对应于一个目录树(树对象)。因为我的提交ID是对目录树及相关的提交信息建立的一个SHA1哈希值,所以可以保证数据的完整性。
CVS:我承认这是我的软肋,一次错误或冲突的提交会导致部分数据被提交,而部分数据没有提交,版本库完整性被破坏,所以人们才设计出来 Subversion(SVN) 来取代我。
Git:我的分支和里程碑管理非常快捷。因为我的分支和里程碑就是一个记录提交ID的引用,你的呢?
CVS:你怎么又提到别人的痛处了!我的分支和里程碑创建速度还是很快的,…嗯… , 如果在版本库中只有几个文件的话。当然如果版本库的文件的很多,创建分支、里程碑创建就需要花费更多的时间。有些人对此忍无可忍,于是设计出 SVN 来取代我。
Git:其实我不用里程碑都没有关系,因为每一个提交ID就对应于唯一的一个提交状态。
CVS:这也是我做不到的。我没有全局版本号的概念,每一个文件都通过单独的版本号记录其变更历史,所以人们在使用我的时候必须经常地用里程碑 (tag)对我的状态进行标识。还需要提醒一句,如果版本库中文件太多,创建里程碑是很耗时的,因为要一一打开每一个版本库中的文件,在其中记录里程碑和 文件版本的关系。
Git:我的工作区很干净。只在工作区的根目录下有一个 .git 目录,此外再无其他辅助目录或文件。
CVS:我要在工作区的每一个目录下都放置一个 CVS 目录,这个目录下有个Entries 文件很重要,记录了对应工作区文件的检出版本以及时间戳等信息。这样做的好处是可以将工作区移动到任何其他磁盘和目录,依然可以使用,甚至我可以将工作区的一个子目录拿出来,作为独立的工作区。
Git:我也可以将工作区移动到其他磁盘,但是要保证工作区下的 .git 目录和工作区一同移动。也不可以只移动工作区下的一个目录到其他磁盘或目录,那样的话移出的目录就不能工作了。
Git:我的网络传输效率很高。在和其他版本库交互时,对方会告诉我他有什么,我也知道我有什么,因为只对缺失对象的打包传输,所以效率很高而且能够显示传输进度。
CVS:这一点我不行。因为我本地没有文件做对照,所以我在传输的时候不可能做到增量传输。
Git:我甚至可以不需要网络,因为我在本地拥有完整的版本库,几乎所有操作都是在本地完成。
CVS:我的操作处处需要网络,如果版本库是在网络中其他服务器上的话。如果网速比较慢,查看日志、查看历史版本都需要花费很长时间等待。
CVS:你怎么没有更新(update)命令?还有你为什么老是要执行检出命令(checkout)?对我而言,检出命令只在工作区创建时一次完成的。
Git:你的检出命令(checkout)是从远程版本库服务器获取数据完成本地工作区的创建,版本库仍然位于远程的服务器上。你 的更新(update)命令执行的很慢对么?之所以你需要执行更新命令是因为你的版本库在远程啊。别忘了我的版本库是在本地,我的每一步操作工作区和版本 库都是同步的,所以更新操作就没有存在的必要了。而我的检出(checkout)操作是将本地版本库的数据检出到本地工作区,用于恢复本地丢失的文件或错 误改动的文件,也用于切换不同的分支。我也有一个和你的更新(update)操作类似的比较耗时的网络操作命令叫做 git fetch 或 git pull,这两个操作是从别人的版本库获取他人改动。一般使用我(Git)做团队协作的时候,会部署一个集中共享的版本库,我就用这两个命令(git fetch 或 git pull)从共享的版本库执行拉回操作。也也许你(CVS)会觉得 git fetch 或者 git pull 和你的 cvs update 命令更像吧。至于你的检出命令(cvs checkout),实际上和我克隆命令(git clone)很相似,只不过我的克隆命令不但创建了本地工作区,而且在本地还复制了和远程版本库一样的本地版本库。
CVS:为什么你的检入命令(commit)命令执行的那么快?
Git:是的,我的检入命令飞一般就执行完了,也是因为版本库就在本地。也许你(CVS)会觉得我的推送命令(git push)和你的检入命令(cvs commit)更相像,其实这是一个误会。如果我不做本地提交,是不能通过推送命令(git push)将我的本地的提交共享给(推送给)其他版本库的。你(CVS)每一次提交都要和版本库进行网络通讯,而我可以在本地版本库进行多次提交,直到我 的主人想喝咖啡了才执行一次 git push,将我本地版本库中新的提交推送给远程版本库。
CVS:我每一个文件都一个独立的版本号,你有么?
Git:每一个文件一个版本号?这有什么值得夸耀的?我听说你最早是用脚本对 RCS 系统进行封装实现的,所以你每个文件都有一个独立的版本控制,这让你变得很零碎。我听说某些商业版本控制系统也是这样,真糟糕。我的每一次提交都有一个全 球唯一的版本号,这样不但是在本地版本库中是唯一的,和其他人的版本库也不会有冲突。
CVS:我能一次检出一个目录,你好像不能吧?
Git:所以我有子模组,以及 repo 等第三方工具,可以帮助我把一个大的版本库拆开多个版本库组合来使用啊。
CVS:我能添加空目录,你好像不能吧!
Git:是的,我现在还不能记录空目录。但是用户可以在空目录下创建一个隐含文件,并将该隐含文件添加到版本库中,也就实现了空目录添加的功能。你,CVS,目录管理是你的软肋,你很难实现目录的重命名,而目录重命名对我来说是小菜一碟。
B.2 CVS 和 Git 命令对照
《GotGit》附录A Git 命令索引
3月10日
附录A. Git 命令索引
每一个 Git 子命令都和特定目录下的一个名为git-<cmd> 的文件相对应,也就是在这个特定目录下存在的名为git-<cmd> 的可执行文件(有几个脚本文件被其他脚本包含提供相应的函数库,不能单独运行。如git-sh-setup )可以用命令git <cmd> 执行。这个特定的目录的位置可以用下面的命令查看:
$ git --exec-path /usr/lib/git-core/
在这个目录下有150多个可执行文件,也就是说 Git 有非常多的子命令。在如此众多的子命令中,实际上常用的只有三分之一不到,其余的命令或者做为底层命令供其他命令及脚本调用,或者用于某些生僻场合,或者 已经过时但出于兼容性的考虑而仍然健在。下面的表格分门别类的对所有Git命令做一概要性介绍,凡是在本书出现过的命令标以章节号和页码。
《Got Git》完稿
2月24日
2011/2/22,当我把第8篇文稿发给编辑后,长长的舒了一口气,这真是一个漫长的旅程。
写一本关于Git的书的想法始于2010年9月,在给客户做了Git培训后,发现还有很多想法难以浓缩在区区几百页的PPT中,为什么不把它扩展成一本书呢?
如果当初知道写一本书需要花这么久,真不知道能不能够坚持下来。感谢家人的支持,感谢华章出版社编辑们的鼓励和辛苦的工作,感谢我的朋友们和客户为本书初稿提出宝贵的修改意见。
关于这本书的名字,我很长时间决定不下来,曾经一度把本书的名字叫做《Got Git》,因为这样我就不用太费力的校正很多人对 Git 错误的发音。不过出版社从商业角度上决定采用《Git权威指南》这个让我倍感压力的名字。不管怎样,名字只是名字,重要的还是内容本身。
把全书的目录(不包含前言和附录)摘录如下,目录中的页码来自Word稿件,正式排版后会压缩至80%。
阅读全部内容 »
回到未来 (3)
12月28日
9.3.3 时间旅行三
《回到未来-第三集》铁匠布朗博士手工打造了可以时光旅行的飞行火车,使用蒸汽作为动力。这款时间旅行火车更大,更安全,更舒适,适合一家四口外加宠物的时空旅行。与之对应本次实践也将采用“手工打造”:交互式变基。
回到未来 (2)
12月28日
9.3.2 时间旅行二
《回到未来-第二集》布朗博士改进的时间旅行车使用了未来科技,是陆天两用的飞车,而且燃料不再依赖核物质,而是使用无所不在的生活垃圾。而此次实践使用的工具也进行了升级,采用强大的git rebase 命令。

回到未来 (1)
12月28日
9.3 回到未来
电影《回到未来》(Back to future)第二集,老毕福偷走时光车,到过去(1955年)给了小毕福一本书,导致未来大变。
Git 这一台“时光机”也有这样的能力,或者说也会具有这样的行为。当更改历史提交(SHA1哈希值变更),即使后续提交的内容和属性都一致,但是因为后续提交 中有一个属性是父提交的SHA1哈希值,所以一个历史提交的改变会引起连锁变化,导致所有后续提交必然的发生变化,就会形成两条平行的时间线:一个是变更 前的提交时间线,另外一条是更改历史后新的提交时间线。
把此次实践比喻做一次电影(回到未来)拍摄的话,舞台依然是之前的DEMO版本库,而剧本是这样的。
补丁中的二进制文件
11月24日
有的时候,需要将对代码的改动以补丁文件的方式进行传递,最终合并入版本库。例如直接在软件部署目录内进行改动,再将改动传送到开发平台。或者是因为在某个开源软件的官方版本库中没有提交权限,需要将自己的改动以补丁文件的方式提供给官方。
关于补丁文件的格式,补丁的生成和应用在我们第一章 TODO 已经进行了介绍,使用的是 Gnu diff 和 patch 命令。很多版本控制系统也可以生成 GNU 兼容的 diff 文件,例如svn diff 命令。但是 GNU 的 diff 格式有一个不足:不支持二进制文件。即如果有二进制文件发生了变化或者增加了新的二进制文件,在差异文件中无法体现,这样的差异文件应用到代码树中,二进制文件丢失了!
Git 突破了传统 DIFF 格式的限制,通过引入新的 DIFF 格式,实现了对二进制文件的支持。



最新评论