作者:Al Dev (Alavoor Vasudevan) alavoor@yahoo.com
	譯者:Cyril Huang cyril_huang@yahoo.com
	v7.0, 20 Feb 2000 翻譯日期: 5 April 2000
	--------------------------------------------------------------------------------
	這份文件是一份 "實(shí)際操作的說(shuō)明" ,以便於能使您很快的設(shè)定 CVS/RCS 原始程式碼控制系統(tǒng)。這份文件里也有一些將 CVS 上常用的混合命令包成可設(shè)定的 shell scripts 。這些 scripts 為 CVS 提供了一個(gè)簡(jiǎn)單的使用者介面。這份文件的內(nèi)容不僅能適用於 Linux 系統(tǒng)也適用於其他像 Unix 的系統(tǒng),例如Solaris, HPUX, AIX, SCO, Sinix, BSD, SCO 等等。
	--------------------------------------------------------------------------------
	--------------------------------------------------------------------------------
1. 簡(jiǎn)介
原始碼控制系統(tǒng)是一個(gè)必須能管理那些在軟體計(jì)劃發(fā)展時(shí)原始碼所做的改變。軟體開發(fā)者需要一個(gè)完整的原始碼改變歷史紀(jì)錄,以便於在發(fā)生問(wèn)題時(shí),能夠追溯到以前穩(wěn)定的版本。既然原始程式碼對(duì)於任何的軟體計(jì)劃與開發(fā),都是花時(shí)間與金錢中關(guān)鍵的部分,所以花時(shí)間藉由使用原始碼控制系統(tǒng)像 CVS 和 RCS 來(lái)安全的保護(hù)(safe-guarding)原始程式碼是非常重要的。
CVS (Concurrent Version Control System) 是一個(gè)能讓很多程式開發(fā)者同時(shí)做軟體開發(fā)的非常強(qiáng)大工具。它使用了RCS 的檔案規(guī)定格式但多了一層像應(yīng)用程式介面的包裝,架在 RCS 的上層。
(譯注: RCS 是較老的版本控制,一個(gè)受 RCS 管制的檔案看起來(lái)是這樣子的 proj1.c,v ,CVS 沿用了一些 RCS 的規(guī)定。)
CVS 能夠紀(jì)錄你的檔案的歷史紀(jì)錄( 通常是原始程式碼,但是其他型態(tài)的檔案則不一定)。 CVS 只存了不同版本中檔案的差異,而不是你所建立的每個(gè)版本中的每個(gè)檔案。 CVS 也保持了一個(gè)何時(shí),何人更改檔案,為什麼更改檔案等等不同觀點(diǎn)的歷史紀(jì)錄。
CVS 對(duì)於軟體的發(fā)行和多人同時(shí)更改目前原始碼的管理是非常的有幫助。他并不只是要對(duì)單一目錄下的檔案提供版本控制,相反的,CVS 更提供了多層有組織的目錄檔案的版本控制。在這個(gè)目錄下除了你的原始程式碼外,還包含有一個(gè) CVS 所建立的改版控制目錄與檔案。
這些目錄與檔案後被合并在一起形成一個(gè)軟體的發(fā)行。
CVS 能被使用在 "C", "C++", Java, Perl, HTML 和其他檔案。
	--------------------------------------------------------------------------------
	--------------------------------------------------------------------------------
2. 那一種版本控制系統(tǒng)適合我? CVS 或 RCS
CVS 實(shí)際上是架在 RCS 之上的, CVS 只是一堆更強(qiáng)大能控制一個(gè)有完整原始程式碼階層目錄的工具。我們非常強(qiáng)烈的推薦您使用 CVS,因?yàn)槟軌蚝苡袕椥缘挠?perl , korn bash shell 等 scripts 語(yǔ)言設(shè)定您自己的 CVS 系統(tǒng)。請(qǐng)看一些 korn shell scripts 的□例 Shell Scripts 。
CVS 的優(yōu)點(diǎn)
CVS 是非集中式的管理,使用者從儲(chǔ)存柜 (repository) 登出一個(gè)檔案目錄,并且有他自己的獨(dú)立的穩(wěn)定目錄樹。
	CVS 能夠在發(fā)行整個(gè)計(jì)劃的原始目錄樹中"蓋上印記" ("STAMP")。
	CVS 能夠使大家同時(shí)修改檔案。
	CVS 能夠用 shell scripts 或 perl 設(shè)定成檔案鎖住成單一使用或同時(shí)修改檔案模式。
	CVS 的缺點(diǎn)
	需要比 RCS 多一點(diǎn)的管理。
	非常成熟復(fù)雜的系統(tǒng),是目前應(yīng)用上已有的高技術(shù)。(感謝網(wǎng)友tsaipaw@mars.seed.net.tw來(lái)信指證我的錯(cuò)誤)
	有豐富的命令還有命令選項(xiàng),因此對(duì)於初學(xué)者來(lái)說(shuō)有很陡的學(xué)習(xí)曲線。簡(jiǎn)單使用的 shell scripts 可在這里找到 Shell Scripts 。
	RCS 的優(yōu)點(diǎn)
	RCS 非常容易設(shè)定。較少一些管理上的工作。
	RCS 用在一個(gè)每個(gè)人在一起工作的集中區(qū)域。
	RCS 對(duì)於簡(jiǎn)單的系統(tǒng)很有用。
	非常嚴(yán)謹(jǐn)?shù)膯我粰n案修改模式 - 同步與同時(shí)是不允許的。
	RCS 的缺失
	由於使用單一目錄控制與檔案鎖住,不可能由很多的程式設(shè)計(jì)者做同時(shí)的開發(fā)。因?yàn)閱我荒夸浵潞芏嗳藢?duì)檔案的改變,會(huì)造成 make 的使用錯(cuò)誤。
	不能對(duì)整個(gè)軟體計(jì)劃戳上發(fā)行(releases)的印記。
	這份文件也包含一些 shell scripts 以提供簡(jiǎn)單的命令來(lái)作登出 (check-out), 登錄 (check-in), 送交(commit) 檔案的動(dòng)作。請(qǐng)看一些 shell scripts 的□例 Shell Scripts 。
	對(duì)於 RCS 而言,請(qǐng)看 Linux CD-ROM 里面的RCS mini-howto。
	--------------------------------------------------------------------------------
	cd /mnt/cdrom/Redhat/RPMS
	ls -l howto-6.0-*.noarch.rpm
	rpm -qpl howto-6* | grep -i rcs
	--------------------------------------------------------------------------------
	或者看 http://sunsite.unc.edu/LDP/HOWTO/mini/RCS-HOWTO.html
	--------------------------------------------------------------------------------
	--------------------------------------------------------------------------------
3. 設(shè)定 CVS
	首先,你需要安裝 CVS 套件,在Redhat Linux 上請(qǐng)用
	--------------------------------------------------------------------------------
	cd /mnt/cdrom/Redhat/RPMS
	rpm -i rcs*.rpm
	rpm -i cvs*.rpm
	To see the list of files installed do -
	rpm -qpl cvs*.rpm | less
	--------------------------------------------------------------------------------
	然後用 j, k, CTRL+f, CTRL+D, CTRL+B, CTRL+U 或上下左右鍵, page up/down 瀏覽一下結(jié)果。請(qǐng)用 'man less' 查看 less 的用法
	在其他的 unix 機(jī)器上,你可能需要下載 RCS CVS 的 tar.gz 檔案,然後根據(jù) README, INSTALL 檔的指示來(lái)安裝 CVS。請(qǐng)到 http://www.cyclic.com 和 http://www.loria.fr/~molli/cvs-index.html
3.1 CVS 的專有環(huán)境變數(shù)
下列的環(huán)境變數(shù)需要在 /etc/profile 檔中設(shè)定,/etc/profile 是對(duì)所有使用者都有效的內(nèi)定值設(shè)定檔,如果沒(méi)有設(shè)定 /etc/profile,那麼你應(yīng)該加這些設(shè)定到你自己的設(shè)定檔 /.bash_profile 內(nèi)。
	--------------------------------------------------------------------------------
	export EDITOR=/bin/vi
	export CVSROOT=/home/cvsroot
	export CVSREAD=yes
	--------------------------------------------------------------------------------
建造一個(gè)目錄來(lái)存你原始程式碼的儲(chǔ)藏柜 (repository) 并且給予 unix group 與 user 讀寫的權(quán)力。 (譯注:這個(gè)目錄下將會(huì)有很多你將來(lái)的原始碼。)
	--------------------------------------------------------------------------------
	export CVSROOT=/home/cvsroot
	mkdir $CVSROOT
	chmod o-rwx $CVSROOT
	chmod ug+rwx $CVSROOT
	--------------------------------------------------------------------------------
	要初始化你的 CVS ,并且從現(xiàn)在開始把你的原始程式碼交給 CVS 管理。請(qǐng)做 -
	--------------------------------------------------------------------------------
cvs init
	(譯注;這個(gè)初始化的動(dòng)作在於建造一個(gè)儲(chǔ)藏柜,是一個(gè)目錄$CVSROOT/。
	同時(shí) $CVSROOT/CVSROOT 也在此時(shí)被建造,這個(gè)模組目錄下面是控制你CVS的administration files,
	里面的檔案做一些修改後,可以使CVS更強(qiáng)大好用。
	$CVSROOT 下的目錄每個(gè)都是 module 的意思,一個(gè) module 可以是一個(gè)專案計(jì)劃。
	但也可能是你把一個(gè)計(jì)劃拆成很多 module ,不同 module 交給不同的 team 去發(fā)展。)
	# 一定要換到想要 CVS 控制的計(jì)劃目錄下喔
	cd $HOME/my_source_code_dir
	# 把整個(gè)目錄納入管理用 import 命令
	cvs import my_source_code_dir V1_0 R1_0 
	(譯注:其實(shí)是 cd 到你的project下後,cvs import 模組 vendor_tag release_tag,
	不一定要是目錄名稱 my_source_code_dir,vendor_tag, release_tag 只是識(shí)別用的東西,
	將來(lái)你可以用 tag 來(lái)存取你要的特定版本
	這個(gè)動(dòng)作會(huì)在 $CVSROOT/ 下開個(gè)" 模組 "的目錄,然後把 my_source_code_dir 整個(gè)放到 CVS 下管理,
	$HOME/my_source_code_dir 沒(méi)用了。import 的動(dòng)作是把已經(jīng)寫好的一堆 code 擺進(jìn)來(lái),
	如果將來(lái)想新增檔案xxxx.c,必須先寫好xxxx.c,再用 cvs add xxxx.c)
--------------------------------------------------------------------------------
3.2 從 RCS 轉(zhuǎn)換到 CVS 系統(tǒng)
要轉(zhuǎn)換已經(jīng)存在的 RCS 檔案到 CVS ,請(qǐng)使用下面的 script 。并確定你從你的 Linux CD-ROM 安裝了 korn shell 套件 pdksh*.rpm。
注意 : Korn shell /bin/ksh 在你從Linux CD-ROM 安裝 pdksh*.rpm 時(shí)會(huì)產(chǎn)生
--------------------------------------------------------------------------------
#!/bin/ksh
	#############################################################
	# Program to Migrate the existing source code in RCS to CVS
	#
	# Needs the korn shell RPM package  pdksh*.rpm from Linux
	# contrib cdrom
	#############################################################
	#
	# rcs2cvs - convert source tree from RCS to CVS
	#
	# project to convert
	PROJECT='project'
	# current RCS root
	RCSROOT="$HOME/rcs"
	if cd "$RCSROOT/$PROJECT"
	then
	        cd "$RCSROOT"
	else
	        echo >&2 "`basename "$0"`: can't change to RCS directory '$RCSROOT/$PROJECT'."
	        exit 1
	fi
	# current CVS root
	CVSROOT="$HOME/cvs"
	# create new CVS directory for project 'project'
	if mkdir "$CVSROOT/$PROJECT"
	then
	        :
	else
	        echo >&2 "`basename "$0"`: can't create CVS directory '$CVSROOT/$PROJECT'."
	        exit 2
	fi
	# create CVS project tree from RCS tree
	find "$PROJECT" -type d -name RCS -print |
	while read RCS
	do
	        CVS="`dirname "$RCS"`"
	        (if cd "$RCS"
	        then
	#               if find . -type f -name '*,v' -print | cpio -pdmv "$CVSROOT/$CVS"
	                if find . -type f -print | cpio -pdmv "$CVSROOT/$CVS"
	                then
	                        :
	                else
	                        echo >&2 "`basename "$0"`: can't convert RCS subdirectory '$RCSROOT/$RCS' to CVS subdirectory '$CVSROOT/$CVS'."
	                fi
	        else
	                echo >&2 "`basename "$0"`: can't change to RCS subdirectory '$RCSROOT/$RCS'."
	        fi)
	done
--------------------------------------------------------------------------------
	現(xiàn)在 RCS 已經(jīng)被改成 CVS 系統(tǒng)下的 'project'. 你可以開始用CVS命令來(lái)存取 'project' 這個(gè)模組了.
	--------------------------------------------------------------------------------
	--------------------------------------------------------------------------------
4. Shell Scripts
下面的 scripts 是基本 CVS 命令的集合,而且是 Korn shell 的 scripts 。你可以把他轉(zhuǎn)成 perl 或者 bash。你可以自己修改成你想要的樣子。這些只是運(yùn)用基本 CVS 命令但有些特殊的花樣加在里面。例如, sedit 這個(gè) script 提供了檔案鎖住的功能使得其他人知道有某人正在修改這個(gè)檔案,當(dāng)然你也可以直接使用 CVS 命令而不用這些 scripts ,這些 scripts 只是在展示 CVS 是多麼的有彈性。
把這些 scripts 復(fù)制到 /usr/local/bin 下,并且此目錄應(yīng)該在你的 PATH 環(huán)境變數(shù)中。
	sget [-r revision_number]  要從 CVS 獲得一個(gè)唯讀檔案或整個(gè)唯讀目錄,請(qǐng)按 sget
	sedit [-r revision_number]  要修改一個(gè)一個(gè)程式碼時(shí),這個(gè) scripts 會(huì)做檔案鎖住的動(dòng)作,因此沒(méi)有別人可以登出這個(gè)檔案了。當(dāng)然你可以改變這個(gè) script 成你想要的功能 - 例如不鎖住,只出現(xiàn)警告訊息,或者相反的,非常嚴(yán)謹(jǐn)?shù)逆i檔案。請(qǐng)按 sedit
	scommit [-r revision_number]  要交出某個(gè)你修改的檔案或整個(gè)目錄。 把你的改變交給 CVS。請(qǐng)按 scommit
	supdate  要藉由從 CVS 得到新的檔案來(lái)update 一個(gè)檔案或整個(gè)目錄。請(qǐng)按 supdate
	sunlock [-r revision_number]  要把因?yàn)橛?sedit 後的檔案鎖關(guān)掉。這會(huì)釋放檔案鎖(Release File Lock)。請(qǐng)按 sunlock
	slist 要看目前正被你修改的檔案列表。做 'ls -l | grep | ...' 命令,請(qǐng)按 slist
	sinfo  要得到一個(gè)檔案的改版資訊。 請(qǐng)按 sinfo
	slog  要得到一個(gè) CVS 檔案改版的歷史紀(jì)錄,請(qǐng)按 slog
	sdif 
	sdif -r rev1 -r rev2  要得到你的檔案與 CVS 柜子里的檔案不同的地方在哪里。請(qǐng)按 sdif
注意: sdif 只有一個(gè) 'f' ,因?yàn)檫@里已經(jīng)有一個(gè) unix 命令叫 'sdiff'。
	sadd  要新增一個(gè)檔案到 CVS 柜子里。請(qǐng)按 sadd
	sdelete  要從 CVS 柜子里清掉一個(gè)檔案。請(qǐng)按 sdelete
	sfreeze   要凍結(jié)原始碼 (freeze codes) ,這是將要發(fā)行 (release) 整個(gè)原始碼目錄樹。請(qǐng)按 sfreeze
	saddtree   要新增一個(gè)目錄樹到 CVS 。請(qǐng)按 saddtree
	例如 :
	--------------------------------------------------------------------------------
	        cd $HOME;  
	        sfreeze REVISION_1_0  srctree 
	--------------------------------------------------------------------------------
	這將會(huì)凍結(jié)原始碼,并貼上一個(gè)標(biāo)簽 REVISION_1_0 ,如此一來(lái)你可以稍後用版本名字登出整個(gè)目錄樹。
******************************************************
4.1 sget
注意 : Korn shell /bin/ksh 在你從Linux CD-ROM 安裝 pdksh*.rpm 時(shí)會(huì)產(chǎn)生
	請(qǐng)把他存成一般文字檔并改變存取權(quán)限 chmod a+rx
	--------------------------------------------------------------------------------
#!/bin/ksh
	# CVS program sget
	# Program to check out the file from CVS read-only
cmdname=`basename $0`
	Usage()
	{
	        print " Usage: $cmdname [-r revision_number/symbolic_tag_name]  "
	        print "The options -r are optional "
	        print "For example - "
	        print " $cmdname -r 1.1 foo.cpp"
	        print " $cmdname foo.cpp "
	        print " $cmdname some_directory "
	        print "Extract by symbolic revision tag like - "
	        print " $cmdname -r REVISION_1 some_directory "
	        print " "
	        exit
	}
	# Command getopt will not supported in next major release.
	# Use getopts instead.
	while getopts r: ii
	do
	        case $ii in
	        r) FLAG1=$ii; OARG1="$OPTARG";;
	        ?) Usage; exit 2;;
	        esac
	done
	shift ` expr $OPTIND - 1 `
#echo FLAG1 = $FLAG1 , OARG1 = $OARG1
	if [ $# -lt 1 ]; then
	        Usage
	fi
bkextn=sget_bak
	hme=` echo $HOME | cut -f1 -d' '  `
	if [ "$hme" = "" ]; then
	        print " Error: $HOME is not set!! "
	        exit
	fi
	# Check if file already exists....
	if [ -f $1 ]; then
	        user_perms=" "
	        group_perms=" "
	        other_perms=" "
	        user_perms=`ls -l $1 | awk '{print $1 }' | cut -b3-3 `
	        group_perms=`ls -l $1 | awk '{print $1 }' | cut -b6-6 `
	        other_perms=`ls -l $1 | awk '{print $1 }' | cut -b9-9 `
	        if [ "$user_perms" = "w" -o "$group_perms" = "w" 
	                -o "$other_perms" = "w" ]; then
	                print " Error: The file is writable. Aborting $cmdname ......"
	                print "       You should either backup, scommit or delete the file and"
	                print "       try $cmdname again "
	                exit
	        fi
	fi
	cur_dir=`pwd`
	#echo $cur_dir
	len=${#hme}
	len=$(($len + 2))
	#echo $len
	subdir=` echo $cur_dir | cut -b $len-2000 `
	#echo $subdir
	if [ "$subdir" = "" ]; then
	        fdname=$1
	else
	        fdname=$subdir"/"$1
	fi
	# Move the file
	touch $1 2>/dev/null
	mv -f $1 $1.$bkextn
	# Create subshell
	(
	cd $hme
	#echo $fdname
	# Use -A option to clear all sticky flags
	if [ "$FLAG1" = "" ]; then
	        cvs -r checkout -A $fdname
	else
	        cvs -r checkout -A -$FLAG1 $OARG1 $fdname
	fi
	)
	#pwd
	if [ -f $1 ]; then
	        print " READ-ONLY copy of the file $fdname obtained."
	        print "Done $cmdname"
	        #print " Tip (Usage): $cmdname  "
	fi
--------------------------------------------------------------------------------