天天看點

CVS使用手冊:手把手教你使用CVS用戶端進行項目開發

CVS使用手冊:手把手教你使用CVS用戶端進行項目開發

============================================ 

時間:2007-04-12

作者:飄揚

首發:飄揚部落格

網址:http://piaoyang.org

Q Q: 47720194

郵箱:piaoyang168#163.com  

版權聲明:可以任意轉載,轉載時請務必标明原始出處和作者資訊

============================================ 

摘要:本文詳細介紹了CVS的基本入門知識以及cvs用戶端的配置過程,并由淺入深的描述了如何應用CVS進行系統開發或其它文檔的版本管理與控制,并對CVS的常見問題進行了分析與解答,可以說是一篇CVS入門、學習與提高的完美教程。通過本教程你可以在最短的時間裡面最快的掌握CVS的所有内容,并用CVS來進行項目的開發。如有不明之處,請聯系飄揚,你可以到我的部落格(飄揚部落格)http://www.piaoyang.org 給我留言或QQ47720194,我會盡力解答你的問題。

*************************************

内容概覽:

一、CVS入門知識詳解

        1、什麼是 CVS?

        2、CVS基本概念

二、CVS用戶端的安裝和配置

        1、連接配接CVS伺服器

        2、登入CVS伺服器

        3、拷貝相應代碼準備開發

三、如何用CVS來開發項目

        1、更新源碼

        2、送出修改後的檔案

        3、檢視日志

        4、将檔案添加到資源庫 

        5、将目錄添加到資源庫 

        6、删除檔案或目錄 

        7、标簽的定義和使用

        8、備份源碼倉庫 

        9、分支的定義和使用

四、CVS常見問題分析

        1、cvs使用者為什麼不能登入?

        2、我怎麼知道别人改了哪些内容?

        3、什麼是沖突?怎麼解決?

五、結束語

*************************************

由于文章涵蓋的内容很多,飄揚建議對于不同需求的朋友請各取所需。如果你是一位CVS初學者,那麼請你多花點時間通讀本文,他将幫你完全掌握CVS;如果你想了解CVS的開發過程,你可以讀本文的第二部分《三、如何用CVS來開發項目》;最後,如果你是因為在使用CVS而遇到問題搜尋到本文時,那麼請你讀《四、CVS常見問題分析》吧,當然你也可以直接和飄揚聯系,你可以去我的部落格http://www.piaoyang.org  (飄揚部落格)給我留言或QQ47720194,我會及時看到你的問題的。好啦,下面開始和飄揚一起學習CVS吧!

一、CVS入門知識詳解

1、    什麼是 CVS? 

CVS 是一種客戶機/伺服器系統,類似于microsoft SourceSafe,可以讓開發人員将他們的項目存儲在稱為資源庫的中央位置。使用 cvs 客戶機工具,開發人員可以對資源庫的内容進行更改。CVS 資源庫會依次記錄對每個檔案所做的每個更改,并建立一個完整的項目開發進展曆史。開發人員可以請求特定源檔案的舊版本、檢視更改日志,并根據需要執行其它一些有用的任務。所有重要的免費軟體項目都使用 CVS 作為其程式員之間的中心點,以便能夠綜合各程式員的改進和更改。這些項目包括: Gnome, KDE, The GIMP, Wine, 等等。

CVS 的基本工作思路是這樣的:在一台伺服器上建立一個倉庫,倉庫裡可以存放許多不同項目的源程式。由倉庫管理者統一管理這些源程式.這樣,就好象隻有一個人在修改檔案一樣.避免了沖突.每個使用者在使用倉庫之前,首先要把倉庫裡的項目檔案下載下傳到本地。使用者做的任何修改首先都是在本地進行,然後用 cvs 指令進行送出,由 cvs 倉庫管理者統一 修改.這樣就可以做到跟蹤檔案變化,沖突控制等等. 

CVS可以協助一組人共同開發一個工程。如果你是一個項目中的一組成員之一,CVS也能夠幫助你。除非你特别仔細,你很容易覆寫其他人的工作。一些編輯器,例如GNUEmacs,試圖去判定一個檔案是否被兩人同時修改。不幸的是,如果一個人使用其它的編輯器時,這個安全方式将不再有效。CVS使用讓不同開發者獨立工作的方式解決了這個問題。每一個開發者的工作都在他自己的目錄内,并且CVS将 在每個開發者的工作完成後進行合并工作。

由于 CVS 是典型的 C/S 結構的軟體,是以它也分成伺服器端和用戶端兩部分。不過大多數CVS 軟體都把它們合二為一了。我們這裡就分别從伺服器和用戶端的角度讨論cvs的使用。

2、    CVS基本概念

Repository(倉庫)

CVS的倉庫存儲全部的版本控制下的檔案copy,通常不容許直接通路,隻能通過cvs指令,獲得一份本地copy,改動後再check in(commit)回倉庫。而倉庫通常為與工作目錄分離的。CVS通過多種方式通路倉庫。每種方法有不同目錄表示形式。資料如何存放在repository中:随着CVS版本的不同,存放結構會發生變化,一般情況下使用者無需了解資料到底是如何存放的。

Revision(修訂版)

每一個file的各個revision都不相同,形如1.1, 1.2.1,一般1.1是該檔案的第一個revision,後面的一個将自動增加最右面的一個整數,比如1.2, 1.3, 1.4...有時候會出現1.3.2.2,原因見後。revision總是偶數個數字。一般情況下将revision看作時CVS自己内部的一個編号,而tag則可以标志使用者的特定資訊。

Tag(标簽)

用符号化的表示方法标志檔案特定revision的資訊。通常不需要對某一個孤立的檔案作tag,而是對所有檔案同時作一個tag,以後使用者可以僅向特定tag的檔案送出或者checkout。另外一個作用是在釋出軟體的時候表示哪些檔案及其哪個版本是可用的;各檔案不同revision可以包括在一個tag中。如果命名一個已存在的tag預設将不會覆寫原來的;

Branch(分支)

當使用者修改一個branch時不會對另外的branch産生任何影響。可以在适當的時候通過合并的方法将兩個版本合起來;branch總是在目前revision後面加上一個偶數整數(從2開始,到0結束),是以branch總是奇數個數字,比如1.2後面branch為1.2.2,該分支下revision可能為1.2.2.1,1.2.2.2,...

Conflict(沖突)

完全是純文字的沖突,不包含邏輯上的沖突,比如CVS不能解決如下問題:某人修改了函數f的參數,而另外一個人在另外一個地方用老的參數調用該函數。文本沖突需要使用者自己參與解決,CVS無法自動解決。(如還有不明之處,請到飄揚部落格 http://www.piaoyang.org 給我留言或QQ47720194,我會盡力解答你的問題。)

Module(子產品):CVS伺服器根目錄下的第一級子目錄。通常用于存放一個項目的所有檔案

Check out(檢出):通常指将倉庫中的一整個子產品首次導出到本地。

Check in(導入)::通常指通過送出整個目錄結構并建立一個新的子產品。

Release(發行版本):整個産品的版本

Update(更新):從子產品中下載下傳其他人的修改過的檔案。更新本地的拷貝。

Commit(送出):将自己修改過的檔案送出到子產品中。

二、CVS用戶端的安裝和配置

在RedHat Linux 7.1之後的版本上,都有自動安裝CVS伺服器軟體,隻要對該軟體進行設定,即可使用。是以這裡不再對CVS用戶端的安裝一一贅述。此次配置我用的是虛拟機VMware環境下的linux redhat9.0,下面是具體的配置過程。

為了連接配接到 CVS 資源庫,您需要知道稱為 "CVSROOT" 的路徑。CVSROOT 是一個字元串,就象 URL,它告訴 CVS使用者遠端資源庫在哪裡,以及如何連接配接它。不僅如此,根據 CVS 資源庫是本地的還是遠端的,以及連接配接到它的不同方式,CVS 還有許多不同的 CVSROOT 格式。

1、連接配接CVS伺服器

當然你必須擁有cvs使用者帳号和密碼,以及相關權限。在Linux環境下:

1) 本地 CVSROOT 

    # export CVSROOT=/usr/local/cvsroot (告訴CVS用戶端,存放代碼的資源庫在什麼地方)

    一個實際的例子,在版本管理者将代碼庫存放在192.168.0.36 的機器上,路徑為:

    /usr/local/cvsroot(如果你telnet 到 192.168.0.36 的機器上的時候,此時你就是在本機)

2) 遠端密碼伺服器 CVSROOT

    # export CVSROOT=:pserver:[email protected]:/usr/local/cvsroot(注意冒号不能漏掉,句子中間沒有空格)

    pserver 是CVS内部的一種傳輸協定。

    piaoyang 是使用者名,

    192.168.0.36 機器的IP或名字(如果有DNS才能用名字)

    /usr/local/cvsroot 是代碼庫的具體路徑

(注意:如果你不想每次敲這麼多枯燥的指令,你可以在你登陸的時候讓系統作這些事情,具體的作法是修改你登入的環境變量。現在用piaoyang作為例子,在檔案/home/piaoyang/.bash_profile後面加入

 export CVSROOT=/home/cvsroot 或export CVSROOT=:pserver:[email protected]:/home/cvsroot 

并在根目錄下運作:. ./.bash_profile)

2、登入CVS伺服器

 # cvs login 

(Logging in to [email protected]) 

CVS password:(在此輸入密碼)

3、拷貝相應代碼準備開發

如輸入: # cvs -z5 checkout VPN 

此指令告訴 CVS 用戶端使用 gzip 壓縮級 5 ("-z5") 在慢速連接配接上加快傳輸速度,來檢出 ("checkout")VPN子產品。 

一旦檢出指令完成,将在包含最新源碼的目前工作目錄中看到 "VPN" 目錄。還會注意到每個子目錄下都有一個"CVS"目錄 -- CVS 在這些目錄中存儲帳戶資訊,可以放心地忽略它們。(如還有不明之處,請到飄揚部落格 http://www.piaoyang.org 給我留言或QQ47720194,我會盡力解答你的問題。)一旦檢出結束,使用者就無需擔心是否設定了 CVSROOT 環境變量,也無需再在指令行上指定它,因為現在所有額外的 "CVS" 目錄裡都有它的緩存。 

記住:隻需要為初始登入和檢出設定 CVSROOT。至此你就可以在剛才check out的VPN子產品上進行修改或開發了

至于怎樣用CVS來開發源代碼請參考本文《三、如何用CVS來開發項目》部分。

三、如何用CVS來開發項目

經過上面的check out之後,現在已經有了源碼,我們可以繼續編譯和安裝它們、檢查它們,或者對它們執行任何操作了。現在,來看一下作為一個開發人員如何與 CVS 互動。 

作為一名開發人員,您需要修改 CVS 上的檔案。要修改檔案,隻需要對資源庫的本地副本進行适當的更改。在您明确地告訴 cvs "送出"更改之前,您對源碼做的更改不會應用到遠端資源庫。測試過所有修改以確定它們可以正常運作之後,就可以準備将這些更改後的檔案運用到資源庫中,遵循下面的兩個步驟。

1、更新源碼

在送出之前需要将已檢(checkout)出的源目錄與 CVS 上的目前版本保持同步。為了做到這一點,您無需再次登入到 pserver;cvs 會将您的認證資訊緩存到那些 "CVS"帳戶目錄中。首先,進入主檢出目錄(在這裡是 "VPN"),然後輸入: 

# cvs update -dP 

如果有任何新檔案或者其他人對檔案有過更改,cvs就會在更新每一行的時候輸出 "U [path]" 行。另外,如果本地編譯了源碼,您有可能會看到許多 "? [path]" 行;cvs 指出這些目标檔案不來自于遠端資源庫。 (另外,請注意我們用于 "cvs update" 的兩個指令行選項。"-d" 告訴 cvs 建立可能已添加到資源庫的新目錄(預設情況下,這不會發生),"-P" 告訴 cvs 從本地已檢出的源碼副本中除去所有空目錄。"-P" 是個不錯的選擇,因為 cvs 傾向于收集許多随時間産生的空(曾經使用過,但現在已經放棄)目錄樹。)

2、送出修改後的檔案 

我們假設在輸入 "cvs update -dP" 後沒有沖突 -- 通常都沒有沖突。由于沒有沖突,本地源碼是最新的。可以在主源碼目錄中輸入以下指令來送出對資源庫的更改: 

# cvs commit 

3、檢視日志 

要檢視某個特定檔案完整的曆史以及送出時開發人員(包括您)所加的注解是很容易的。要檢視這些資訊,輸入: 

# cvs log myfile.c 

"cvs log" 指令是遞歸的,是以如果您想檢視整個目錄樹的完整日志,隻需要進入該目錄,輸入: 

# cvs log | less 

4、将檔案添加到資源庫 

要将源檔案添加到 CVS 很容易。首先,用您喜愛的文本編輯器建立該檔案。然後,輸入以下指令: 

# cvs add myfile.c 

cvs server: use cvs commit to add this file permanently 

這将告訴 cvs 在您下次執行 "cvs commit" 時,将該檔案添加到資源庫。在那之前,其它開發人員看不它。 

5、将目錄添加到資源庫 

将目錄添加到 CVS 的過程類似于mkdir foo指令:

# cvs add foo 

Directory /home/cvsroot/mycode/foo added to the repository 

與添加檔案不同,當您添加目錄時,它會立即出現在資源庫中;不需要 cvs commit。将本地目錄添加到 cvs 後,您會注意到在遠端cvs伺服器的對應目錄中建立了一個 "CVS" 目錄,它作為包含 cvs 帳戶資料的容器。因而,您隻要看一下其中是否有 "CVS" 目錄,就可以很容易地知道某個目錄是否已添加到遠端cvs伺服器的 cvs中了 

在将檔案或目錄添加到資源庫之前,您必須確定它的父目錄已經添加到 CVS。否則,您會看到類似于下面的錯誤: 

# cvs add myfile.c cvs add: cannot open CVS/Entries for 

reading: No such file or directory cvs [add aborted]: no repository 

6、删除檔案或目錄 

1) 删除檔案是分兩個過程。首先,從源碼的本地副本删除該檔案,然後執行 "cvs remove" 指令從資源庫中删除該檔案:

# rm myoldfile.c 

# cvs remove myoldfile.c 

在您下次送出時,該檔案計劃将從資源庫中除去。一旦送出,該檔案會從資源庫目前的版本中正式删除。然而, cvs 不會将該檔案抛棄,而是仍然完整地保留該檔案的内容及其曆史,以備您以後需要它。這隻是 cvs 保護您有價值的源代碼的衆多方法之一。

2) 如果您想除去整個目錄,首先用"cvs remove" 删除目錄中的所有檔案: 

# rm *.c 

# cvs remove 

然後執行送出: 

# cvs commit 

7、标簽的定義和使用 

源碼倉庫中各個檔案的修訂号是獨立增加的,互相之間沒有任何關聯關系,和軟體的釋出号也沒有任何關系,例如一個項目中的各個檔案的修訂号可能是這樣的: 

ci.c 5.21 

co.c 5.9 

ident.c 5.3 

rcs.c 5.12 

rcsbase.h 5.11 

rcsdiff.c 5.10 

rcsutil.c 5.10 

為了便于标記,可以使用标簽來為某個特定版本的特定檔案設定一個标記以友善通路,可以使用cvs tag和cvs rtag來定義标簽,其中cvs tag用來為倉庫中目前工作檔案(或檔案集合)指定一個符号标記;cvs rtag用來顯式地為源碼倉庫的特定修訂号的檔案定義一個标記。例如下面的例子就是給檔案backend.c的目前修訂号定義一個标簽,然後察看該檔案的狀态: 

$ cvs tag rel-0-4 backend.c 

T backend.c 

$ cvs status -v backend.c 

但是在實際應用中很少會為特定檔案定義一個标簽,而往往是在開發過程中的特定階段為特定項目的所有檔案定義一個标簽,以友善釋出或者定義分支,例如: 

$ cvs tag rel-1-0 . 

cvs tag: Tagging . 

T Makefile 

T backend.c 

T driver.c 

T frontend.c 

T parser.c 

定義标簽以後,可以在随後的任何時候通路對應該标簽的項目檔案,例如下面這個指令就用來實作檢出對應于标簽rel-1-0的所有檔案(可能是軟體的1.0釋出): 

$ cvs checkout -r rel-1-0 tc 

當然也可以删除标簽,例如: 

cvs rtag(/tag) -d rel-0-4 tc 删除子產品tc的rel-0-4标記 

8、備份源碼倉庫 

首先斷開所有的cvs連接配接,然後使用cp指令備份即可 

9、分支的定義和使用 

通過CVS可以實作将對源碼的修改送出給一個獨立的開發線,被稱為分支(branch)。當對分支的檔案進行修改時,這些修改不會對主分支和其他分支産生影響。 

随後可以将将一個分支的修改合并(merging)到其他分支。合并是通過cvs update -j來合并修改到目前工作目錄(本地),然後就可以送出修改來影響其他分支了。 

9.1 分支的重要性 

讓我們假設這種情況,項目tc的1.0版本已經搞定,你将繼續開發tc項目,計劃在幾個月内釋出1.1版本,但是客戶抱怨軟體中有緻命的錯誤。是以你檢出1.0版本(這裡就是需要使用标記的原因)并發現了這個bug的原因。然而目前的源碼處于1.0和1.1版本之間是以代碼處于混亂狀态,而且在一個月内不大可能出現穩定版本,是以不大可能根據目前的版本得到一個修複錯誤的版本來釋出。 對于這種情況這就需要建立一個包含錯誤修改的1.0分支釋出,而不需要影響目前的開發,在合适的時候可以将修正合并的主釋出中去。 (如還有不明之處,請到飄揚部落格 http://www.piaoyang.org 給我留言或QQ47720194,我會盡力解答你的問題。)

9.2 建立分支 

建立分支首先為拟修改的某些檔案建立一個标簽(tag),标簽是賦于一個檔案或一組檔案的符号.在源代碼的生命周期裡,組成一組子產品的檔案被賦于相同的标簽。 

建立标簽:在工作目錄裡執行cvs tag 。 

例: 為src建立标簽: 

cvs checkout src(/update亦可,用來更新本地的源代碼) 

cvs tag release-1-0(為目前最新源碼加一個标簽) 

标簽建立後, 就可以為其建立一個分支: 

cvs rtag -b -r release-1-0 release-1-0-path print 

-b :建立分支 

-r release-1-0:-r參數用來标記那些包含指定的标簽的檔案 

releas-1-0-patch:分支 

print: 子產品名 

可以使用tag -b來建立分支,例如在工作目錄中: 

$ cvs tag -b rel-1-0-patches 

将會基于目前工作的分支分離一個分支,并将該分支命名為`rel-1-0-patches。應該了解分支是在cvs的源碼倉庫中建立的,而不是目前工作目錄,基于目前修正建立分支不會将目前工作拷貝自動轉換為新的分支,需要手工來實作的。也可以通過使用rtag指令實作不涉及工作目錄的分支: 

$ cvs rtag -b -r rel-1-0 rel-1-0-patches tc 

`-r rel-1-0訓示建立的新分支應該以标記`rel-1-0指定的修訂為基礎,而不是基于目前的工作主分支。這主要是用來從舊版本中建立一個分支(例如上面的例子)。 

rtag -b訓示建立分支(而不是僅僅建立标記)。應該注意的是`rel-1-0包含的各個檔案的修訂号可能是不一樣的。 

是以該指令的效果是為工程tc建立一個新分支-名字為`rel-1-0-patches,以标記`rel-1-0為基礎。 

9.3 通路分支 

可以以兩種方式通路分支:從源碼倉庫中檢出分支代碼,或者将目前的工作拷貝切換為分支。 

從源碼倉庫中創檢出新分支可以使用指令checkout -r release-tag指令: 

$ cvs checkout -r rel-1-0-patches tc 

将目前分支切換到分支指令: 

$ cvs update -r rel-1-0-patches tc 

或者 

$ cd tc 

$ cvs update -r rel-1-0-patches 

随後的送出等影響源碼倉庫的操作都僅僅對分支起作用,而不會影響主分支和其他分支。可以使用status指令來察看目前工作拷貝屬于哪個分支。輸出中察看sticky tag資訊,這就是cvs顯示目前工作拷貝是在哪個分支上: 

$ cvs status -v driver.c backend.c 

9.4 合并分支 

分支開發一段時間以後往往需要将修訂合并到主分支中來,可以通過`-j branchname參數實作合并分支。使用該參數将分支和其父分支合并。 分支1.2.2被定義為标記R1fix。下面的例子假設子產品mod僅僅包含一個檔案m.c: 

$ cvs checkout mod #擷取最新的1.4版本 

$ cvs update -j R1fix m.c # 合并分支的所有更新到主分支 

# 也就是1.2到1.2.2.2的修改合并到目前工作拷貝 

$ cvs commit -m "Included R1fix" # 建立修訂版本1.5. 

合并時可能出現沖突情況,如果發生沖突,應該在送出以前手工處理沖突。 

四、CVS常見問題分析

1、cvs使用者為什麼不能登入?

# export CVSROOT=:pserver:[email protected]:2401/cvsroot

# cvs login  

Logging in to :pserver:[email protected]:2401/cvsroot

CVS password: 

no such user cvsroot in CVSROOT/passwd

解決辦法:檢視是/cvsroot/CVSROOT/passwd檔案,看裡面有沒有包含該使用者

2、    我怎麼知道别人改了哪些内容?

假如我們現在修改了,檔案config.h , 現在想看看除了我之外還有什麼人修改了這個檔案,到底修改了什麼東西? 怎麼辦?此時,輸入以下指令即可檢視:cvs diff config.h

3、什麼是沖突?怎麼解決?

當兩個或兩個以上的使用者對同一個檔案的相同部分進行修改時,往往會引起沖突,下面用一個例子說明。 

假設使用者A與使用者B都下載下傳了檔案example.h,内容如下: 

void main(int argv,char *argc)
              {
              printf("I am Cather/n");
              }      

然後使用者A把檔案修改成如下,并送出到CVS伺服器(一般将正常送出):

void main(int argv,char *argc)
              {
              printf("I am Cather/n");
              printf("I am Pat/n");
              }      

接着使用者B又把檔案修改成如下:

void main(int argv,char *argc)
              {
              printf("I am Cather/n");
              printf("I love you Cather/n");
              }      

如果使用者B這時選擇“Commit Selection”準備把修改結果送出到CVS伺服器,此時将顯示如下的錯誤提示:

cvs -z9 commit -m "update in 11:20" example.h (in directory C:/my cvs/STW/src/)
              cvs server: Up-to-date check failed for `example.h'
              cvs [server aborted]: correct above errors first!
              
              ***** CVS exited normally with code 1 *****      

表明使用者B的修改與其它使用者的修改沖突,這時要先選擇“Update Selection”,将顯示如下提示:

 cvs -z9 update example.h (in directory C:/my cvs/STW/src/)
              RCS file: /home/cvsroot/STW/src/example.h,v
              retrieving revision 1.9
              retrieving revision 1.10
              Merging differences between 1.9 and 1.10 into example.h
              rcsmerge: warning: conflicts during merge
              cvs server: conflicts found in example.h
              C example.h
              
              ***** CVS exited normally with code 0 *****      

example.h前面的C表示與其它使用者的修改有沖突,并且檔案的圖示會加顯示一個“C”,輕按兩下example.h将顯示example.h的内容,如下:

void main(int argv,char *argc)
              {
              printf("I am Yanxi/n");
              printf("I am Cather/n");
              <<<<<<< example.h
              printf("I love you Yanxi,too!/n"); //這部分為你的修改
              =======
              printf("I love you Cather!/n"); //這部分為其它使用者的修改
              >>>>>>> 1.10
              }      

這時你應該與使用者A進行協商以決定最終要怎樣修改。比如,可以修改成:

void main(int argv,char *argc)
              {
              printf("I am Yanxi/n");
              printf("I am Cather/n");
              printf("I love you Yanxi,too!/n"); //這部分為你的修改
              printf("I love you Cather!/n"); //這部分為其它使用者的修改
              }      

然後選擇“Commit Selection”進行送出,将顯示如下的提示資訊:

cvs -z9 commit -m "update in 11:20" example.h (in directory C:/my cvs/STW/src/)
              Checking in example.h;
              /home/cvsroot/STW/src/example.h,v <-- example.h
              new revision: 1.11; previous revision: 1.10
              done
              
              ***** CVS exited normally with code 0 *****      

表明使用者A與使用者的修改已經合并成功。這樣,向CVS伺服器送出檔案所會遇到的問題也基本上就是這些,使用者要根據所遇到的實際問題進行修改。

五、結束語

如果你是通篇讀到這裡,那麼到這裡,你基本掌握了怎樣配合使用CVS進行系統開發或其它文檔的版本管理與控制。謝謝你的耐心與信任,同時也對你的小有成就小小的祝賀一下。在最後 飄揚要申明的是,本教程裡的大部分内容是由網上多篇介紹CVS的文章提取而來, 飄揚隻是用自己的邏輯和語言将這些内容串聯起來。在此, 飄揚對這些文章的原作者深表感謝。如還有不明之處,請聯系 飄揚,你可以到我的 部落格( 飄揚 部落格) http://www.piaoyang.org 給我留言或QQ47720194,我會盡力解答你的問題。以此拙作,希望能給大家帶去些微的幫助。

本文為 飄揚原創,首發 飄揚部落格 , http://piaoyang.org,作者郵箱piaoyang168#163.com,請尊重版權,轉載請保留本行說明。  

繼續閱讀