版本控制的开放标灌
。 撰文/Jim BI andy 编译/马维达
CVS,即同步版本系统 (Concu rrent Version s Sy stem),是占统治地位的版本控制 系统。它具有开放源码、 “网络透明” (Network—
t r a n S P a r e n t)的特点 ,从个体开 发者到 大型的分布式开发队伍 ,都可以使 用它来进行项 目的版本控制。许多流行的开放源码项
目.{~M0zilla,GIMP,XEMac s、KDE和GNOME,都使用了CVS。
关键词 :cVS 版本控制
【CVS的用途是什么?】
c V S根据一系列改动来维护源树的历史。它通过改动时
『日】和改动者的 用户名来记录每次改 动。通常 ,改动者还应 该
提 供 一些 文本 ,描 述为 什 么要做 出改动 。根 据 这些 信启、,
c V s可以帮助开 发者回答这样的问题 :
÷ 是 谁做出的特 定改 动?
他们 是什 么时候 做 出改 动的?
+ 他 们为 什么要做改 动?
÷ 他们 同时还做 丁哪些 其他改动 ?
下面 ,我们来学习使用c VS。
【设置你的仓库 (repository)l
c V S将每个人对特定项 目的改动记录在称为仓库的目录树
中。在使用cVS之前,你需要将cVSR00T环境变量设置为仓库
的路 径 。无论 是谁 负 责项 目的 配置 管理 ,他们 都必须 知 道
cVSRO0T,或许他们已经在某处为CVSRO0T作 了全局定义。
在任何情况下,在我们的系统上cVS仓库都是 “/u/S rc/
m aster”
。 这样,如果你的shell是csh或它的派生版本,你需
要输 入命 令 :
sPteIp,,CVSRO0T //u/src/master
如果你的shell是Bash或某种其他的Bourne shell变种,
输 入 :
CVSROOT /U/src/master
export C}SROOT
如果你忘 了这样做,C V S将 在你试图使用它时抱怨
$ CVS checkout httpc
S checkout: No CVSROOT specifiedf Please use the ’ d’ option
S『checkout aborted『:or set the CVSROOT environment variable
C V S并非是 在普通的 目录树 工作,你需要在C V S为你
创建的 目录中工作。就 像你在把一本 书从图书馆带 回家 阅读
之前需 要先检出一样 ,在操 作一个 目录树 之前 ,你也需要使
用CVS checkout命令来从cVS那里获取它。例如,假设你 目前
工作的项目名为httpc,一个平常的HTTP客户:
$ cd
$ c~’s checkout httpc
CVS checkout:U吣 ating httpc
U httpc /.cvsignore
U httpc /M akefile
U httpc/httpc.c
U httpc /poll server
命令CVS checkout httpc意为: “从由CVSROOT环境变
量指定的仓库中检出称为httP C的源树。”
cVS将树放在名为 “httpc”的子 目录中:
$ cd httpc
$ Is l
total 8
drwxr xr x 2 jimb 5l2 Oct 31 11:04 CVS
rw—r r l jimb 89 Oct 3I 10:42 Makefile
rw r r I jimb 4432 Oct 3I 10:45 httpc .c
rwxr xr x I jimb 460 0ct 3O 10:2I poll server
这些文件的大多数是你的h t t P C源的工作拷贝,但名为
“cV S”的子 目录 (在顶部 )不同:cV S使用它来记录该 目
录 中每 个文件的额外信息 ,以帮助确定你把 文件检出后都对
它做 了什 么改动 。
雯
一 旦c V S创建了工作 目录树,你可以通过平常的方式来
编辑 、编译 和测试 该 目录 中所 包含的文件一一 它们就 只是文
件而 已 。
例 如 ,假设我 们尝试编译 我们刚 刚检出的包 :
$ mare
gcc g W all Insl lsocket httpc .c 0 httpc
httpc .c: In function ’tcp connec tion’:
httpe .C:48:wam illg:pas~ing arg 2 of’connmt from incompatible poin~r type
看起 来 “h t t P C.C”还 没有被移植到 这个操作 系统 ,
我们需要转换C 0 n n e ct的一个参数。为修正此 问题 ,4 8行
维普资讯
版本黯制的开放摞准
。撰文 jJim Blandy 编译/马维这
CVS. !!r 同步版本系统 (Concurrent Vers lO ns System) ,是占统治地位约盖在本控制系统。它具有开放派码‘ "网络透明" (阳twor k.
transparent) 的特,主、从个体开发者到大型约分布式开发队伍‘都可以使用它来进行项目的成本控制。许多过行的开放派码项
目. f享Mozilla 、 GIMP XξMacs 、 KDE和G挝OMξ 啕都使用了 CVS o
关键词 cvs 版本控哥哥
icvs的用途是什么Ð
C vs 较据→系列改动采维护源树的历史 c 它通过改动时
间和改动者的用户名来记录每次改动。 i虽常,改动者还应该
提供一些文本,描述为什么委做出改动。根据这些信息,
CYS 可以帮助开发者回答这样的问题 z
令是谁做出的特定改动?
令他们是什么时候做出改动的?
令他们为什么要散放动?
令他们同时还做 f哪些其他改动?
下面,我们来学马使用 C VS 0
i t量置惊的仓库(repositoryl]
C VS 将每个人对特定项目的改动记录在称为仓库的吕录树
中。在使用CVS之前, f.尔需要将CVSROOT环境变量设置为仓库
韵跻径。 E论是谁负责项目的自己置管理,他有1都必须知道
CVSROOT ,或许他有1已经在某处为CVSROOT作了全局定义c
在任何情况下,在我们的系统上CVS仓库都是勺u/srcl
master" 。这样,如果你的she lI是csh或它的派生版本,你需
委输入命令:
S('tcnv C 飞 SROOT "U/Sfe,/master
如果你的shell是Bash或某种其他的Bourne shell变称,要Ij
输入 z
C飞iSROOτ , .u'src'rnaster
expoτt C\'SROOT
如果你忘了这样做, C V S 将在你试图使用它对抱怨 z
$口sεheckuut httpc
C\ S chcckùut: Noζ\'SROOT spcôfied! Plcase use the 可 d 可 oDtIOn
cvs Ichcckout abortedl 去 or SCT the (二 VSROOT cnvÌronment 可 a fIable
i 撞出 (check out) 工作目录;
CVS 并非是在普通的目录树七工作,你需要在CVS 为你
创建的吕录中工作。就像你在把一本书从图书馆带回家阅读
之前需要先检出一样,在操作一个吕录树之前,你也需要使
用cvs checkout命令来从CVS那里获取它。例如,假设你目前
工作的项目名为httpc ,一个平常的HTTP客户:
军 cd
$ Cß chε'('kout hHpc
C飞 s checkuul ~ U萨1ating hHpc
U httlX- .L' lislgnu fC
U httpc, 扎,1a豆豆efile
U httpc/httpc. c
U httIX/poll sen'cr
命令cvs checkout httpc意为从自CVSROOT环境变
量指定的仓库中检出称为h tt pc 的源树。"
CVS将树放在名为 "httpc 的子目录中:
$ cd httpc
$ 15 1
drwxr -xτx 2 Jlmh 51~ 0ιl 31 11 ,04 t二 vs
rw- r 1 Jimb 89 Oct 31 10 ,42 Makefile
rw r r - I jímb 4432ζlct 31 10 ,45 hlt (X .c
rWXf xr x I Jimb 460 Oc! 30 10:21 poll--scrver
这些文件的大多数是你的httpc 源的工作拷贝.但名为
"C V S" 的子目录(在顶部)不同 CVS 使用它来记录该目
录中每个文件的额外信息,以帮助确定你把文件检出后都对
立做了什么改动。
i 对立件做出改动}
一旦C V S íìlj建了王作目录树,你可CJ,通过平常的方式来
编辑、编译和测试该吕录中所包含的文件→一它们就只是文
件而已。
例如,假设我们尝试编译我们刚刚检出的包 z
军 make
gcc g 有 all ln剖 lsocket htt lX .c -0 httpc
: ln functton 飞CP_COllηectìon'
http:.e:4:5: waming年 pa由ìng arg 2 of ! connoct I from În∞白i且tible pointετtypc
看起来 "" 还没有被移植到这个操作系统,
我们需要转摸connect 前→个参数。为修正此问题, 48 行
必 须 从 :
1f (COnI2CCt (sock, &llan1c S1zcof(1lame))> 0)
改 变 为
lf(connect(sock,(struct sockaddr·)&name,sizeof(name))> 0
现在 它应该可以 通过编 译 r:
$ make
gcc g “all lnsl lsocket httpc c o httpc
$ httpc GET http:/ ww w .cyclic.con]
.
HTM L text for Cyclic Softw are’s home page follow s
匦圃
因 为 每 个 开 发 者 部 使 用 自 己的 工 作 目录 , 所 以 你 对
你 的 工 作 目录 所 做 的 改 动并 不 会 自动 地 被 其 他 开 发 者 看
见。不 到 你准备 就绪 ,C V S不 会公 布 你的改 动 。当 你完
成测试 以后 ,必须将 它们提 交 (c o m m i t)给仓库 ,以 使
它 仃】能 为组 的 其 他 成 员 所 用 。 我 们 将 在 下 面 介 绍 c v s
commit命令。
但是 ,如果 另一 个开 发者 已经改动 了你所改动的 同一文
件甚至 同一 行 ,该怎 么 办呢 ?谁 的改 动应 该 成功 ? 一般而
言,要 自动解决这种问题是不可能的,C V S当然没有能 力来
怍出那样 的 lJ断 。
因而 ,在提 交你的改 动之前 ,C V S要求 f;『:的源 与其 他
组 成员提交的任何改动保持同步。c v s uPd ate命令负责这
个功 能 :
$ cvs uDdate
c、s update: Updating .
U M akefile
RCS file: u/src/master/httpc/httpc.c,v
retriex inn revision 1.6
retriexing rexision 1.7
Merging differences between 1.6 and 1 7 into httpc c
M httpc c
让我们 一行一 行地来查 看 :
U Makefile
“U f i 1 e”形式的行意味着该f i 1 e已被明确地更新
(Updated):另外有人对此文件做了改动,而CVS已将被修
改的文 件拷贝进 你的主 目录 中。
RCS file: ..
retrieving revision 1 6
retrie~ing re、ision 1 7
Merging differences between 1.6 and 1.7 into httpc.c
这些消息表示另外有人改动了 “httpc.c”,CVS将他们
的改动 与你的 合并在 了一起 ,并 且没有发现任 何文本 匕的冲
突。数字 “1.6”和 “1.7”是修订版号 (r e v i s i o n
numbe r),用于标识文件的历史中的特定点。注意CVS只是
将改动合并进 你的 工作拷贝中 ,仓库和其他开 发者的工作 目
录没有受到打扰 。要 由你来测试 合并后的文本 ,并 确保它是
有效的 。
“M file”形式的行意味着该file已被你修改 (Modified),
并含有对其他 开发嚣还 町 的 改动。这些是你需 要提 交的
改动。这样, “httP c.c”现在同时含有你的修改和其他用户
的修 改。
因为C V S已将其他人的改动合并进你的源中 ,最好确定
程 序还 能工 作 :
$ m ake
gcc g W all 【rm issing prototypes lnsl lsockct httpc.c o httI~"
g httpc GET http·///u u u .cyclic.com
. }ITM L text for Cyclic Software s home page follows ...
逐塑囹
现在 你已使你的源跟 上了其 余成 员那里的最 新情况 、并
对它们做 了测试 ,你可以提 交你的改动给 仓库 .并使它们对
组 的 其 余 成 员成 为 可 见 的 。唯 一 被 你 修 改 过 的 文 件 是
“httpc.c”,但最好还是运行cvs update,从Cvs/J[~里获取被
修改过 的文 件列表 :
$CVS update
c、’s update:Uixlating
M httpc.c
如所预期的,CVS所性fll~;j唯一文件是 “httpc c”,它说
该文件含有你还 未提交的改动。你可以像这 样来提交它们:
$ CVS COUImit httpc.c
在这时,C V S会启动你所喜爱的编辑器 ,并提示你输 入
日志消息来描述改动。当你退出编辑器时 ,C V S将提 交你的
改 动 :
checking in httpc.c;
/u/src/master/httpc/httIx".c v < httpc c
new revision: l 8; previous rexision: l 7
现在 你已经提交 了你的改动 ,它们对组的其 他成 员是可
见的。当另外的开发者运行c v s uPd ate时 ,CVS将把你对
“httP c c”的改动合并进他们的工作 目录中。
现在你可能很想知道其他开发者都对 “h tt P c.c”做 了什
么改动。为了查看特定文件的 日志条 目,你可以使用c v s 1og
命 令 :
$ cvs log httpc.c
RCS file:/u/src master/httpc/httpc c v
W orking file:httpc.c
head: 1 8
1]Fanch:
locks!strict
access llst!
symbolic names:
kcyword substitution:kv
tota1 revisions: 8I selecte(1 revisions: 8
description:
The o1]e and onlY SOUrCe file for the trivial 1 1TTt clicnt
revision 1.8
(1ate: 1996//lO/31 10:lI:14
维普资讯
··墨画国.. 墨噩噩
-NCEN--M
"M filc" 形式的行意味者该filc已被({I;修改 (Modificd) 、
并含有对其他开发.r,-)f -1二哥见的改动。这些是 ({I; 需要提交的
改动。这样 "" 现在同时含有你的修改和其他用户
。)
必须从:
lf 江 onncL' 1 \sock. &ndmC. ::'l í'.C'nf fU d. THc)) >
改变为
的修改。
因为CVS 己将其他人的改动合并进你的源中、最好确定
程序还能工作:
$ ,引 akc
gü: g 飞~'all WmlS'、llìg - proloty~充 111':>1
$ hllpc GET hllp ,". w飞V飞.ζlîc. com
HTML tcxl fùr Cyclic SOf:Wð陀、 homc pagc follows
。}If {COlllICl'l fsoch.. (::,lrud sOL'kaúúr 叫 &ηamc. sìzeof (name)) >
现在它应该可也通过编译 j\
$ makc
gc乙 g Wa11 ìnsl lsockεt
$ hllpc GET lrl'.p,飞.v v..'飞ζlî c. con飞
HTML tcxt [or CyclIc Soft玩farc's home pagεfollows
o httpc
。 httpcIsockcl htt jJC
[合并你的毒动}
因为每个开发者都使用自己的工作目录吨 i 提交悔的鼓动}
现在你已使你的源跟上 7其余成员那里的忌新情况、并
对它们做了测试、你可以提交你的改动给仓库.并使它们对
组的其余成员成为 4 见的。唯 a 被句:修改过的文件是
所以你对
你的工作目录所做的改动并不会自动地被其他开发者看
见 c 不到你准备就绪、 CVS 不主公布你的改动。当你完
成测试以后、必须将它们提交 (cornrnit) 给仓库,以使
"" 、{旦最好还是运行cvs updatc. 从CVS那里获取被
修改过的文件)111 表
$ C\'S updatc
C飞 s update! LiX!<HlIìg
M hltp:.c
知所预期的、 CVS所提到剖唯一文件是 "" 、它说
该文件含有你还未建交的改动。约I;~T以像这样来提交它们:
我们将在下面介绍 c v s
cornrnit 命令。
但是.如果另一个开发者已经改动了的;所改动的同一文
它们能为组的其他成员所用。
?牛甚至同一行亥 Z. 么办呢?谁的改动应该成功?一般而
言、要自动解决这种问题是不吁能的、 CVS 当然没在能力来
作出那样的判断。
民币、在提交你的改动之前、 CVS 要求你的源与其他 $ c飞 s con,口号 lt htt}X年 C
在这时, CVS 会启动你所喜爱的编辑器,并提示 1尔输入
日志、消息来描述改动。当你退出编辑器时、 CVS 将提交你的
改动 z
Checkìng In
u.'src Ilwstcr !lttρc , <- - hl~i汇 C
new rc飞l::Hon: I 也 1 jJ rc飞 IOUS re川 SlOjÌ;
现在你已经提交了你的改动、它们对组的其他成员是可
泣 pda te 命令负责这
见的。当另外的开发者运行cvs updatc 肘, CVS将把你对
"" 的改动合并进他!门的工作吕录中。
个功能:
$ CYS Uρdat{'
C飞 s upcicJ tc: U1旦aung
U 、.1akcfilc
RCS [il(': U src , maslcc ht:pc , 'htlJ)i.二 .C ‘飞
rctnc飞 III只 re飞 lsíon
rctne 飞 111义 re飞 fsion
Merglllg dlHerences betwceη 1. 6 anè 1. 7 lI1 :0 hltpc. c
M
让我们一行一行地来查看:
组成员提交的任何改动保持同步。 cvs
i 撞查鼓动)
现在你可能很想知道其他开发者都对 "" 做 rt十
log 么E主动。为了查看特定文件的日志条吕,你可以使用cvs
命令:
$ c\'s log httpc , c
RCS fiìc: . u. src mastcr: h :tpc , 'httPC ,c,飞
飞飞。rking filc; httpc. L'
hcad , L~
branch:
locks! slnct
access hst ~
S 飞.'r:l hollC namcs
kcy飞wurd SUbS~l:ut1Oηk、
total rcnS1OIlS: :S! selectcd 陀、 îsioTl5: :S
【 lc骂~nption:
Thc 011(' and only sourcc ftlc for ~且c trîvìal llTTP clicn',
f i 1 c "形式的行意味着该f i 1 e 已被明确地更新
(Updated) 另外有人对此文件做了改动、而CVS 已将被修
改的文件拷贝进你的主吕录中 c
RCS Îl lc
rctnCYlllg rc飞 lSlon
rctnc亏Ulg rc飞 ISlOn 1 、 7
Mcrgmg dlffcrcnccs hctwccn 1.á and into h:t !JC .c
这些消息表示另外有人改动了 "" 、 CVS将他们
的改动与你的合并在 7 一起,并且没有发现任何文本七的冲
突。数字 "1 .毛"手口气 .7" 是修订极号 (rev sio 召
numbe 叶,用于标i只文件的历史中的特定点。注意CVS 只是
将改动合并进你的工作拷贝中,仓库和其他开发者的工作吕
录没有受到打扰 o 要由你来源l试合并后的文本、并确保它是
C MahefJic
"U
r('\'15101> LR
r1alc , 1996, 10 31 白 11:14~ author: jimb; ~talc: EXPI
有效的。
1m臼z 吟M h
-…四…·
tcp connection):Cast address structure when calling connect
revision l 7
date: 1996/10/31 19:18:45I author: fred{ state: Expl lines:+6 2
(match header):Make this test case 1nsensitive.
revision 1.6
date:1996/'10/31 19:15:23} author:jimb{ state:Exp{ lines:+2 6
你可以忽略这里的 大多数文本 ,要仔 细查看的部分是 第
一 行连字号 后面的 日志 条 目。越近的修 改通 常也越能吸 引你
的兴趣 ,所以这些条 目以年月 日的顺序 反向出现 。每 个条 目
描述 对文件的一 次改动 ,并可被解析 如下 :
revision 1.8
文 件的 每 个版 本都 有唯 一的 修订 版号 。它看起 来 像是
“1.1”
、 “1.2” “1.3.2.2”,甚或 “1.3.2.2.4.5”。
缺省地 ,修订版 1.1是文件的第一版 ,每 个后继修订版通过将
最右边 的数字加 一来获 得一 个新号 。
date:1996/10/31 20:1 1:14I author:jimb} ⋯
这一行给 出改动 日期 ,以及提交它的 人的用户名 ,余 下
部分没 什 么意思 。
(tcp connection):Cast...
这是 (相 当明显)对改 动进行描述 的 日志 条 目。
Cv S lOg命令可以通过 日期范围或修订版号来选择 日志条
目,详细资料见CVs手册 (manua1)。
如果你实际上想要查看正在讨论的改动,你可以使用c v s
diff命令。例如 ,如果你想要查看F red作为修订版 1.7提交的
改动 ,你 可以使用下 面的命 令 :
$ CVS diff c r 1.6 r 1.7 httpc.c
这里是 该命 令的输出 :
Index: http~.c
RCS file: /u/src/master/http~/http~.C.v
retrieving revision 1.6
retrieving revision 1.7
diff c r1.6 rl 7
+ httpc.c l996/l0,/31 19:1 5:23 1
httpc.c 1996/10/31 19:1 8:45
⋯ +⋯ ⋯ ++
⋯ 62 68 +}+
/ Return non zeFo i HEADER is a prefix of TEXT. HEADER should be
null terminated t LEN is the length of TEXT /
static ln L
match header (char*header.char*text,size L len)
62.69
Return non zero iff HEADER is a prefix of TEXT。 ignoring
differences in CaSe. ttEADER should be lower case . and
null—terminatedI LEN is the length of TEXT. +/
sta cic Int
match header(char *header
●●⋯ ⋯ ⋯ ⋯
char t= text[i】}
if( A < L&& t <= 。Z
L += a ’A l
if(header(i1 1 t)
return 0I
需要一 点努力才能 习惯此输 出 ,但它毫 无疑问是值得理
解 的 。
有趣的部分是从第一处 由 和⋯ 起头的两行开始的,
它们描述较 旧和较新的被 比较 的文件 。余下部 分 由两 个大块
(h u n k)组成 ,每个大块都由一行星号开始。这里是第一个
大块 :
十⋯ +⋯ 川 十 +
十 62.68 十
/ Retum non zero iff HEADER is a prefix of TEXT. tlEADER should be
null terminated t LEN is the length of TEXT +/
static lnt
match header(char *header,char*text,size t 1en)
62.69
1/ Return non zero iff HEADER is a prefix of TEXT,ignoring
! differences 1n case . HEADER should be lower case,and
null terminated t LEN is the length of TEXT ·/
static 1n L
match header(char*header,char*text,size L len)
来 自较旧版本的文本出现在 62,6 8 行后面,来
自较新版本的文本出现在⋯ 62,69 ⋯ 行后面,每对数
字指示所显示行的范围。C V S在改动的周围提供上下文,并
将实 际被影响的 行标上 “!”字符 。因而 ,我们 可以看到上
半 边的单 行被下半 边的双 行取代 了。
这 里是 第二个 大块 :
+++⋯ ⋯ +
+ 76 81 ++
一 77.84
for(1= 0I i< header len{i++)
char t=textfilI
+ if(。A <= t&& t<= 。Z’)
+ t + a。 。A I
if(headerli】!= L)
return 0I
这个大块描 述插入的两 行,它们被标上 了 “+ ”字符。
在这种情况下 cV S省略了旧文本,因为它是多余的。cV S使
用类似 的大块格式 来描 述删除 。
像Unix diff命令一样,来 自CVS diff的输出通常被称为
补丁 (Patch),因为开发者经常使用该格式来发布错误修正
或小的新特性 。在适 当地让 人能理 解的同时 ,补丁 也含有足
够的信息 ,能让 一个程序来将 该补 丁描 述的改动应 用到未被
鲫
.奢
<
~ 一m
趴__ ⋯
~
维普资讯
(tcp_canneclion): Cast address struc山陀百hen callingεannεcl.
revlsian
date , [996/ [0/3[ 19 ,[8 ,45 , authar , fred 雹 state: Exp~
lmatch_header) , Make this 旨乳 case-msensítlve.
τεvislGn
lines , +6 --2
date: 1996/IO/31 19 :1 5:23 t author: jimb~ stale: Exp~ 1incs: +2 6
你可以忽略这里的大多数文本,要仔细查看的部分是第
→行连字号后面的日志条目。越远的修改通常也越能吸引你
的兴趣,所以这些条目以年月日的顺序反向出现。每个条目
描述对文件的一次改动,并可被解析如下:
r它亏 iSlGn
文件的每个版本都有唯一的修订版号。它看起来像是
"1 . 1 "、 "1. 2" , "1. " ,甚或"1. " 。
缺省地,修订版上 l 是文件的第一极,每个后继修订版通过将
最右边的数字加一来获得一个新号。
da12 , [99ó/ lOd[ 20 ,[[, [4 ‘ authoτJlmb t
这一行给出改动日期,以及提交它的人的用户名,余下
部分没什么意思。
fLcp_cannectìon): Cast ."
这是(相当明显}对改动进行描述的日志条目。
cvs log命令可以通过日期范围或修订版号来选择日志条
目,详细资料见cvs手册 (manual) 。
如果你实际上想要查看正在讨论的改动,你可以使用cvs
diff命令。例如,如果你想要查看Fred 作为修订版 提交的
改动,你可以使用下面的命令 z
S cvs <lJff -c r 1. 6 -r 1. 7 httpεaε
这里是该命令的输出:
lndex ,
RζS (, Ieτ/u/src/mas垃r/httpc/ , v
re飞ne忘 íng rε\'isíon
retríevmg revisìon
dl((ετ1. 6 -r l. 7
.拿拿 httpc.ε [996/10/31 [9 , [5 ,23 [.6
[996/10/3[ 19, 18 ,45
.李审 61 警 68 .拿拿拿
• Reluπ1 n阳罚 zero 1ft HEADER ís a prefix 01 TEXT. HEADER should be
null terminat缸J , LEN is the [εngth a( TEXT. • /
stat主c mt
match header (char 时leadεr , char .text. sizc_l 1εn)
62 , 69
• Return !1ao- zero if( HEADE在比 a prefix af TEXT. 19noríng
(lif(ε目前白的 case. lIEADER shau[d be lower--白里, and
n u l1 ~ termin a tεd ‘ LEN is the [ength af TEXT. 巷/
StdUε int
用atch hεad C'γlchaτ.head町, char .tC'xt. size 1 )en)
.....拿..拿司...司...
拿李拿 76寄自 1 ."..
77 , 84
(ar (, 0- 毡, 1 < hεadeτlen , i+ -+)
ε且aτt ~ tex t{ il ,
íf ('A' <- t && t <= 'Z')
1 斗争二 'a I - 'A I ‘
lf (header{礼!飞?
reWrn 0 ‘
需要一点努力才能习惯此输出,但它毫无疑问是值得理
解的。
有趣的部分是从第一处囱***和 -起头的商行开始的,
它们描述较 i日和较新的被比较的文件。余下部分由两个大块
(h un k) 组成,每个大块都囱一行星号开始。这里是第一个
大块=
••• 62 、 68 ..拿拿
/. Retum nan zero i打 HEADER is a prefix o( TEXτ. IlEADER should be
null-terminated , LEN !s the length o( TEXT. 春/
static 10t
ma注h headεr 1εhar 拿hε'adC'τ , char .texl. S!zC' _t len)
62 ,69
! /. Rεtum non zera i(( HEADER is a pre(ix a( TEXT. ígnaring
diHerences in case. HEADER should be lawer-case. and
nu lI terminated , LEN IS lhe !englh o( T主XT. 拿/
statlc mt
match_header (char 拿 header. char 吨εx t. s!ze_l lcn)
来自较 1E3版本的文本出现在*** 岳 2 , 68 ***行后面,来
自较新版本的文本出现在一 - 62 哩 69 行后面电每对数
字指示所显示行的范围。 cvs 在改动的周围提供上下文,并
将实际被影响的行析、上"! "字符。因而,我们可以看到上
半边的单行被下半边的双行取代了。
这里是第三个大块:
**" 76 , 8! .拿..
77 , 84
(ar tl 二在寻 i < headeτ!en~ i 斗争+)
εhar t 二 textlil ,
+ 过 ('A' <~ t && t <=- 'Z')
+ 一争 'a' - 'A' ,
if (h回der{i] 二。
return 0 雹
这个大块描述插入的两行,它们被标上了+ "字符。
在这种情况下CVS 省略了|日文本,因为它是多余的。 CVS 使
用类似的大块格式来描述鹰号除。
像Unix dìff命令一样,来自 cvs dìff的输出通常被称为
补了 (patch) ,因为开发者经常使用该格式来发布错误修正
或小的新特性。在适当地让人能理解的闰时,补了也含有足
够的信息,能让一个程序来将该补了描述的改动应用到未被
修 改的 文本 文件 。
匾夔匿
C V S像对待其他改动一样对待文件创建和删除 ,它在文
件的历史中记录这样的事件。也就是说 ,C V S记录 目录以及
它们所 包含的文 件的 历史。
C V S没有假定新创建的文件应被置于它的控制之下 ,在
许多情况下这样 的假定会 出错 。例如 ,我 们不需要记录对 目
标 文件和可执 行文件的改动 ,因为它们的 内容 总是可以 重新
从源文件 创建 (我们希望如此 )。相反 ,如果你创建 了一个
新文件,CVS update会用 “? 字符标记它,直到你告诉CVS
你想要 对它做 什么 。
要 把 文 件 增 加 到 项 目中 ,你 必 须 先 创 建 该 文 件 ,然
后使 用C v S a d d命 令来 为它做 上增 加标 记 。于是 ,下一
次对C v S C 0 m m i t的 调用 会把该 文件 增加 到仓库 中 。例
如 ,这 里演 示你 可以怎 样将R EADME文 件增加 到htt P C
项 目 中 :
$ Is
CVS Makefile httpc.c poll—server
$ vl README
. . . enter a description of httpc ...
httpc.c poll—server
CVS UI)date: Updating .
? READM E — CVS doesn’t know about this file yet.
$ CVS add README
CVS add:scheduling file ’READM E’for addition
CVS add:use CVS com mit’to add this file permanently
$ CVS update Now what does CVS think?
~VS update Updating .
A READM E — The file is marked for addition.
$ CVS com mit READM E
. . .
CVS prompts you for a log entry ...
RCS file:/u/jimb/cvs—class/rep/httpc/README,v
done
Checking in READM E l
/u/src/master/httpc/READM E.v <一一 README
lnitial revision:1.1
done
C V S以类似的方式 对待被删除的文件 。如果你删除一个
文件并随即运行cVS UPdate,CVS不会假定你想要删除该文
件 。相 反,它会采取 比较温 和的行为 一 通过最后记 录的内
容 重新 创建该文件 ,并用 “u ” 字符标记 它 ,就像对待任 何
其他 的更新一样 (这意 味着如果你想要 撤消你对 工作 目录中
的 文件所 做的 改动 ,你可 以简单地 删 除它 们,然 后让C V S
Update重新创建它们 )。
要 从项 目中移除 文件 ,你必须先 删除该文件 ,然 后使用
CVS rm命令为它做上删除标记。于是,下一次对cvs commit
的 调用会把 该文件 从仓库 中删除 。
提交通过CV S rm标记的文件不会销毁该文件的历史,它
只是增加一个新的修订 版 ,标记 为 “不存在” 。仓库还 有该
文件先前 内容 的记录 ,并 可在需要时恢 复它们一 例如 ,通
过cvs diff或CVS log。
有若干策 略可用于重 命名文件 ,最 简单的就是直接重命
名工作 目录 中的文件,并对旧名字运行cvS rm,对新名字运
行cVS add。该方法的缺点是 旧文件内容的 日志条 目不会传递
给 新文件 。其 他一些策略避 免 了这一 问题 ,但却有着其他更
为奇怪 的 问题 。
你可 以像 对待普通 文件那 样增加 目录 。
f编写良好的日志条目ll
如果我们可以使用CVS diff来取得改动的实际文本,为什么还
要费事地编写 日志条目呢?显然地 ,日志条目可以比补丁更短,
并允许读者获得对改动的全面了解,而无需深入它的细节。
但是 ,好 的 日志条 目应描 述开发者做 出改动的原 因。例
如 ,为上面 所示的修 订版1.7编 写的糟糕的 日志 条 目可能会
说 : “将t转换 为小写 。”这是准 确的 ,但 却完全没 有用 :
C V s diff提供同样的信息,而且更为清楚。更好的 日志条目
是 : “使该测试 对大小写 不敏 感。” 因为这样 其他人就会对
代码有大体的 了解,从而弄清楚其 目的 :H TT P客户应该在
解析 回复头时忽略 大小写 差异。
正如上面所提到的,CVS update命令将其他开发者所做的
改动并入你 的工作 目录 中。如果你和其 他开发者修改 了同一
文件,C V S将他们的改动和你的改动合并在一起。
当改动应 用于文件的 不同区域时 ,很 容易想 象这是怎样
工作的 。但当你和 另外的 开发者修改 了同一 行时 ,又会发生
什么呢? C V S称这种情况为冲突,并将它留给你来消除。
例如 ,假 设你刚刚给 主机名查找代码增 加了某种错误检
查。在提交你的改动前,你必须运行CVS update,以使你的源
保持 同步 :
$cvs update
CVS update: Ulxlating .
RCS file:/u/src/master/httpc/httpc.c,v
retrieving revision 1.8
retrieving revision 1.9
Merging differences between 1.8 and 1.9 into httpc c
rcsmerge:warning}conflicts during merge
CVS update: conflicts found in httpe.c
C httpc.c
在此 例中 ,另外的开 发者 已经改 动了你所改动的文件 的
同一区域 ,因而 C V S会抱怨有冲突。它不会像通 常那样打印
“M httpc.C ,而是打印 “C httpc.C”,以指示在该文件
中已经发生 了一处 冲突 。
为消除冲突 ,在你 的编辑器中打开该文件 。C V S这样标
记冲 突的文 本 :
/+1.ook up the IP address of the host.+/ (下转95页)
E
M
D
A
E
R
e
e
k
a
M
a
U
k
$ C $
维普资讯
•• 噩噩擅噩盟匾墨-
MOCNC
国
文件先前内容的记录,并可在需要对恢复它们→ 例如,通
过cvs dìff或cvs 1哩。
有若干策略可用于重命名文件,最简单的就是直接重命
名王作目录中的文件,并对 113名字运行c vs r m .对新名字运
add 。该方泣的缺点是旧文件内容的日志条目不会传递
给新文件。其他一些策略避免了这一问题,但却有着其他更
{彦改的文本文件 p
{增加和副除文件}
c V S I象对待其他改动-样对待文件创建和勤l除,它在文
行cvs
为奇怪的剖题。
你可在像对待普通文件那样增加目录。
{锺写良好的 E志条目}
如果我们可以使用cvs diff采取得改动的实际文卒,为什么还
件的历史中记录这样的事件。也就是说. c V S 记录目录以及
它们所包含的文件的历史。
CVS 没有假定新创建的文件应被置于它的控制之下,在
许多情况下这样的假定会出绪。例如,我们不需要记录对目
标文件和可执行文件的改动,因为它们的内容总是可以重新
从源文件创建(我的希望如此)。相反,如果你创建了二个
update会用蝇1" 字符标记它,直到你告诉CVS
要费事地编写日志条目呢?显然地,日志、条目可以比补了更短,
并允许读者获得对改动的全面了解,而无需深入它的细节。
但是,好的日志条自应描述开发者做出改动的原因。例
如,为上面所示的修订版 1 . 7 编写的糟糕的日志条自叮能会
说: 恤将 t 转换为小写这是准确的,但却完全没有用:
d ì ff 提供嗣样的信息,而且更为洁楚 e 更好的日志条目
是使该测试对大小写不敏感。"因为这样其他人就会对
代码有大体的了解,从而弄清楚其目的 HTTP 客户应该在
新文件. cvs
你想要对它做什么 o
要把文件增加到项目中,你必须先创建该文件,然
add 命令来为它做上增加标记。于是,下
co mm 川的词用会把该文件增加到仓库中。伊j
后使用 C γs
次对c ¥ s
cvs
知,这里演示你可以怎样将README 文件增加到 httpc
poll-server
解析回复头时忽略大小写差异。
i处理冲寞 (conflìct刊
正如上面所提到的, cγs update命令将其他开发者所做的
改动并入你的王作目录中。如果你和其他开发者修改了同一
文件. C V S 将他们的改动和你的改动合并在一起。
当改动应用于文件的不同区域肘,很容易想象这是怎样
王作的。但当你和另外的开发者修改了!可一行时,又会发生
什么呢? C V S 称这种情况为冲突,并将它留给你来消除。
例如,留设你同IJ 限给主机名查找代码增加7某种错误检
查。在提交你的改动前,你必须运行cvs
poll-server README Makefile
保持同步 z
$ cvs update
cvs up::iate: U到
RCS fíle: IU/STc/master/htt胆/
retrieving revIsion
retrieving revísíon 1 、 9
Merging diffe陀nces bet ween 1 > 8 and 1 、 9 into 刘
rcsmerge: warnîng: con f!icts dunng merge
cvs u iXiateζcon丑icts found in
C
在此例中,另外的开发者已经改动了你所改动的文件的
同一区域,因而CVS 会抱怨有冲突。它不会像通常那样打印
" .而是打印蝇 C " 以指示在该文件
update ,以使你的源
README
CVS 以类似的方式对待被翻除的文件。如果你删除一个
文件并随即运行cvs update. CVS不会假定你想要服除该文
件。根反,它会采取比较温和的行为 通过最后记录的内
容重新创建该文件,并用蝇U 霄字符标记它,就像对待任何
其他的更新一样(这意味着如果你想要撤消你对王作目录中
的文件所做的改动,你可以简单地鹰号除它们,然后让c v s
update 重新创建它们)。
要从项目中移徐文件,你必须先删除该文件,然后使用 "M
中已经发生了一处冲突。
为消除冲突,在你的编辑器中打开该文件。 CVS 这样标
项吕中:
$ Is
CVS Makefíle
$ VI README
enter a descriptíon o( httpc 、.喧
嚣 15
cvs
$ c\'s upda桂
cvs uP<iatc: Updating
句 README -- CVS doesn't know about this file ye仁
$ cvs add README
cvs add , scheduling file 'README' for addition
cvs add ~ use . cvs commit' to add t扫 lS file perrnanently
$ cvs update Now what do臼 CVS think?
cvs updatet Updaüng .
A README 一 τhe file Ís marked for addition.
$ cvs commit README
C飞IS prompts you for a log entry 、.
RCS file , lu/ji由b/c"\t--s-class/ rep/httpc/丑EADME , v
done
Checking in README.
/u/src/master/httpc/README , v <
mitial 陀VlsÌon:
donc
i己冲突的文本 z
1* σk up thεlP address of the host
CVS rm命令为它做上删除标记。于是,下一次对cvs comm泣
的词用会把该文件从仓库中剔除。
提交通过cvs rm 标记的文件不会销毁域文件的历史,它
只是增加一个新的修订版,标记为"不存在"。仓库还有该
{下转95页}.. /
一一
【rfReplaceAl1])l
URL : URL + &root=Northwind l
XM LDoc .Async : False}
XMLDoc.Load(URL);
Root : XMLDOC.DocumentElementl
Nodes : Root.SelectNod es( //row )}
index : 0}
ShowOneNode(Index)l
endl
在这里 ,我们把 C OmP anYN am e、C onta ctTitl e、
ContactName和Phone~ll从Customers表中选取出来,创建一个
节点列表。然后用ShowOneNode (代码段九)过程显示第一
个节 点 。
代码段九:ShowOneNode 过程
procedure TForm 1.ShowOneNode(1:Integer)}
vat
Node : lXM 1 DOMNod e。
begin
Node: Nodes{ll,
Edit1.Text:= Node Attributes[01.Text{
Edit2.Text:= Node.Attributesl11.Text}
Edit3.Text: Node.Attributes[2】.Text}
Edit4 Text:一 Node.Attributes[31.Textl
endl
四个SpeedButton的代码 (第一行、前一行、后一行、最
后一 行 )如 代码段十 所示 。
代码段十 :数据行 导肮代 码
/显示第一行
procedure TForm1.SimedButtonlClick(Sender:TObject)}
be gin
index ;一 0}
ShowOneNode(0)}
endl
(上接1O1页)
host info = gethostbyname (hostname)}
<<<<<<< httpc.C
if(!host—info)
{
fprintf(stderr,”%s:host not found:%s\n”,progname,hostname)
exit(1)}
printf(”httpc:no host”
exit(1)}
I ’
l
>>>>>>> I.9
sock=socket(PF 1NET.SOCK STREAM,O)}
重要的是要理 解cVs是怎样判断 冲突的 。cVs不理 解
程序 的 语 义 ,它 只 是 简 单地 把 它的 源 代 码 作 为 文 本 文件
树 来对 待 。如 果 一 个 开 发 者给 一个 函数 增 加 了一 个 新 的
参数 , 并修 正 了 它 的调 用者 ,而 另外 的 开 发 者 也 同时 增
加 了对 那个 函数 的 新 调 用 ,但 却 没有 传 递 新 参 数 ,那肯
// 显示前 一行
procedure TForm1.SpeedButton2Click(Sender:TObject
begin
if Index> 0 then Dec(index)}
ShowOneNode(1ndex)}
end
//显示后一行
procedure TForm 1.SpeedButton3Click(Sender:TObject
be gin
if Index < Nodes Length 1 then Inc(1ndex)I
Show0neNode(Index)
end‘
// 显示最 后 一行
procedure TForm1.SpeedButton4Click(Sender:TObject
begin
Index := Nodes .Length 1}
ShowOneNode(Nodes.Length 1)I
endl
结 语 一}一
本 部分 到 此结 束 。在这 部 分的 文章 里 ,我 们研 究 了如
何通过Microsoft SQL Server 2000的XML特性取得数据。
这 些特性 允 许我 们 不需 要使 用 任何 其它 数据 存取 组 件 ,只
需直接指定sQL查询 、通过HTTP协议与sQL Se rve r通
话 。我们还 创建 了几个例 子 ,演示 在 D e l P h i中如何利 用
Microsoft XML Document Object Model(DOM)显示
数 据 。
在下一部分 ,我 们将研究 另外两个 F O R X M L模式
(AUT0和EXPLI CIT)的数据查询技术 ,并演示如何利用
xML模板把DelPhi代码与基于xML的查询分离开来。一一
定 是一 个冲 突一 一 这两 处改 动是 不 一致 的一 一但 c V s并
不 会报 告该 冲 突 。c V s对 冲突 的理 解是 严 格的 文本 意 义
上 的 。
幸运的是 ,在 实践 中冲突很 少见。通常 ,它们似乎源 于
两个 开发者试 图解决 同一问题 、在 开发者之 间缺 乏交流 、或
是对程 序的设计意 见不合。以合理 的方式来给 开发者分配任
务可降低 冲突的可 能性 。
许 多版本控 制系统允许 开发者锁住 (1 o c k)文 件,以
防 止 其他 人对 其进 行改 动 ,直 到他提 交 了他的 改动 为止 。
虽然加 锁 在某 些情 况下 是 合适 的 ,但它 显然 不 是一 种更 好
的解 决方 案 。改 动通 常可 以被 正确 地 合并 ,而 开发 者有 时
会忘记 释 放锁 。而 在 这两 种情 况下 ,显式 的加 锁都 会导 致
不 必要 的 延迟 。
而且 ,加 锁 仅能防 止 文 本的 冲突 。如 果 两个 开发者 对
不同 文件 做 出改动 ,加 锁 并 不能防 止上 面 描述 的那 种语 义
的冲 突 。嚣
维普资讯
…IJ_司E回国醋-
UN--M{}
山田//显示前 1i
pr。但dure (Sender , TObJec t),
begin
íf Index > 0 !hen Dεc(!ndex沁
Sho飞"OneNodeOndex) ,
εnd 霉
11 显示后一行
procedure TForm 1 . Speed日 utton3Clíck(Sender , TOb)ecll ,
桂gin
lf lndex < Nodes. Length -1 !hen IncOndexl ,
ShowOneNode( lndex)
εnd.
URL URL ~ StríngReplò'二e(SQL , ' ',' '~"20' ,
!rfReplaccAllll ,
URL URL + '&root-Northwmd'.
False;
XMLDoc. LOdd(UR Ll,
Root 二 XMLDoc. DocumentElemeot 筝
Nodes ,-- R∞ I1凹Wl)f
lndεx - 0;
ShowOneNode( Index) ,
end 事
在这里,我们把CompanyName 、 ContactTìtl E' 、
ContactName和Phone列从Customers表中选取出来,创建一个
/!显示最后斗亏
procedurεTforml. SpeedButlon4Clíck(Sender , TObJec t),
begin
lnde豆、 Nod四.Length-I ,
ShowOneNode\Nodes. Lenglh -1) ,
结语 -i 一
本部分到此结束 e 在这部分的文章里,我们研究了如
何通过Microsoft SQL Server 2000的XML特性取得数据。
这些特性允许我们不需要使用任何其它数据存取组件,只
需直接指定SQL查询、通过HTTP协议与SQL Server通
end;
节点要1)衰。然后用ShowOneNode (代码段九)过程显示第二
活。我宫]还创建了几个例子,演示在 Delphi 中如何利用
Microsoft XML Document Object Model (DOM) 显示
数据。
XML 模式
(AUTO和EXP Ll CIT) 的数据查询技术,并演示如何利用
XML模板把Delphí代码与基于XML的查询分离开来。::1
在下一部分,我们将研究另外两个FOR
个节点 e
代码段九 ShowOneNode
procεdure TForm 1. ShowOneNode(1
飞-ar
Node , IX 主臼de ,
begin
Node ,- Nodes{lh
Edit \. Teμ: 二 Nodε. Altrít】 utes!O].τC'xt;
Ed1l2.τe五,~ Node. l\ttributesll]. Text 事
Edít3. Text Node ‘ Atlrí扭ltes[ 2]. Text,
Edlt4τext . Node. ,1\tlnbutes!刻,τext ,
end 雹
四个SpeedButton的代码(第一行、前一行、后→行、最
后→行)如代码段十所示。
代码段十:数据行导航代码
主革示第一行
procedure \Sender, TObject),
begm
index - 0;
ShowOne只ode(O)事
end;
过程
Integer) 害
定是一个冲突← 这两处改动是不一致的一一但 CVS 并
不会报告该冲突。 CVS 对冲突的理解是严格的文本意义
上的。
幸运的是,在实践中冲突很少见。通常,它们似乎源于
两个开发者试图解决同一问题、在开发者之闽缺乏交流、或
是对程序的设计意见不合。以合理的方式来给开发者分配任
务可降低冲突的可能性。
许多版本控制系统允许开发者锁住(J ock) 文件,以
防止其他人对其进行改动,直到他提交了他的改动为止。
虽然加锁在某些情况下是合适的,但它显然不是一种更好
的解决方案 e 改动通常可以被正确地合并,而开发者有时
会忘记释放锁。而在这两种情况下,显式的加镇都会导致
{土篝101 页}
host mfo gethostbyname (hostname).
<<<<<<<
íf (!柱。sUnfo)
fprin tf (stderr,叫悼 host not found ,纱;s\n!!. progname. hostna田功,
exit (1),
工二--工二z
íf (! 坦因Cinfo)
printf (nhtt~! no host!!h
exit (1),
>>>>>>> 1. 9
不必要的延迟。
而且,加锁仅能防止文本的冲突。如果两个开发者对
不同文件做出改动,加锁并不能防止上面描述的那种语义
的冲突 o ::1
茸茸k socket (PLINET , . 0) ,
重要的是要理解CVS是怎样判断冲突的。 CVS不理解
程序的语义,它只是简单地把它的源代码作为文本文件
树来对待。如果一个开发者给一个函数增加了一个新的
参数,并修正了它的词用者,而另外的开发省也同时增
加了对那个函数的新调用,但却没有传递新参数,那肯