如何使用CVS进行版本控制

GitHub 网站发布于 2008 年。若是你的软件工程师职业生涯跟我同样,也是晚于此时间的话,Git 多是你用过的惟一版本控制软件。虽然其陡峭的学习曲线和不直观地用户界面时常会遭人抱怨,但不能否认的是,Git 已经成为学习版本控制的每一个人的选择。cvs和Git并没有差异,今天就来介绍Git的前身-cvs

Stack Overflow 2015 年进行的开发者调查显示,69.3% 的被调查者在使用 Git,几乎是排名第二的 Subversion 版本控制系统使用者数量的两倍。2015 年以后,也许是由于 Git 太受欢迎了,你们对此话题再也不感兴趣,因此 Stack Overflow 中止了关于开发人员使用的版本控制系统的问卷调查。
GitHub 的发布时间距离 Git 自身发布时间很近。2005 年,Linus Torvalds 发布了 Git 的首个版本。如今的年经一代开发者可能很难想象“版本控制软件”一词所表明的世界并不只仅只有 Git,虽然这样的世界诞生的时间并不长。除了 Git 外,还有不少可供选择。那时,开源开发者较喜欢 Subversion,企业和视频游戏公司使用 Perforce (到现在有些仍在用),而 Linux 内核项目依赖于名为 BitKeeper 的版本控制系统。
其中一些系统,特别是 BitKeeper,会让年经一代的 Git 用户感受很熟悉,上手也很快,但大多数相差很大。除了 BitKeeper,Git 以前的版本控制系统都是以不一样的架构模型为基础运行的。《Version Control By Example》一书的做者 Eric Sink 在他的书中对版本控制进行了分类,按其说法,Git 属于第三代版本控制系统,而大多数 Git 的前身,即流行于二十世纪九零年代和二十一世纪早期的系统,都属于第二代版本控制系统。2 第三代版本控制系统是分布式的,第二代是集中式。大家之前大概都听过 Git 被描述为一款“分布式”版本控制系统。我一直都不明白分布式/集中式之间的区别,随后本身亲自安装了一款第二代的集中式版本控件系统,并作了相关实验,至少明白了一些。
我安装的版本系统是 CVS。CVS,即 “并发版本系统Concurrent Versions System” 的缩写,是最初的第二代版本控制系统。大约十年间,它是最为流行的版本控制系统,直到 2000 年被 Subversion 所取代。即使如此,Subversion 被认为是 “更好的 CVS”,这更进一步突出了 CVS 在二十世纪九零年代的主导地位。html

CVS 最先是由一位名叫 Dick Grune 的荷兰科学家在 1986 年开发的,当时有一个编译器项目,他正在寻找一种能与其学生合做的方法。3 CVS 最初仅仅只是一个包装了 RCS(修订控制系统Revision Control System) 的 Shell 脚本集合,Grune 想改进这个第一代的版本控制系统。 RCS 是按悲观锁模式工做的,这意味着两个程序员不能够同时处理同一个文件。须要编辑一个文件话,首先得向 RCS 系统请求一个排它锁,锁定此文件直到完成编辑,若是你想编辑的文件有人正在编辑,你就必须等待。CVS 在 RCS 基础上改进,并把悲观锁模型替换成乐观锁模型,迎来了第二代版本控制系统的时代。如今,程序员能够同时编辑同一个文件、合并编辑部分,随后解决合并冲突问题。(后来接管 CVS 项目的工程师 Brian Berliner 于 1990 年撰写了一篇很是易读的关于 CVS 创新的 论文。)linux

从这个意义上来说,CVS 与 Git 并没有差别,由于 Git 也是运行于乐观锁模式的,但也仅仅只有此点类似。实际上,Linus Torvalds 开发 Git 时,他的一个指导原则是 WWCVSND,即 “CVS 不能作的What Would CVS Not Do”。每当他作决策时,他都会力争选择那些在 CVS 设计里没有使用的功能选项。4 因此即便 CVS 要早于 Git 十多年,但它对 Git 的影响是反面的。git

我很是喜欢折腾 CVS。我认为要弄明白为何 Git 的分布式特性是对之前的版本控制系统的极大改善的话,除了折腾 CVS 外,没有更好的办法。所以,我邀请你跟我一块儿来一段激动人心的旅程,并在接下来的十分钟内了解下这个近十年来无人使用的软件。(能够看看文末“修正”部分)程序员

CVS 入门后端

CVS 的安装教程能够在其 项目主页 上找到。MacOS 系统的话,可使用 Homebrew 安装。安全

因为 CVS 是集中式的,因此它有客户端和服务端之区分,这种模式 Git 是没有的。两端分别有不一样的可执行文件,其区别不太明显。但要开始使用 CVS 的话,即便只在你的本地机器上使用,也必须设置 CVS 的服务后端。网络

CVS 的后端,即全部代码的中央存储区,被叫作存储库 repository。在 Git 中每个项目都有一个存储库,而 CVS 中一个存储库就包含全部的项目。尽管有办法保证一次只能访问一个项目,但一个中央存储库包含全部东西是改变不了的。架构

要在本地建立存储库的话,请运行 init 命令。你能够像以下所示在家目录建立,也能够在你本地的任何地方建立。并发

$ cvs -d ~/sandbox init

CVS 容许你将选项传递给 cvscvs 命令自己或 init 子命令。出如今cvs 命令以后的选项默认是全局的,而出如今子命令以后的是子命令特有选项。上面所示例子中,-d 标志是全局选项。在这儿是告诉 CVS 咱们想要建立存储库路径在哪里,但通常-d 标志指的是咱们想要使用的且已经存在的存储库位置。一直使用 -d 标志很单调乏味,因此能够设置 CVSROOT 环境变量来代替。分布式

由于咱们只是在本地操做,因此仅仅使用 -d 参考来传递路径就能够,但也能够包含个主机名。

此命令在你的家目录建立了一个名叫 sandbox 的目录。 若是你列出sandbox 内容,会发现下面包含有名为 CVSROOT 的目录。请不要把此目录与咱们的环境变量混淆,它保存存储库的管理文件。

恭喜! 你刚刚建立了第一个 CVS 存储库。

检入代码

假设你决定留存下本身喜欢的颜色清单。由于你是一个有艺术倾向但很健忘的人,因此你键入颜色列表清单,并保存到一个叫 favorites.txt 的文件中:

blue
orange
green
definitely not yellow

咱们也假设你把文件保存到一个叫colors 的目录中。如今你想要把喜欢的颜色列表清单置于版本控制之下,由于从如今起的五十年间你会回顾下,随着时间的推移本身的品味怎么变化,这件事颇有意思。

为此,你必须将你的目录导入为新的 CVS 项目。可使用import 命令:

$ cvs -d ~/sandbox import -m "" colors colors initial
N colors/favorites.txt
No conflicts created by this import

这里咱们再次使用-d 标志来指定存储库的位置,其他的参数是传输给import 子命令的。必需要提供一条消息,但这儿不必,因此留空。下一个参数colors ,指定了存储库中新目录的名字,这儿给的名字跟检入的目录名称一致。最后的两个参数分别指定了 “vendor” 标签和 “release” 标签。咱们稍后就会谈论标签。
咱们刚将 colors 项目拉入 CVS 存储库。将代码引入 CVS 有不少种不一样的方法,但这是 《Pragmatic Version Control Using CVS》 一书所推荐方法,这是一本关于 CVS 的程序员实用指导书籍。使用这种方法有点尴尬的就是你得从新检出check out工做项目,即便已经存在有 colors 此项目了。不要使用该目录,首先删除它,而后从 CVS 中检出刚才的版本,以下示:

$ cvs -d ~/sandbox co colors
cvs checkout: Updating colors
U colors/favorites.txt

这个过程会建立一个新的目录,也叫作 colors。此目录里会发现你的源文件 favorites.txt,还有一个叫 CVS 的目录。这个 CVS 目录基本上与每一个 Git 存储库的.git 目录等价。

作出改动

准备旅行。

和 Git 同样,CVS 也有 status 命令:

$ cvs status
cvs status: Examining .
===================================================================
File: favorites.txt     Status: Up-to-date
   Working revision:    1.1.1.1 2018-07-06 19:27:54 -0400
   Repository revision: 1.1.1.1 /Users/sinclairtarget/sandbox/colors/favorites.txt,v
   Commit Identifier:   fD7GYxt035GNg8JA
   Sticky Tag:      (none)
   Sticky Date:     (none)
   Sticky Options:  (none)

到这儿事情开始陌生起来了。CVS 没有提交对象这一律念。如上示,有一个叫 “提交标识符Commit Identifier” 的东西,但这多是一个较新版本的标识,在 2003 年出版的《Pragmatic Version Control Using CVS》一书中并无提到 “提交标识符” 这个概念。 (CVS 的最新版本于 2008 年发布的。5 )

在 Git 中,咱们所谈论某文件版本实际上是在谈论如 commit 45de392 相关的东西,而 CVS 中文件是独立版本化的。文件的第一个版本为 1.1 版本,下一个是 1.2 版本,依此类推。涉及分支时,会在后面添加扩展数字。所以你会看到如上所示的 1.1.1.1 的内容,这就是示例的版本号,即便咱们没有建立分支,彷佛默认的会给加上。

一个项目中会有不少的文件和不少次的提交,若是你运行 cvs log 命令(等同于 git log),会看到每一个文件提交历史信息。同一个项目中,有可能一个文件处于 1.2 版本,一个文件处于 1.14 版本。

继续,咱们对 1.1 版本的favorites.txt 文件作些修改(LCTT 译注:原文此处示例有误):

blue
orange
green
cyan
definitely not yellow

修改完成,就能够运行cvs diff 来看看 CVS 发生了什么:

$ cvs diff
cvs diff: Diffing .
Index: favorites.txt
===================================================================
RCS file: /Users/sinclairtarget/sandbox/colors/favorites.txt,v
retrieving revision 1.1.1.1
diff -r1.1.1.1 favorites.txt
3a4
> cyan

CVS 识别出咱们我在文件中添加了一个包含颜色 “cyan” 的新行。(实际上,它说咱们已经对 “RCS” 文件进行了更改;你能够看到,CVS 底层使用的仍是 RCS。) 此差别指的是当前工做目录中的favorites.txt 副本与存储库中 1.1.1.1 版本的文件之间的差别。

为了更新存储库中的版本,咱们必须提交更改。Git 中,这个操做要好几个步骤。首先,暂存此修改,使其在索引中出现,而后提交此修改,最后,为了使此修改让其余人可见,咱们必须把此提交推送到源存储库中。

而 CVS 中,只须要运行 cvs commit 命令就搞定一切。CVS 会聚集它所找到的变化,而后把它们放到存储库中:

$ cvs commit -m "Add cyan to favorites."
cvs commit: Examining .
/Users/sinclairtarget/sandbox/colors/favorites.txt,v < -- favorites.txt
new revision: 1.2; previous revision: 1.1

我已经习惯了 Git,因此这种操做会让我感到十分恐惧。由于没有变动暂存区的机制,工做目录下任何你动过的东西都会一股脑给提交到公共存储库中。你有过由于不爽,私下里重写了某个同事不佳的函数实现,但仅仅只是自我宣泄一下并不想让他知道的时候吗?若是不当心提交上去了,就太糟糕了,他会认为你是个混蛋。在推送它们以前,你也不能对提交进行编辑,由于提交就是推送。仍是你愿意花费 40 分钟的时间来反复运行 git rebase -i 命令,以使得本地提交历史记录跟数学证实同样清晰严谨?很遗憾,CVS 里不支持,结果就是,你们都会看到你没有先写测试用例。

不过,到如今我终于理解了为何那么多人都以为 Git 不必搞那么复杂。对那些早已经习惯直接 cvs commit 的人来讲,进行暂存变动和推送变动操做确实是毫无心义的差事。

人们常谈论 Git 是一个 “分布式” 系统,其中分布式与非分布式的主要区别为:在 CVS 中,没法进行本地提交。提交操做就是向中央存储库提交代码,因此没有网络链接,就没法执行操做,你本地的那些只是你的工做目录而已;在 Git 中,会有一个完彻底全的本地存储库,因此即便断网了也能够无间断执行提交操做。你还能够编辑那些提交、回退、分支,并选择你所要的东西,没有任何人会知道他们必须知道的以外的东西。

由于提交是个大事,因此 CVS 用户不多作提交。提交会包含不少的内容修改,就像现在咱们能在一个含有十次提交的拉取请求中看到的同样多。特别是在提交触发了 CI 构建和自动测试程序时如此。

如今咱们运行cvs status ,会看到产生了文件的新版本:

$ cvs status
cvs status: Examining .
===================================================================
File: favorites.txt     Status: Up-to-date
   Working revision:    1.2 2018-07-06 21:18:59 -0400
   Repository revision: 1.2 /Users/sinclairtarget/sandbox/colors/favorites.txt,v
   Commit Identifier:   pQx5ooyNk90wW8JA
   Sticky Tag:      (none)
   Sticky Date:     (none)
   Sticky Options:  (none)

合并

如上所述,在 CVS 中,你能够同时编辑其余人正在编辑的文件。这是 CVS 对 RCS 的重大改进。当须要将更改的部分从新组合在一块儿时会发生什么?

假设你邀请了一些朋友来将他们喜欢的颜色添加到你的列表中。在他们添加的时候,你肯定了再也不喜欢绿色,而后把它从列表中删除。

当你提交更新的时候,会发现 CVS 报出了个问题:

$ cvs commit -m "Remove green"
cvs commit: Examining .
cvs commit: Up-to-date check failed for `favorites.txt'
cvs [commit aborted]: correct above errors first!

这看起来像是朋友们首先提交了他们的变化。因此你的 favorites.txt 文件版本没有更新到存储库中的最新版本。此时运行cvs status 就能够看到,本地的 favorites.txt 文件副本有一些本地变动且是 1.2 版本的,而存储库上的版本号是 1.3,以下示:

$ cvs status
cvs status: Examining .
===================================================================
File: favorites.txt     Status: Needs Merge
   Working revision:    1.2 2018-07-07 10:42:43 -0400
   Repository revision: 1.3 /Users/sinclairtarget/sandbox/colors/favorites.txt,v
   Commit Identifier:   2oZ6n0G13bDaldJA
   Sticky Tag:      (none)
   Sticky Date:     (none)
   Sticky Options:  (none)

你能够运行cvs diff 来了解 1.2 版本与 1.3 版本的确切差别:

$ cvs diff -r HEAD favorites.txt
Index: favorites.txt
===================================================================
RCS file: /Users/sinclairtarget/sandbox/colors/favorites.txt,v
retrieving revision 1.3
diff -r1.3 favorites.txt
3d2
< green
7,10d5
<
< pink
< hot pink
< bubblegum pink

看来咱们的朋友是真的喜欢粉红色,但好在他们编辑的是此文件的不一样部分,因此很容易地合并此修改。跟git pull 相似,只要运行 cvs update 命令,CVS 就能够为咱们作合并操做,以下示:

$ cvs update
cvs update: Updating .
RCS file: /Users/sinclairtarget/sandbox/colors/favorites.txt,v
retrieving revision 1.2
retrieving revision 1.3
Merging differences between 1.2 and 1.3 into favorites.txt
M favorites.txt

此时查看 favorites.txt 文件内容的话,你会发现你的朋友对文件所作的更改已经包含进去了,你的修改也在里面。如今你能够自由的提交文件了,以下示:

$ cvs commit
cvs commit: Examining .
/Users/sinclairtarget/sandbox/colors/favorites.txt,v < -- favorites.txt
new revision: 1.4; previous revision: 1.3

最终的结果就跟在 Git 中运行 git pull --rebase 同样。你的修改是添加在你朋友的修改以后的,因此没有 “合并提交” 这操做。

某些时候,对同一文件的修改可能致使冲突。例如,若是你的朋友把 “green” 修改为 “olive”,同时你彻底删除 “green”,就会出现冲突。CVS 早期的时候,正是这种状况致使人们担忧 CVS 不安全,而 RCS 的悲观锁机制能够确保此状况永不会发生。但 CVS 提供了一个安全保障机制,能够确保不会自动的覆盖任何人的修改。所以,当运行cvs update 的时候,你必须告诉 CVS 想要保留哪些修改才能继续下一步操做。CVS 会标记文件的全部变动,这跟 Git 检测到合并冲突时所作的方式同样,而后,你必须手工编辑文件,选择须要保留的变动进行合并。
这儿须要注意的有趣事情就是在进行提交以前必须修复并合并冲突。这是 CVS 集中式特性的另外一个结果。而在 Git 里,在推送本地的提交内容以前,你都不用担忧合并冲突问题。

标记与分支

因为 CVS 没有易于寻址的提交对象,所以对变动集合进行分组的惟一方法就是对于特定的工做目录状态打个标记。

建立一个标记是很容易的:

$ cvs tag VERSION_1_0
cvs tag: Tagging .
T favorites.txt

稍后,运行 cvs update 命令并把标签传输给 -r 标志就能够把文件恢复到此状态,以下示:

$ cvs update -r VERSION_1_0
cvs update: Updating .
U favorites.txt

由于你须要一个标记来回退到早期的工做目录状态,因此 CVS 鼓励建立大量的抢先标记。例如,在重大的重构以前,你能够建立一个 BEFORE_REFACTOR_01 标记,若是重构出错,就可使用此标记回退。你若是想生成整个项目的差别文件的话,也可使用标记。基本上,现在咱们惯常使用提交的哈希值完成的事情都必须在 CVS 中提早计划,由于你必须首先有个标签才行。

能够在 CVS 中建立分支。分支只是一种特殊的标记,以下示:

$ cvs rtag -b TRY_EXPERIMENTAL_THING colors
cvs rtag: Tagging colors

这命令仅仅只是建立了分支(每一个人都这样以为吧),因此还须要使用 cvs update 命令来切换分支,以下示:

$ cvs update -r TRY_EXPERIMENTAL_THING

上面的命令就会把你的当前工做目录切换到新的分支,但《Pragmatic Version Control Using CVS》一书其实是建议建立一个新的目录来房子你的新分支。估计,其做者发如今 CVS 里切换目录要比切换分支来得更简单吧。

此书也建议不要从现有分支建立分支,而只在主线分支(Git 中被叫作 master)上建立分支。通常来讲,分支在 CVS 中主认为是 “高级” 技能。而在 Git 中,你几乎能够任性建立新分支,但 CVS 中要在真正须要的时候才能建立,好比发布项目。

稍后可使用 cvs update 和 -j 标志将分支合并回主线:

$ cvs update -j TRY_EXPERIMENTAL_THING

修正

有人告诉我,有不少组织企业,特别是像作医疗设备软件等这种规避风险类的企业,仍在使用 CVS。这些企业中的程序员经过使用一些小技巧来解决 CVS 的限制,例如为几乎每一个更改建立一个新分支以免直接提交给 HEAD。 (感谢 Michael Kohne 指出这一点。) via: https://twobithistory.org/2018/07/07/cvs.html

相关文章
相关标签/搜索