版本变更追踪

作者:Michael K. Johnson

多年来,软件开发者一直在使用他们称之为“版本控制”软件来追踪他们正在开发的程序的所有变更。然而,即使是 компетентный 的开发者有时也觉得使用这些工具太麻烦,除非他们发现绝对必要,因此,非常少的非开发者使用过这些工具也就不足为奇了。

他们发现这些工具如此难以使用的原因是,关于如何使用它们的规则是以最复杂的情况为前提编写的。 对于这些工具的大多数个人用途来说,这就像为了驾驶汽车而学习驾驶喷气式飞机一样。 有一种更简单的方法。

让每个人做这么多额外工作的假设是,不止一个人会尝试同时修改文件。 对于您自己的个人文件,或者当您是唯一的系统管理员时,为了追踪系统文件的变更,这不是问题。

在 Linux(以及大多数其他版本的 Unix)下进行版本控制的标准程序是 GNU 的 RCS,它代表 Revision Control System(版本控制系统)。它有很多选项,但您几乎不需要了解任何这些选项就可以很好地使用它。您可以将 RCS 视为系统上最简单的工具之一。

实际上,您可以使用一个命令来追踪您对文件所做的所有更改。每次您更改文件时,运行命令 ci -l filename (“ci”代表 “check in”(“签入”);您正在“签入您的更改”)。第一次执行此操作时,您将可以选择描述该文件

$ ci -l foo
foo,v  <--  foo
enter description, terminated with single '.'
  or end of file:
NOTE: This is NOT the log message!
>

您不是必须描述文件,但如果您愿意,可以描述。然后输入一个单独一行的 . 字符,或者在空行上按 ^D

当您更改文件后,您只需要输入一个命令即可告知 RCS 为您追踪该更改

$ ci -l foo
foo,v  <--  foo
new revision: 1.2; previous revision: 1.1
enter log message, terminated with single '.'
  or end of file:
>

在这里,您可能希望描述您刚刚进行的更改,特别是如果您认为将来想要检查该更改,但这不是必需的。

记住这一个命令就足以让您为灾难做好准备。本文的其余部分将向您展示一些简单的技巧,使 RCS 使用起来 稍微 舒适一些,并帮助您学习如何从灾难中恢复。

更简单?

如果您认为每次完成编辑文件后运行一个命令 仍然 太麻烦,我们可以让它更简单!(如果懒惰使您更聪明而不是更努力地工作,那么懒惰就是一种 美德。)

我们将使用一个 shell 脚本。您可以在您自己的 “bin” 目录(如果您的帐户与其他人共享计算机)或 /usr/local/bin(如果您正在使用自己的计算机)中创建一个文件。您选择的任何目录都必须在您的 PATH 中列出。为了便于输入,我们将其称为 et,是 edit text(编辑文本)的缩写。

#!/bin/bash
# et - Edit Text file, while keeping
#      track of changes with RCS
[ -z "$1" ] && {
  # No file specified on the command line
  echo "Edit what file?"
  exit 1
}
${EDITOR-vi} $1
# Only check in the file if it exists.
[ -f $1 ] && ci -l $1

将这些内容键入到名为 et 的文件中,不要出现任何错别字。然后运行命令 chmod +x et 使该文件 可执行。现在,您将能够运行 et filename 来编辑,然后自动签入您的更改。

#!/bin/bash 行告诉 Linux,此脚本是一个 shell 脚本,由 bash shell 解释,bash shell 是每个 Linux 发行版的标准组成部分。所有其他以 # 字符开头的行都是注释,bash 会忽略它们。

如果您确定您永远不想包含对您所做更改的描述,您可以将脚本的最后一行更改为

[ -f $1 ] && echo "." | ci -l $1

[ -f $1 ] 部分的原因是,如果您输入 et mistake,然后在不保存文件的情况下退出编辑器,当 ci 发现没有文件可以签入时,您不会收到错误消息。

${EDITOR-vi} 部分运行您最喜欢的编辑器,或者如果您没有选择最喜欢的编辑器,则默认使用旧标准 vi 编辑器。您可以选择 vi 以外的默认编辑器;pico、jed、joe、Emacs 和其他编辑器都是可能的选择。例如

${EDITOR-jed} $1

将运行 jed,除非您选择了其他最喜欢的编辑器。

要选择您最喜欢的编辑器,这将适用于所有希望您选择编辑器的程序,而不仅仅是 et,您需要将 EDITOR 环境变量设置为您想要使用的编辑器的名称。如果您使用的是 bourne shell,例如 bash、zsh、pdksh、ksh 或 sh,您需要将如下行添加到您主目录中的 .profile 文件中

EDITOR=jed ; export EDITOR

如果您使用 C shell(csh 或 tcsh),您需要将如下行添加到您主目录中的 .login 文件中

setenv EDITOR jed
它是如何工作的?

RCS 不会在每次您签入更改时都保留文件的全新副本。相反,它只记录包含更改的行,以及更改的描述(如果您选择提供)。它在一个单独的文件中执行此操作。对文件 filename 的更改保存在文件 filename,v 中。如果您觉得这太乱,您可以告诉 RCS 将 filename,v 文件隐藏起来,放在一个名为 RCS 的子目录中。只需创建子目录,RCS 就会自动使用它。

还记得上面签入 foo 文件时的样子吗?让我们创建一个 bar 文件,其对应的 bar,v 文件被隐藏在 RCS 目录中

$ mkdir RCS
$ vi bar
$ ci -l bar
RCS/bar,v  <--bar
enter description, terminated with single '.'
  or end of file:
NOTE: This is NOT the log message!
>
$ vi bar
$ ci -l bar
RCS/bar,v  <--  bar
new revision: 1.2; previous revision: 1.1
enter log message, terminated with single '.'
  or end of file:
>

如果您最初没有创建 RCS 目录,并且您厌倦了看到 ,v 文件,您可以使用以下命令安全地将它们隐藏起来

$ mkdir RCS
$ mv *,v RCS

RCS 将知道在哪里搜索它们。

时光倒流

那么,当您搞砸时该怎么办?您已经仔细地保存了更改副本,但是如何检索昨天的版本、去年的版本或倒数第二个最新版本呢?

RCS 通过 版本号 追踪版本。您签入的第一个版本被分配数字 1.1,第二个版本被分配数字 1.2,第三个版本是 1.3,依此类推。

当您发现自己犯了一个错误时,您可以将当前拥有的内容与以前的版本进行比较。要与之前的版本进行比较,请使用此命令

$ rcsdiff -u filename

-u 告诉 rcsdiff 使用 “unified diff”(统一差异)格式来向您显示更改,它将当前版本的 filename 文件与最近签入的版本进行比较。这是一个例子。之前签入的文件 foo 版本 1.3,包含以下内容

This is a test of the emergency
RCS system.  This is only a test.

此后,我编辑了当前版本,使其内容为

This is a test of the emergency
RCS version control system.
This is only a test.

签入这个新版本之前,我可以检查文件当前内容与之前签入的版本之间的差异,得到以下结果

$ rcsdiff -u foo
==============================================
RCS file: foo,v
retrieving revision 1.3
diff -u -r1.3 foo
--- foo 1996/02/01 00:34:15     1.3
+++ foo 1996/02/01 00:34:31
@@ -1,2 +1,3 @@
 This is a test of the emergency
-RCS system.  This is only a test.
+RCS version control system.
+This is only a test.

在显示要比较的版本之后,会显示差异。未更改的行前面会打印一个空格。较新版本中删除的行前面有一个 -,添加的行前面有一个 +。正如您所看到的,已更改的行被视为从旧版本中删除,并将更改的替换项添加到新版本中。在任何包含已更改行的部分周围,最多会显示 3 行未更改的“上下文”,以帮助您了解更改发生在文件中的哪个位置。

此机制可用于比较任意两个版本。在对文件进行更多更改后,我可以将版本 1.6 与版本 1.3 进行比较

$ rcsdiff -u -r1.3 -r1.6 foo
==============================================
RCS file: foo,v
retrieving revision 1.3
retrieving revision 1.6
diff -u -r1.3 -r1.6
-- foo 1996/02/01 00:34:15     1.3
+++ foo 1996/02/01 01:05:28     1.6
@@ -1,2 +1,6 @@
 This is a test of the emergency
-RCS system.  This is only a test.
+RCS version control system.
+This is only a test.
+
+I'm now adding a few lines for
+the next version.

请注意,最好先列出较早的版本。否则,+- 的含义将颠倒。

您的更改可能比这些示例重要得多,并且可能占用超过一个屏幕的列表。这不是问题;使用分页器一次查看一个屏幕的输出

$ rcsdiff -u -r1.3 -r1.6 foo | less

一旦您看到您所做的更改,您通常可以找出您在哪里犯了错误并通过手动修复它。有时您可能不小心造成了巨大且令人困惑的损坏,但更多时候您更改了一个短语或一个段落,对您的更改不满意,并且根本不记得它以前是什么样子了。

如果需要,您可以使用类似以下的命令将更改副本存储在文件中

$ rcsdiff -u -r1.3 -r1.6 foo > filename

或像这样打印更改

$ rcsdiff -u -r1.3 -r1.6 foo | lpr
从重大错误中恢复

如果到了您宁愿简单地恢复到旧版本的时候,请选择您希望恢复到的版本(称之为 1.x),然后运行以下命令

$ ci -l foo
$ co -r1.x foo
RCS/foo,v  -->  foo
revision 1.x
writable foo exists; remove it? [ny](n): y
$ chmod +w foo

此时,您已恢复到文件版本 1.x。从那时起,您可以继续进行更改,就好像您只是编辑了最新版本,直到它与版本 1.x 完全相同。

请注意,您几乎永远不必这样做;很多人从未这样做过。

查找旧的更改

当您正在查找某个特定更改时,您如何找出要查看的版本?一个命令提供了自版本 1.1 以来所做更改的即时摘要。这就是为重要更改提供描述派上用场的地方。要查看所有更改的日志,请使用 rlog 命令

$ rlog bar
RCS file: RCS/bar,v
Working file: bar
head: 1.3
branch:
locks: strict
        johnsonm: 1.3
access list:
symbolic names:
keyword substitution: kv
total revisions: 3;     selected revisions: 3
description:
----------------------------
revision 1.3    locked by: johnsonm;
date: 1996/02/01 02:40:16;  author: johnsonm;
    state: Exp;  lines: +1 -0
Added different text.
----------------------------
revision 1.2
date: 1996/02/01 02:39:59;  author: johnsonm;
    state: Exp;  lines: +1 -0
Added some text.
----------------------------
revision 1.1
date: 1996/01/31 21:22:36;  author: johnsonm;
    state: Exp;
Initial revision
==============================================

对您来说最重要的是最后的版本描述。例如,在 revision 1.2 部分下,有一条注释 Added some text. 这是您在每次修订后被允许键入的注释。如果您选择不输入注释,则会显示

*** empty log message ***

当您正在查找您所做的更改时,这并不是特别有用。

另一方面,如果您发现输入注释太麻烦以至于您想避免使用 RCS,那么当事情出错时,能够去查找您想要的版本,总比没有任何信息要好。不要因为您觉得应该描述每个修订版而避免使用 RCS...

这些日志会变得非常长,非常快。为了一次查看一个屏幕的日志,请使用分页程序

$ rlog bar | less

同样,如果您愿意,您可以将日志发送到文件或打印出来。

结论

关于 RCS 的内容 比本文中介绍的要多得多,因为这是一篇旨在让您轻松使用 RCS 的教程。如果您有兴趣,RCS 附带了完整的手册页,以及关于如何在开发环境中使用 RCS 的论文。此外,《Linux Journal》在 1995 年 2 月刊中发表了一篇关于 RCS 的早期文章,该文章更侧重于开发者。但是不要认为您必须了解 RCS 的所有内容才能有效地使用它。

Michael K. Johnson (XXXXXXXXXXXXXX) 是《Linux Journal》的编辑,并以本文描述的方式使用 RCS 来追踪他在为《Linux Journal》编辑文章时所做的所有更改。

加载 Disqus 评论