第 01 天:认识 Git 版本控管

笔者使用 Subversion (SVN) 已经将近 10 年,从来都不觉得有任何必要转换至其他版本控管平台,直到前几年因应云端化的改变,慢慢导入 TFS 版本控管 (TFS Service),转换的过程还算顺利,只因为 SVN 与 TFS 的版本控管概念相近,都属于集中式版本控管系统。这类集中式版本控管系统,使用上简单、直觉且容易进行权限控管,说真的,在大部分的开发情境下,Subversion 或 TFS 已经相当足够,那又是甚麽契机或是需求,迫使我们一定要转换到 Git 版本控管呢?我相信,不同人採用 Git 一定有他的理由,有些人觉得好玩、有些人觉得新鲜、有些人觉得功能强大,无论如何,只要这个理由能够支持你去主动认识一个陌生技术,都是好的,本篇文章除了带大家认识 Git 版本控管机制外,也会说说我想转换到 Git 的理由。

文章目的

在软体开发领域,对原始码进行版本控管是非常重要的一件事,有别于 Subversion 或 TFVC (Team Foundation Version Control) 这类集中式版本控管系统,Git 是一套分散式版本控管系统(DVCS; Distributed Version Control System),并带来许多版本控管上的各种优势与解决传统集中式版本控管的缺失,例如支援本地操作、备份容易、功能强大且弹性的分支与合併等等。不过,由于 Git 版本控管无论在版控观念与工具使用上,都与传统集中式版控工具差异甚大,因此造成了不小的学习门槛。

虽然说本次文章的主题是「30 天精通 Git 版本控管」,不过,说实在的,还真有点言过其实了,因为 Git 博大精深,有非常多细节可以探究,如果真的要应用在工作上,学几天可以真正上手呢?每天学一点,连续学习 30 天,似乎是个合理的数字 (或太多?),如果有一个工具大家都要用,而且要立刻上手的工具,如果学 30 天都还不知道怎麽活用,那这学习门槛也太高了些。因此我想,这个系列的文章,主要还是专注于「如何在 30 天内学会 Git 版本控管,而且必须要能熟练的应用在实务开发工作上」,这才是本系列的真正目的,那些繁琐的细节,我不会特别强调,但总是有些重要的概念与细节还是不能错过,我会尝试在每一个主题中提到一部份,一有机会就会深入探讨,希望大家可以透过做中学,深刻体会 Git 版本控管的强大魅力。

转换的契机

这几个月,公司因为有个大型专案,参与开发人数超过 12 人,最后大家决议採用 Git 作为本次专案的版本控管机制,与其说我们採用了 Git 版本控管,其实真正採用的原因是「我们选择使用 GitHub 当成我们的版控平台」,原因就是 GitHub 平台实在整合得太好,完整的 Git 版控支援、议题追踪与管理、线上 Wiki 文件管理、友善的原始码审核(Code Review)介面。这些特性,都能有效协助我们在多人协同开发的过程中,减少团队沟通的问题。

刚开始接触 Git 说实在挺辛苦的,因为 Git 版本控管的观念,实在与 Subversion 差太多,没有办法很直觉的去体会其差异,就算给了你 GUI 图形化工具介面,你也不见得就会使用。你知道的,一个强大又好用的工具在你手上,「错误的使用方式」比「不会用」还可怕!说穿了,就是你必须先建立一套思维模式(Mindset),了解 Git 的运作原理,然后再上手使用 Git 相关工具 (无论是指令列工具或图形化介面工具),才是正途!

学习的方法

我在刚学习 Git 的时候,看了好几本书 (其实是挑重点看),也看了许多线上的文章与简报,甚至还看了好几部教学影片,看著看著,确实可以学会如何使用 Git 工具,我觉得并不会太过艰深。不过,Git 的指令与参数非常多,完全超出大脑能记忆的范围,除非每天使用,否则哪有可能一天到晚打指令进行版控,如果每次要使用 Git 指令都要查书的话,那这也太没效率了点,当下的我就直觉地认为,学习 Git 最终还是要回归到好用的 GUI 工具,否则这东西在团队中可能不容易推广。

再者,因为 Git 是属于「分散式版本控管」机制,当开发人数开始变多,版本库又开始变成一人一份时,在第一次进行多人分支与合併的过程时,大家都饱受煎熬,而且持续一段不短的时间。虽然公司内部有先进行技术分享,不过由于大家都是第一次学,那些 Git 的抽象概念,还没办法深植人心,只能基于 Git 的使用方法进行分享,例如工具怎麽用、有哪些常用的指令、甚麽特殊的情况下应该下甚麽指令,诸如此类的。过程中就算说出了複杂的原理,由于大家对于 Git 的认知还很模糊,不同人对 Git 版控方式的理解也不尽相同,所吸收到的知识与概念,也不一定一致。所以,虽然上完课了,大家还是需要好几天的时间不断磨合,相互讨论,互相解决问题,如果你只有一人使用 Git 的话,确实不容易感受 Git 带来的好处,也恐怕不容易坚持下去。

所以,我认为,要学好 Git 版本控管,若先知道以下几点,也许比较容易学会:

  • 先拥有 Git 基础观念,透过下指令的方式学习是最快的方式,不要跳过这一段
  • 找多一点人跟你一起学 Git 版本控管,最好能直接用在实务的开发工作上
  • 团队中最好要有几个先遣部队,可以多学一点 Git 观念,好分享给其他人,或有人卡关时,能适时提供协助
  • 了解 Git 属于「分散式版本控管」,每个人都有一份完整的储存库(Repository),所以必须经常合併档案
  • 使用 Git 的时候,分支与合併是常态,但只要有合併,就会有衝突,要学会如何解决衝突

认识 Git 版本控管

Git 的出现,来自于 Linux 之父 "Linus Torvalds" 开发 Linux kernel 的时候,因为早期的版本控制方法非常没有效率,属集中式控管,当 Linux kernel 这类複杂又庞大的专案在进行版本控管时,出现了许多问题。最早期 Linux kernel 採用 BitKeeper 进行版本控管,但后来 Linus Torvalds 基于 BitKeeper 与 Monotone 的使用经验,设计出更棒的 Git 版控系统。原先 Git 只被设计成一个低阶的版控工具,用来当做其他版控系统(SCM)的操作工具,后来才渐渐演变成一套完整的版本控制系统。

有趣的是,Linus Torvalds 改採 Git 进行版本控管初期,由于 Git 太过複杂,许多版控观念跟以往差异太大,也受到世界各地开放原始码社群的反对,但经过几年的努力与发展,操作 Git 的相关工具也越来越成熟,才渐渐平抚反对的压力,从 2013 年的市场调查看来,全世界已有 30% 的开放原始码专案改採 Git 进行版本控管,这是个非常惊人的市占率,意谓著 Git 绝对有其惊豔之处,不好好研究一番还不行呢!

讲到 Git 的架构,完全是基于 Linus Torvalds 在维护 Linux kernel 这个大型专案时得到的经验,以及他本身在档案系统优化方面的丰富经验进行设计,也因为这样,Git 包含了以下几个重要的设计:

  • 强力支援非线性开发模式 (分散式开发模式)
    • Git 拥有快速的分支与合併机制,还包括图形化的工具显示版本变更的历史路径。
    • Git 非常强调分支与合併,所以版本控管的过程中,你会不断的在执行分支与合併动作。
    • Git 的分支机制非常轻量,没有负担,每一次的分支只是某个 commit 的参考指标而已。
  • 分散式开发模型
    • 参与 Git 开发的每个人,都将拥有完整的开发历史纪录。
    • 当开发人员第一次将 Git 版本库複製(clone)下来后,完全等同于这份 Git 版本库的「完整备份」。
    • 整个版本库中所有变更过的档案与历史纪录,通通都会储存在本机储存库(local repository)。
  • 相容于现有作业系统
    • Git 版本库其实就只是一个资料夹而已,资料夹中有许多相关的设定档与各种 blob 物件档案而已。
    • Git 版本库可以用任何方式发布,所以你用 HTTP, FTP, rsync, SSH 甚至于用 Git protocol 都可以当成存取 Git 版本库的媒介,相容性极高。
  • 有效率的处理大型专案
    • 由于完整的版本库会複製(clone)一份在本机,该版本库包含完整的档案与版本变更纪录,所以针对版本控管中的各种档案操作速度,将会比直接从远端存取来的快上百倍之多。
    • 这也代表著,Git 版本控管不会因为专案越来越大、档案越来越多,而导致速度变慢。
  • 历史纪录保护
    • Git 版控的过程,每次 commit 都会产生一组 hash id 编号,而且每个版本在变化的过程都会参考到这个 hash id,只要 hash id 无法比对的上,Git 就会无法运作,所以当专案越来越大,版本库複製(clone)的越来越多份,你几乎无法窜改档案的内容或版本纪录。
    • 请记得: 每个人都有一份完整的版本库,你改了原始的那份,所有人的版本库就无法再合併回原本的版本库了,所以你几乎不可能任意窜改版本纪录。
  • 以工具集为主的设计 (Toolkit-based design)
    • Git 被设计成一个一个的工具软体(指令列工具),你可以很轻易的组合不同工具的使用,使用上非常弹性。
  • 弹性的合併策略 (Pluggable merge strategies)
    • Git 有一个拥有良好设计的「不完整合併(incomplete merge)」 机制,以及多种可以完成合併的演算法,并在最后告知使用者为何无法自动完成合併,或通知你需要手动进行合併动作。
  • 被动的垃圾回收机制
    • 在使用 Git 的时候,若想要中断目前的操作或回复上一个操作,都是可以的,你完全可以不必担心可能有其中一个指令下错,或指令执行到一半当机等问题。
    • Git 的垃圾回收机制,其实就是那些残留在档案系统中的无用档案,这个垃圾回收机制只会在这些无用的物件累积一段时间后自动执行,或你也可以自行下达指令清空它。例如: git gc --prune
  • 定期的封装物件
    • 我们在 Git 中提到的 "物件" 其实就是代表版本库中的一个档案。而在版本异动的过程中,专案中的程式码或其他档案会被更新,每次更新时,只要档案内容不一样,就会建立一个新的 "物件",这些不同内容的档案全部都会保留下来。
    • 你应该可以想像,当一个专案越来越大、版本越来越多时,这个物件会越来越多,虽然每个档案都可以各自压缩让档案变小,不过过多的档案还是会档案存取变得越来越没效率。因此 Git 的设计有个机制可以将一群老旧的 "物件" 自动封装进一个封装档(packfile)中,以改善档案存取效率。
    • 那些新增的档案还是会以单一档案的方式存在著,也代表一个 Git 版本库中的 "档案" 就是一个 Git "物件",但每隔一段时间就会需要重新封装(repacking)。
    • 照理说 Git 会自动执行重新封装等动作,但你依然可以自行下达指令执行。例如: git gc
    • 如果你要检查 Git 维护的档案系统是否完整,可以执行以下指令: git fsck

关于 Git 的分散式版控系统,我再重申几件事:

  • Git 完全不需要伺服器端的支援就可以运作版本控制,因为每个人都有一份完整的储存库副本。
  • 因为每个人都有一份完整的储存库副本,所以每次提交版本变更时,都仅提交到本地的储存库而已,因此提交速度非常快,也不用网路连线,可大幅节省开发时间。
  • 由于每个人都有一份完整的储存库副本,代表著在使用 Git 版本控管时,没有所谓的「权限控管」这件事,每个成员都能把储存库複製(clone)回来,也都可以在本地提交变更,没有任何权限可以限制。使用 Git 时,唯一能设定的权限是,你有没有权利存取上层储存库(upstream repository)或远端储存库(remote repository)的权限。
  • 如果需要跟别人交换变更后的版本,随时可以透过「合併」的方式进行,Git 拥有非常强悍的合併追踪(merge tracing)能力。
  • 要合併多人的版本,你只要有存取共用储存库(shared repository)的权限或管道即可。 例如:在同一台伺服器上可以透过资料夹权限进行共用,或透过 SSH 远端存取另一台伺服器的 Git 储存库,也可以透过 Web 伺服器等方式来共用 Git 储存库。

今日小结

今天这篇只是个大致介绍,若看不太懂 Git 的设计理念没关系,你可以用一段时间之后再回来看这篇文章,或许会有更深一层的体会。

我觉得要写「认识 Git 版本控管」比教大家怎麽用还难许多,以下我在列出一些 Git 相关链接,供大家进一步学习。

参考链接

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8