當團隊成員完成新的工作時,通過將這些更改提交到資源庫來共享他們的工作。類似地,當他們希望獲得最新可用的工作成果時,就可以根據資源庫中的更改,更新自己的本地工作空間。這意味著項目資源庫會因團隊成員提交新工作成果而經常發生更改。換句話說,資源庫應該表示項目的當前狀態。任何時候,團隊成員都要能夠根據資源庫更新自己的工作空間,并確信它們是最新的。
維護歷史記錄也很重要,那樣就可以將當前工作與先前版本進行比較,如有必要,還可以回復到先前版本。協調團隊的工作,以便只存在唯一的當前項目狀態定義,以及包含團隊已集成的工作,這些對于管理版本控制也是十分必要的。這種協調有可能是最難實現的目標。
最理想的模型是:團隊的任何成員都可以對自己有權訪問的任何資源進行更改。因為兩個團隊成員可以提交對同一資源的更改,所以有可能發生沖突,必須解決這種沖突。這種模型假定沖突具有唯一性。但遺憾的是,沒有任何源代碼是孤立地存在的;通常它包含與其它資源隱式或顯式的相關性。源代碼引用了在其它源代碼資源中描述的構件。但源代碼管理軟件的工作就到此為止了,因為它并不能取代項目管理。項目管理者必須履行其職責:協調其它成員的工作以及負責進度、項目階段和發布日期。此外,源代碼管理也不能替代開發人員之間的交流。
Eclipse 平臺如何支持代碼管理
Eclipse 平臺提供了作為團隊在軟件項目中共享代碼和工作的能力。Eclipse 廣泛地支持各種代碼管理解決方案,這要歸功于它的插件體系結構(不過,現已推出了對 CVS 的支持)。Eclipse 平臺體系結構的重點在于工作空間。工作空間維護構建和測試軟件項目所需的一切。它包含對象(源代碼和資源)。它還保存了用于項目、IDE 和插件的配置設置。工作空間是在開發人員的機器上本地進行維護的,而團隊通過外部資源庫進行協作,不同開發人員的代碼在資源庫進行匯集。可以經由因特網通過“客戶機-服務器”體系結構訪問資源庫。
Eclipse 平臺提供了對于直接從工作空間進行團隊開發操作的支持。這種支持允許開發人員并發地與幾個獨立的資源庫以及不同版本的代碼或項目進行交互。工作空間中的資源允許團隊支持組件處理版本和配置管理問題。當然,單個工作空間可以同時訪問不同類型的資源庫。Eclipse 平臺并沒有提供它自己的代碼管理解決方案;它總是依靠外部系統。Eclipse 平臺只對一個(但也是最流行的一個)源代碼管理系統提供內置支持:并發版本控制系統(Concurrent Versions System,CVS)。對第三方代碼管理應用程序的支持一節中描述了使用第三方插件支持其它資源庫。
CVS 是什么?
CVS 誕生于 1986 年,當時作為一組 shell 腳本而出現,但它現在已經發展成了最流行的針對軟件開發人員的源代碼版本管理解決方案。CVS 是用于代碼版本管理的開放源碼的客戶機/服務器解決方案,它可用于各種平臺,包括 Linux 和 Windows NT/2000/XP。請參閱本文末尾的參考資料,其中有 CVS 客戶機、服務器和源代碼的下載鏈接。
通常,CVS 的主要功能是記錄源文件的歷史。當一組開發人員從事同一個項目時,CVS 將他們彼此隔離開來。每個開發人員都在他/她自己的目錄中獨立工作,然后使用 CVS 資源庫(不時地)合并工作結果。
Eclipse 擁有與 Eclipse 平臺 IDE 緊密集成的內置 CVS 客戶機,它是作為一個單獨透視圖(CVS Repository Exploring 透視圖)而實現的,用于與 CVS 的交互。用于 CVS 的通用 Eclipse 設置(General Eclipse settings for CVS)位于 Window -> Preferences window -> Team 下。在切換到 CVS Repository Exploring 透視圖之后,就可以使用所有 CVS 操作了(轉至 Window -> Open Perspective -> Other -> CVS Repository Exploring 菜單 — 請參閱圖 1 和圖 2)。
圖 1. 切換到 CVS Repository Exploring 透視圖
首先設置資源庫的位置,它將定義用于選定 CVS 服務器/資源庫的連接參數。請確保使用 SSH 隧道(extssh
)。
圖 2. 瀏覽 CVS Repository Exploring 透視圖中的 CVS 資源庫
Eclipse/CVS 的源代碼工作流
在 CVS 團隊協作模型中,團隊成員彼此獨立地在他們各自的工作臺上完成自己的所有工作。最后,他們希望共享其工作。他們通過 CVS 資源庫實現這一點。CVS 使用分支(branch)模型來支持彼此獨立而又高度相互依賴的多個工作流程(course of work)。這些分支是開發團隊用來共享和集成正在進行中的工作的地方。可以認為分支是一個共享的工作臺,當團隊成員對源代碼進行更改時就更新這個工作臺。這個模型允許從事 CVS 團隊項目的每個開發人員在進行更改時與其他成員共享其工作,以及在項目進展期間訪問其他成員的工作。
一個稱為 HEAD 的特殊分支用來表示資源庫中的主要工作流程(HEAD 通常被稱為主干)。當團隊成員將資源提交給該分支時,會影響這些相關性。確保相關性的完整性是很重要的,因為該分支表示了當前項目的狀態。當然,任何時候,團隊成員都可以使用該分支的內容作為新工作的基礎。
那些規則不僅適用于 CVS:無論使用哪種版本控制軟件,團隊項目中都有一些用于源代碼管理的常見步驟。下面是一個使用 Eclipse 內置的 CVS 支持的示例工作流:
1. 啟動新的團隊項目
每個新的空 Eclipse 項目都可以通過 CVS(或受支持的任何其它源代碼管理系統)進行共享。開發人員也可以通過將其現有的代碼遷移到資源庫來共享它。要進行共享,單擊項目主文件夾,在顯示的上下文菜單中使用 Team -> Share Project 選項,如圖 3 所示。
另一個選項是通過從選定的 CVS 資源庫分支導入代碼來創建新的工作臺項目。只要選擇適當分支(或 HEAD),然后選擇從 CVS Repository Exploring 透視圖中的上下文菜單中選擇“Checkout As Project”選項,如圖 4 所示。
2. 使用代碼并進行更改
開發人員通過 Eclipse 工作臺在本地使用代碼,包括的工作有創建新資源、修改現有資源、編寫注釋,并在他們使用后在本地保存這些內容。
3. 使本地更改與 CVS 資源庫同步
如果一個項目開發人員準備提交他/她的工作,那么首先要執行更新操作。這會針對引入的更改核對資源庫,并將這些更改添加到該開發人員的本地工作臺。這樣確保了開發人員知道這些更改可能會影響他/她將要提交的工作的完整性。使用項目上下文菜單中的 Compare With... 選項將本地版本與資源庫中存儲的代碼進行比較(請參閱圖 5)。
下一步是解決最后出現的任何沖突,并設法再次編譯代碼。如果一切正常,那么從項目上下文菜單使用 Team -> Commit... 選項執行提交操作,如圖 6 所示。這會使所有更改都集成到資源庫中。
4. 管理資源庫
CVS 允許開發人員將更改隔離在開發的某些獨立路徑之內,這些路徑稱為分支。當一個開發人員更改某個分支上的文件時,這種更改不會出現在主干或其它分支上。那些分支被命名為子版本(subversion)或代碼分叉(code fork)。稍后,由合并操作將更改從一個分支遷移到另一個分支(或主干)。然后提交這些修訂。這樣就有效地將更改復制到了另一個分支上。使用項目上下文菜單的 Team -> Branch... 選項,Eclipse 使開發分支之間的遷移變得容易。
當然,當開發團隊維護大型資源庫時,有必要控制項目內的提交和合并操作。Eclipse/CVS 集成提供了一種特殊的視圖:CVS Repository History(請參閱圖 7)。它給出了關于團隊成員在資源庫中所執行更改的快速預覽。
圖 7. 在 CVS Resource History 窗口中查看帶注釋的修訂歷史記錄
Eclipse 平臺提供了幾個支持代碼管理的實用程序。最有用的是補丁功能。它將出自兩個來源(譬如本地工作臺和資源庫)的代碼進行比較,然后創建一個包含代碼差異的類似 UNIX 的補丁文件(請參閱圖 8)。可以將該文件發送給開發人員以將源代碼升級到最新版本。
5. 斷開項目與 CVS 的連接
當項目開發已經結束,并且團隊希望凍結源代碼時,可以從 HEAD 資源庫刪除該項目的最終版本。斷開項目與 CVS 的連接將在該項目及其資源上禁用資源庫操作,并刪除與該項目相關聯的 CVS 信息(這一操作是可選的)。
可以通過項目上下文菜單中的 Team -> Disconnect 選項執行斷開連接操作。通過選擇這個選項,會打開 Confirm Disconnect from CVS 對話框。在將該項目與資源庫的連接斷開之后,該團隊必須確定如何處理 CVS 信息。第一個選項是“Delete the CVS meta information”;它將禁用 CVS 團隊菜單操作并從文件系統中刪除CVS 文件夾及其內容。第二個選項是“Do not delete the CVS meta information”;它將禁用 CVS 團隊菜單操作,但保留 CVS 元信息。
對第三方代碼管理應用程序的支持
CVS 有幾個重要的限制:它不能確定單個文件或整個文件集范圍內同時進行的更改,它也不能檢測文件之間的邏輯沖突。其沖突概念純粹是文本意義上的,當對于同一基本文件的兩個更改時間上非常非常接近,從而使合并命令受到干擾時,就會發生沖突。CVS 也不能提供任何類似于消息傳遞這樣的交互式協作工具。幸運的是,CVS 并不是 Eclipse 平臺所支持的唯一的源代碼管理軟件。開發人員可以通過插件擴展 Eclipse 平臺的功能,而且目前(到 2003 年 3 月 4 日為止)已有 16 個可用于團隊開發軟件的插件。所有插件都是由 Eclipse 社區或商業軟件供應商創建的。這些插件中的大多數添加了對第三方、商業源代碼管理系統的支持。最有價值的插件是那些支持流行的企業代碼管理系統(如 Merant PVCS 和 Rational ClearCase)的插件。例如,CVS-SSH2 插件允許通過 SSH2 會話訪問 CVS,而 Microsoft Visual SourceSafe(VSS)團隊提供程序插件添加了對 MS VSS 產品的支持(也可以在諸如 Linux 這樣的非 Windows 平臺上使用)。
但是,我本人所偏愛的插件是 Koi(請參閱參考資料以獲取鏈接)。盡管它并非嚴格用于源代碼控制,但這個創新的工具給協作開發注入了許多新的活力。其當前版本支持工作臺到工作臺的消息傳遞、共享標記、沖突更改通知、共享日歷和事件通知。Koi 將 XML-RPC 用作其客戶機-服務器體系結構中的通信模型。客戶機是與“協作服務器”通信的單個 Eclipse 平臺實例,而協作服務器也是一個 Eclipse 插件。Koi 使用以 JDBC 訪問的關系數據庫作為數據存儲。可在參考資料中找到指向完整的、經過分類的 Eclipse 插件注冊表的鏈接。
關于作者 Pawel Leszek 是 Studio B 的一位作家,他是一位專長于 Linux/Win/Mac OS 系統體系結構和管理的獨立軟件顧問和作家。他具有許多操作系統、編程語言和網絡協議方面的經驗,尤其是 Lotus Domino 和 DB2 方面。Pawel 還在 LinuxWorld 上發表過一系列文章,他是 PC World 波蘭版的 Linux 專欄作家。Pawel 和他的妻子以及可愛的小女兒住在華沙。歡迎提問并提出意見;您可以通過 pawel.leszek@ipgate.pl 與 Pawel 聯系。 |
何謂“持久層”
持久層(Persistence Layer),即專注于實現數據持久化應用領域的某個特定系統的一個邏輯層面,將數據使用者和數據實體相關聯。
何謂“對象數據映射(ORM)”
ORM-Object/Relational Mapper,即“對象-關系型數據映射組件”。對于O/R,即 Object(對象)和 Relational(關系型數據),表示必須同時使用面向對象和關系型數據進行開發。
備注:建模領域中的 ORM 為 Object/Role Modeling(對象角色建模)。另外這里是“O/R Mapper”而非“O/R Mapping”。相對來講,O/R Mapping 描述的是一種設計思想或者實現機制,而 O/R Mapper指以O/R原理設計的持久化框架(Framework),包括 O/R機制還有 SQL自生成,事務處理,Cache管理等。
除了 ORM 技術,還有以下幾種持久化技術
主動域對象模式
它是在實現中封裝了關系數據模型和數據訪問細節的一種形式。在 J2EE 架構中,EJB 組件分為會話 EJB 和實體 EJB。會話 EJB 通常實現業務邏輯,而實體 EJB 表示業務實體。實體 EJB 又分為兩種:由 EJB 本身管理持久化,即 BMP(Bean-Managed Persistence);有 EJB 容器管理持久化,即 CMP(Container-Managed Persistence)。BM P就是主動域對象模式的一個例子,BMP 表示由實體 EJB 自身管理數據訪問細節。
主動域對象本身位于業務邏輯層,因此采用主動域對象模式時,整個應用仍然是三層應用結構,并沒有從業務邏輯層分離出獨立的持久化層。
JDO 模式
Java Data Objects(JDO)是 SUN 公司制定的描述對象持久化語義的標準API。嚴格的說,JDO 并不是對象-關系映射接口,因為它支持把對象持久化到任意一種存儲系統中,包括 關系數據庫、面向對象的數據庫、基于 XML 的數據庫,以及其他專有存儲系統。由于關系數據庫是目前最流行的存儲系統,許多 JDO 的實現都包含了對象-關系映射服務。
CMP 模式
在 J2EE 架構中,CMP(Container-Managed Persistence)表示由 EJB 容器來管理實體 EJB 的持久化,EJB 容器封裝了對象-關系的映射及數據訪問細節。CMP 和 ORM 的相似之處在于,兩者都提供對象-關系映射服務,都把對象持久化的任務從業務邏輯中分離出來。區別在于 CMP 負責持久化實體 EJB 組件,而 ORM 負責持久化 POJO,它是普通的基于 Java Bean 形式的實體域對象。
一般把基于 Java Bean 形式的實體域對象稱為 POJO(Plain Old Java Object),意為又普通又古老的 Java 對象的意思。隨著各種 ORM 映射工具的日趨成熟和流行,POJO有重現光彩,它和基于 CMP 的實體 EJB 相比,即簡單又具有很高的可移植性,因此聯合使用 ORM 映射工具和 POJO,已經成為一種越來越受歡迎的且用來取代 CMP 的持久化方案。POJO 的缺點就是無法做遠程調用,不支持分布式計算。
為什么要做持久化和ORM設計
在目前的企業應用系統設計中,MVC,即 Model(模型)- View(視圖)- Control(控制)為主要的系統架構模式。MVC 中的 Model 包含了復雜的業務邏輯和數據邏輯,以及數據存取機制(如 JDBC的連接、SQL生成和Statement創建、還有ResultSet結果集的讀取等)等。將這些復雜的業務邏輯和數據邏輯分離,以將系統的緊耦合關系轉化為松耦合關系(即解耦合),是降低系統耦合度迫切要做的,也是持久化要做的工作。MVC 模式實現了架構上將表現層(即View)和數據處理層(即Model)分離的解耦合,而持久化的設計則實現了數據處理層內部的業務邏輯和數據邏輯分離的解耦合。而 ORM 作為持久化設計中的最重要也最復雜的技術,也是目前業界熱點技術。
簡單來說,按通常的系統設計,使用 JDBC 操作數據庫,業務處理邏輯和數據存取邏輯是混雜在一起的。
一般基本都是如下幾個步驟:
1、建立數據庫連接,獲得 Connection 對象。
2、根據用戶的輸入組裝查詢 SQL 語句。
3、根據 SQL 語句建立 Statement 對象 或者 PreparedStatement 對象。
4、用 Connection 對象執行 SQL語句,獲得結果集 ResultSet 對象。
5、然后一條一條讀取結果集 ResultSet 對象中的數據。
6、根據讀取到的數據,按特定的業務邏輯進行計算。
7、根據計算得到的結果再組裝更新 SQL 語句。
8、再使用 Connection 對象執行更新 SQL 語句,以更新數據庫中的數據。
7、最后依次關閉各個 Statement 對象和 Connection 對象。
由上可看出代碼邏輯非常復雜,這還不包括某條語句執行失敗的處理邏輯。其中的業務處理邏輯和數據存取邏輯完全混雜在一塊。而一個完整的系統要包含成千上萬個這樣重復的而又混雜的處理過程,假如要對其中某些業務邏輯或者一些相關聯的業務流程做修改,要改動的代碼量將不可想象。另一方面,假如要換數據庫產品或者運行環境也可能是個不可能完成的任務。而用戶的運行環境和要求卻千差萬別,我們不可能為每一個用戶每一種運行環境設計一套一樣的系統。
所以就要將一樣的處理代碼即業務邏輯和可能不一樣的處理即數據存取邏輯分離開來,另一方面,關系型數據庫中的數據基本都是以一行行的數據進行存取的,而程序運行卻是一個個對象進行處理,而目前大部分數據庫驅動技術(如ADO.NET、JDBC、ODBC等等)均是以行集的結果集一條條進行處理的。所以為解決這一困難,就出現 ORM 這一個對象和數據之間映射技術。
舉例來說,比如要完成一個購物打折促銷的程序,用 ORM 思想將如下實現(引自《深入淺出Hibernate》):
業務邏輯如下:
public Double calcAmount(String customerid, double amount)
{
??? // 根據客戶ID獲得客戶記錄
??? Customer customer = CustomerManager.getCustomer(custmerid);
??? // 根據客戶等級獲得打折規則
??? Promotion promotion = PromotionManager.getPromotion(customer.getLevel());
??? // 累積客戶總消費額,并保存累計結果
??? customer.setSumAmount(customer.getSumAmount().add(amount);
??? CustomerManager.save(customer);
??? // 返回打折后的金額
??? return amount.multiply(protomtion.getRatio());
}
這樣代碼就非常清晰了,而且與數據存取邏輯完全分離。設計業務邏輯代碼的時候完全不需要考慮數據庫JDBC的那些千篇一律的操作,而將它交給 CustomerManager 和 PromotionManager 兩個類去完成。這就是一個簡單的 ORM 設計,實際的 ORM 實現框架比這個要復雜的多。
目前有哪些流行的 ORM 產品
目前眾多廠商和開源社區都提供了持久層框架的實現,常見的有
Apache OJB (http://db.apache.org/ojb/)
Cayenne (http://objectstyle.org/cayenne/)
Jaxor (http://jaxor.sourceforge.net)
Hibernate (http://www.hibernate.org)
iBatis (http://www.ibatis.com)
jRelationalFramework (http://ijf.sourceforge.net)
mirage (http://itor.cq2.org/en/oss/mirage/toon)
SMYLE (http://www.drjava.de/smyle)
TopLink (http://otn.oracle.com/products/ias/toplink/index.html)
其中 TopLink 是 Oracle 的商業產品,其他均為開源項目。
其中 Hibernate 的輕量級 ORM 模型逐步確立了在 Java ORM 架構中領導地位,甚至取代復雜而又繁瑣的 EJB 模型而成為事實上的 Java ORM 工業標準。而且其中的許多設計均被 J2EE 標準組織吸納而成為最新 EJB 3.0 規范的標準,這也是開源項目影響工業領域標準的有力見證。
參考文獻:1、《深入淺出Hibernate》
???????? 2、《精通Hibernate:Java對象持久化技術詳解》
內容摘要:
CVS是一個C/S系統,多個開發人員通過一個中心版本控制系統來記錄文件版本,從而達到保證
文件同步的目的。工作模式如下:
CVS服務器(文件版本庫)
/ | \
(版 本 同 步)
/ | \
開發者1 開發者2 開發者3
作為一般開發人員挑選2,6看就可以了,CVS的管理員則更需要懂的更多一些,最后還簡單介紹了
一些Windows下的cvs客戶端使用,CVS遠程用戶認證的選擇及與BUG跟蹤系統等開發環境的集成
問題。
一個系統20%的功能往往能夠滿足80%的需求,CVS也不例外,以下是CVS最常用的功能,可能還不
到它全部命令選項的20%,作為一般開發人員平時會用cvs update和cvs commit就夠了,更多的需求在
實際應用過程中自然會出現,不時回頭看看相關文檔經常有意外的收獲。
tcsh
setenv CVSROOT /path/to/cvsroot
bash
CVSROOT=/path/to/cvsroot ; export CVSROOT
后面還提到遠程CVS服務器的設置:
CVSROOT=:ext:$USER@test.server.address#port:/path/to/cvsroot CVS_RSH=ssh;
export CVSROOT CVS_RSH
初始化:CVS版本庫的初始化。
cvs init
一個項目的首次導入
cvs import -m "write some comments here" project_name vendor_tag release_tag
執行后:會將所有源文件及目錄導入到/path/to/cvsroot/project_name目錄下
vender_tag: 開發商標記
release_tag: 版本發布標記
項目導出:將代碼從CVS庫里導出
cvs checkout project_name
cvs 將創建project_name目錄,并將最新版本的源代碼導出到相應目錄中。這個checkout和Virvual SourceSafe中的check out不是一個概念,相對于Virvual SourceSafe的check out是cvs update,
check in是cvs commit。
注意:第一次導出以后,就不是通過cvs checkout來同步文件了,而是要進入剛才cvs checkout
project_name導出的project_name目錄下進行具體文件的版本同步(添加,修改,刪除)操作。
將文件同步到最新的版本
cvs update
不制定文件名,cvs將同步所有子目錄下的文件,也可以制定某個文件名/目錄進行同步
cvs update file_name
最好每天開始工作前或將自己的工作導入到CVS庫里前都要做一次,并養成“先同步 后修改”的習
慣,和Virvual SourceSafe不同,CVS里沒有文件鎖定的概念,所有的沖突是在commit之前解決,如果
你修改過程中,有其他人修改并commit到了CVS 庫中,CVS會通知你文件沖突,并自動將沖突部分用
>>>>>>
content on cvs server
<<<<<<
content in your file
>>>>>>
標記出來,由你確認沖突內容的取舍。
版本沖突一般是在多個人修改一個文件造成的,但這種項目管理上的問題不應該指望由CVS來解決。
確認修改寫入到CVS庫里
cvs commit -m "write some comments here" file_name
注意:CVS的很多動作都是通過cvs commit進行最后確認并修改的,最好每次只修改一個文件。在確認
的前,還需要用戶填寫修改注釋,以幫助其他開發人員了解修改的原因。如果不用寫-m "comments"而
直接確認`cvs commit file_name` 的話,cvs會自動調用系統缺省的文字編輯器(一般是vi)要求你寫入注釋。
注釋的質量很重要:所以不僅必須要寫,而且必須寫一些比較有意義的內容:以方便其他開發人員能
夠很好的理解不好的注釋,很難讓其他的開發人員快速的理解:比如: -m "bug fixed" 甚至 -m ""
好的注釋,甚至可以用中文: -m "在用戶注冊過程中加入了Email地址校驗"
修改某個版本注釋:每次只確認一個文件到CVS庫里是一個很好的習慣,但難免有時候忘了指定文件
名,把多個文件以同樣注釋commit到CVS庫里了,以下命令可以允許你修改某個文件某個版本的注釋:
cvs admin -m 1.3:"write some comments here" file_name
添加文件
創建好新文件后,比如:touch new_file
cvs add new_file
注意:對于圖片,Word文檔等非純文本的項目,需要使用cvs add -kb選項按2進制文件方式導入(k表示
擴展選項,b表示binary),否則有可能出現文件被破壞的情況
比如:
cvs add -kb new_file.gif
cvs add -kb readme.doc
如果關鍵詞替換屬性在首次導入時設置錯了怎么辦?
cvs admin -kkv new_file.css
然后確認修改并注釋
cvs ci -m "write some comments here"
刪除文件
將某個源文件物理刪除后,比如:rm file_name
cvs rm file_name
然后確認修改并注釋
cvs ci -m "write some comments here"
以上面前2步合并的方法為:
cvs rm -f file_name
cvs ci -m "why delete file"
注意:很多cvs命令都有縮寫形式:commit=>ci; update=>up; checkout=>co/get; remove=>rm;
添加目錄
cvs add dir_name
查看修改歷史
cvs log file_name
cvs history file_name
查看當前文件不同版本的區別
cvs diff -r1.3 -r1.5 file_name
查看當前文件(可能已經修改了)和庫中相應文件的區別
cvs diff file_name
cvs的web界面提供了更方便的定位文件修改和比較版本區別的方法,具體安裝設置請看后面的cvsweb
使用
正確的通過CVS恢復舊版本的方法:
如果用cvs update -r1.2 file.name
這個命令是給file.name加一個STICK TAG: "1.2" ,雖然你的本意只是想將它恢復到1.2版本
正確的恢復版本的方法是:cvs update -p -r1.2 file_name >file_name
如果不小心已經加成STICK TAG的話:用cvs update -A 解決
移動文件/文件重命名
cvs里沒有cvs move或cvs rename,因為這兩個操作是可以由先cvs remove old_file_name,然后cvs add new_file_name實現的。
刪除/移動目錄
最方便的方法是讓管理員直接移動,刪除CVSROOT里相應目錄(因為CVS一個項目下的子目錄都是
獨立的,移動到$CVSROOT目錄下都可以作為新的獨立項目:好比一顆樹,其實砍下任意一枝都能獨
立存活),對目錄進行了修改后,要求其開發人員重新導出項目cvs checkout project_name 或者用
cvs update -dP同步。
項目發布導出不帶CVS目錄的源文件
做開發的時候你可能注意到了,每個開發目錄下,CVS都創建了一個CVS/目錄。里面有文件用于記錄
當前目錄和CVS庫之間的對應信息。但項目發布的時候你一般不希望把文件目錄還帶著含有CVS信息
的CVS目錄吧,這個一次性的導出過程使用cvs export命令,不過export只能針對一個TAG或者日期導出,比如:
cvs export -r release1 project_name
cvs export -D 20021023 project_name
cvs export -D now project_name
cvs tag release_1_0
開始一個新的里程碑:
cvs commit -r 2 標記所有文件開始進入2.x的開發
注意:CVS里的revsion和軟件包的發布版本可以沒有直接的關系。但所有文件使用和發布版本一致的
版本號比較有助于維護。
版本分支的建立
在開發項目的2.x版本的時候發現1.x有問題,但2.x又不敢用,則從先前標記的里程碑:release_1_0導出
一個分支 release_1_0_patch
cvs rtag -b -r release_1_0 release_1_0_patch proj_dir
一些人先在另外一個目錄下導出release_1_0_patch這個分支:解決1.0中的緊急問題,
cvs checkout -r release_1_0_patch
而其他人員仍舊在項目的主干分支2.x上開發
在release_1_0_patch上修正錯誤后,標記一個1.0的錯誤修正版本號
cvs tag release_1_0_patch_1
如果2.0認為這些錯誤修改在2.0里也需要,也可以在2.0的開發目錄下合并release_1_0_patch_1中的修改到
當前代碼中:
cvs update -j release_1_0_patch_1
常見的登陸格式如下:
cvs -d :pserver:cvs_user_name@cvs.server.address:/path/to/cvsroot login
例子:
cvs -d :pserver:cvs@samba.org:/cvsroot login
不是很安全,因此一般是作為匿名只讀CVS訪問的方式。從安全考慮,通過系統本地帳號認證并通過
SSH傳輸是比較好的辦法,通過在客戶機的 /etc/profile里設置一下內容:
CVSROOT=:ext:$USER@cvs.server.address#port:/path/to/cvsroot CVS_RSH=ssh; export CVSROOT CVS_RSH
所有客戶機所有本地用戶都可以映射到CVS服務器相應同名帳號了。
比如:
CVS服務器是192.168.0.3,上面CVSROOT路徑是/home/cvsroot,另外一臺開發客戶機是192.168.0.4,
如果 tom在2臺機器上都有同名的帳號,那么從192.168.0.4上設置了:
export CVSROOT=:ext:tom@192.168.0.3:/home/cvsroot
export CVS_RSH=ssh
tom就可以直接在192.168.0.4上對192.168.0.3的cvsroot進行訪問了(如果有權限的話)
cvs checkout project_name
cd project_name
cvs update
...
cvs commit
如果CVS所在服務器的SSH端口不在缺省的22,或者和客戶端與CVS服務器端SSH缺省端口不一致,
有時候設置了:
:ext:$USER@test.server.address#port:/path/to/cvsroot
仍然不行,比如有以下錯誤信息:
ssh: test.server.address#port: Name or service not known
cvs [checkout aborted]: end of file from server (consult above messages if any)
解決的方法是做一個腳本指定端口轉向(不能使用alias,會出找不到文件錯誤):
創建一個/usr/bin/ssh_cvs文件,假設遠程服務器的SSH端口是非缺省端口:34567
#!/bin/sh
/usr/bin/ssh -p 34567 "$@"
然后:chmod +x /usr/bin/ssh_cvs
并CVS_RSH=ssh_cvs; export CVS_RSH
注意:port是指相應服務器SSH的端口,不是指cvs專用的pserver的端口
使用的樣例可以看:http://www.freebsd.org/cgi/cvsweb.cgi
CVSWEB的下載:CVSWEB從最初的版本已經演化出很多功能界面更豐富的版本,這個是我個人感
覺安裝設置比較方便的:
原先在:http://www.spaghetti-code.de/software/linux/cvsweb/,但目前已經刪除,目前仍可以在本站下載CVSWEB,其實最近2年FreeBSD的CVSWeb項目已經有了更好的發展吧,而當初沒有用FreeBSD那個
版本主要就是因為沒有彩色的文件Diff功能。
下載解包:
tar zxf cvsweb.tgz
把配置文件cvsweb.conf放到安全的地方(比如和apache的配置放在同一個目錄下),
修改:cvsweb.cgi讓CGI找到配置文件:
$config = $ENV{'CVSWEB_CONFIG'} || '/path/to/apache/conf/cvsweb.conf';
轉到/path/to/apache/conf下并修改cvsweb.conf:
CVSWEB可不能隨便開放給所有用戶,因此需要使用WEB用戶認證:
先生成 passwd:
/path/to/apache/bin/htpasswd -c cvsweb.passwd user
修改httpd.conf: 增加
<Directory "/path/to/apache/cgi-bin/cvsweb/">
AuthName "CVS Authorization"
AuthType Basic
AuthUserFile /path/to/cvsweb.passwd
require valid-user
</Directory>
幾個常用的缺省文件:
default.php
<?php
/*
* Copyright (c) 2002 Company Name.
* $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11
chedong Exp $
*/
?>
====================================
Default.java: 注意文件頭一般注釋用 /* 開始 JAVADOC注釋用 /** 開始的區別
/*
* Copyright (c) 2002 MyCompany Name.
* $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11
chedong Exp $
*/
package com.mycompany;
import java.;
/**
* comments here
*/
public class Default {
/**
* Comments here
* @param
* @return
*/
public toString() {
}
}
====================================
default.pl:
#!/usr/bin/perl -w
# Copyright (c) 2002 Company Name.
# $Header: /home/cvsroot/tech/cvs_card.html,v 1.9 2003/11/09 07:57:11
chedong Exp $
# file comments here
use strict;
CVS沒有文件鎖定模式,VSS在check out同時,同時記錄了文件被導出者鎖定。
CVS的update和commit, VSS是get_lastest_version和check in
對應VSS的check out/undo check out的CVS里是edit和unedit
在CVS中,標記自動更新功能缺省是打開的,這樣也帶來一個潛在的問題,就是不用-kb方式添加
binary文件的話在cvs自動更新時可能會導致文件失效。
$Header: /home/cvsroot/tech/cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $ $Date: 2003/11/09
07:57:11 $這樣的標記在Virsual SourceSafe中稱之為Keyword Explaination,缺省是關閉
的,需要通過OPITION打開,并指定需要進行源文件關鍵詞掃描的文件類型:*.txt,*.java, *.html...
對于Virsual SourceSafe和CVS都通用的TAG有:
$Header: /home/cvsroot/tech/cvs_card.html,v 1.5 2003/03/09 08:41:46 chedong Exp $
$Author: chedong $
$Date: 2003/11/09 07:57:11 $
$Revision: 1.9 $
我建議盡量使用通用的關鍵詞保證代碼在CVS和VSS都能方便的跟蹤。
cvs Windows客戶端:目前穩定版本為1.2
http://cvsgui.sourceforge.net
ssh Windows客戶端
http://www.networksimplicity.com/openssh/
安裝好以上2個軟件以后:
WinCVS客戶端的admin==>preference設置
1 在general選單里
設置CVSROOT: username@192.168.0.123:/home/cvsroot
設置Authorization: 選擇SSH server
2 Port選單里
鉤上:check for alternate rsh name
并設置ssh.exe的路徑,缺省是裝在 C:\Program Files\NetworkSimplicity\ssh\ssh.exe
然后就可以使用WinCVS進行cvs操作了,所有操作都會跳出命令行窗口要求你輸入服務器端的認
證密碼。
當然,如果你覺得這樣很煩的話,還有一個辦法就是生成一個沒有密碼的公鑰/私鑰對,并設置
CVS使用基于公鑰/私鑰的SSH認證(在general 選單里)。
可以選擇的diff工具:examdiff
下載:
http://www.prestosoft.com/examdiff/examdiff.htm
還是在WinCVS菜單admin==>preference的WinCVS選單里
選上:Externel diff program
并設置diff工具的路徑,比如:C:\Program Files\ed16i\ExamDiff.exe
在對文件進行版本diff時,第一次需要將窗口右下角的use externel diff選上。
這里首先說一下CVS的pserver模式下的用戶認證,CVS的用戶認證服務是基于inetd中的:
cvspserver stream tcp nowait apache /usr/bin/cvs cvs --allow-root=/home/cvsroot pserver
一般在2401端口(這個端口號很好記:49的平方)
CVS用戶數據庫是基于CVSROOT/passwd文件,文件格式:
[username]:[crypt_password]:[mapping_system_user]
由于密碼都用的是UNIX標準的CRYPT加密,這個passwd文件的格式基本上是apache的htpasswd格式
的擴展(比APACHE的 PASSWD文件多一個系統用戶映射字段),所以這個文件最簡單的方法可
以用
apache/bin/htpasswd -b myname mypassword
創建。注意:通過htpasswd創建出來的文件會沒有映射系統用戶的字段
例如:
new:geBvosup/zKl2
setup:aISQuNAAoY3qw
test:hwEpz/BX.rEDU
映射系統用戶的目的在于:你可以創建一個專門的CVS服務帳號,比如用apache的運行用戶apache,
并將/home/cvsroot目錄下的所有權限賦予這個用戶,然后在passwd文件里創建不同的開發用戶帳號,
但開發用戶帳號最后的文件讀寫權限都映射為apache用戶,在SSH模式下多個系統開發用戶需要在
同一個組中才可以相互讀寫CVS庫中的文件。
進一步的,你可以將用戶分別映射到apache這個系統用戶上。
new:geBvosup/zKl2:apache
setup:aISQuNAAoY3qw:apache
test:hwEpz/BX.rEDU:apache
CVSTrac很好的解決了CVSROOT/passwd的管理問題,而且包含了BUG跟蹤報告系統和集成WIKI交
流
功能等,使用的 CGI方式的安裝,并且基于GNU Public License:
在inetd里加入cvspserver服務:
cvspserver stream tcp nowait apache /usr/bin/cvs cvs --allow-root=/home/cvsroot pserver
xietd的配置文件:%cat cvspserver
service cvspserver
{
disable = no
socket_type = stream
wait = no
user = apache
server = /usr/bin/cvs
server_args = -f --allow-root=/home/cvsroot pserver
log_on_failure += USERID
}
注意:這里的用戶設置成apache目的是和/home/cvsroot的所有用戶一致,并且必須讓這個這個用戶對/home/cvsroot/下的 CVSROOT/passwd和cvstrac初始化生成的myproj.db有讀取權限。
安裝過程
修改登錄密碼,進行BUG報告等,
更多使用細節可以在使用中慢慢了解。
對于前面提到的WinCVS在perference里設置:
CVSROOT欄輸入:username@ip.address.of.cvs:/home/cvsroot
Authenitication選擇:use passwd file on server side
就可以了從服務器上進行CVS操作了。
CVS的權限管理分2種策略:
Linux上通過ssh連接CVS服務器的多個開發人員:通過都屬于apache組實現文件的共享讀寫開發人員
有開發服務器上的系統帳號:sysuser1 sysuser2,設置讓他們都屬于apache組,因為通過cvs新導入的
項目都是對組開放的:664權限的,這樣無論那個系統用戶導入的項目文件,只要文件的組宿主是apache,所有其他同組系統開發用戶就都可以讀寫;基于ssh遠程認證的也是一樣。
? ?apache(system group)
/ ? ? ? ? ? ?| ? ? ? ? ? \
sysuser1 ? sysuser2 ? ? sysuser3
Windows上通過cvspserver連接CVS服務器的多個開發人員:通過在passwd文件種映射成 apache用戶
實現文件的共享讀寫
他們的帳號通過CVSROOT/passwd和readers writers這幾個文件管理;通過cvstrac設置所有
虛擬用戶都映射到apache用戶上即可。
? ?apache(system user)
/ ? ? ? ? ? ?| ? ? ? ? ? ?\
windev1 ? ? windev2 ? ? ?windev3? ? ? ? ? ? ?
利用CVS WinCVS/CVSWeb/CVSTrac 構成了一個相對完善的跨平臺工作組開發版本控制環境。
相關資源:
CVS HOME:
http://www.cvshome.org
CVS FAQ:
http://www.loria.fr/~molli/cvs-index.html
相關網站:
http://directory.google.com/Top/Computers/Software/Configuration_Management/Tools/Concurrent_
Versions_System/
CVS--并行版本系統
http://www.soforge.com/cvsdoc/zh_CN/book1.html
CVS 免費書:
http://cvsbook.red-bean.com/
CVS命令的速查卡片 refcards.com/refcards/cvs/
WinCVS:
http://cvsgui.sourceforge.net/
CVSTrac: A Web-Based Bug And Patch-Set Tracking System For CVS
http://www.cvstrac.org
StatCVS:基于CVS的代碼統計工具:按代碼量,按開發者的統計表等
http://sourceforge.net/projects/statcvs
首先,由于其他所有的GUI工具都是基于CVS基本協議的,而且他們可能會提供CVS的命令行或者等價形
式作為顯示的一部分,所以你應該對命令行操作有所了解。如果你還沒有一個cvs。exe的命令行程序,從
www.cvsnt.org你可以得到一個cvsnt的下載連接,其中就包含了一個命令行的cvs.exe程序。我們先從它開始
(為了作為一個client使用,你不需要安裝cvsnt的server組件)。CVSNT的cvs.exe是專門為windows編寫的,你
需要把cvs.exe放在你的path里面。
1.進入命令行方式。和VSS一樣,你也需要在本地有一個工作目錄對應于一個repository。假設這個目錄是'd:\works\sandbox'。
請切換到這個目錄。輸入"cvs"。你會看到:
這些提示信息告訴您關于cvs的基本語法。cvs后面跟著的是全局參數,然后是命令,最后是命令的參數。2.login
正確的login不會有任何輸出,否則會告訴你錯誤原因。
如果login失敗,則可以先嘗試命令:set cvsroot=:pserver:cao@IP或者計算機名字/CVSRoot
3.下面我們看看這個CVS server中有哪些module。
4.假設現在我們工作的項目是projectX,下面我們需要得到它下面的全部文件。
現在讓我們看一下我們得到了什么。
在d:/works/sandbox目錄下,你可以看到有一個projectX目錄。這就是你得到的所有文件。
這個目錄下你會發現一個叫做 CVS的目錄。危險!請不要刪除這個目錄,或者改名,或者改動其中的
任何文件,除非你知道你在做什么。這個目錄是CVS的控制目錄。如果你用過source safe,你一定很熟悉。scc這個文件,CVS目錄的作用就和這個控制文件一樣,都是用來記錄你訪問服務器的參數。
這里我們需要解釋一下cvs和VSS的名詞差別。在VSS中,checkout意味著你將獲得一個文件的修改權,而
cvs中checkout的這個含義取消了,僅僅指取得文件的新版本。很多cvs server會有一個anonymous用戶,他
只有checkout權限,也就意味著它只讀。???
5.讓我們試著加入一個文件:
在d:/works/sandbox/projectX下,新建一個文件newfile.txt,
然后,在這個目錄下執行:
你需要commit它才能被sever接受。
一個notepad窗口彈出請您輸入注釋。
這是commit完成的結果。現在的版本號是1.1。
6.好了,現在假設您需要改一下這個文件的內容。
CVS可以幫助您比較現在您的版本和repository中的版本有什么不同。
好了,現在您可以提交您的新文件。
CVS會幫您保留您的各個版本。在commit之后,現在我們來看一看各個版本的history。
7.最后,為了完成這個試驗,請把這個newfile文件刪去。
我們現在認識了一些最基本的CVS入門級指令。
其實CVS是非常強大的,我們并沒有用到一些更復雜的功能,請參閱cvs的手冊來得到更為詳盡的幫助。
轉載至:http://www.redsaga.com/CVS_newbie_win32/
5、打開一個cmd窗口,輸入命令set cvsroot=:pserver:sunxdd@server/Root(sunxdd是剛才建立的用戶名,server是安裝的計算機名稱或者IP,/Root是剛才建立的文件夾別名,每次登陸之前都要這樣告訴系統建立這樣一個環境變量)
cvs login
密碼為空
這時會登錄成功
改密碼
cvs passwd
到這里CVSNT服務器基本上搞定了。
有用的資源:
http://edu.tmn.cn/html/5/47/185/2005210/104247.htm
http://www.redsaga.com/CVS_newbie_win32/#tortoisecvs
http://sunxdd.blogchina.com/2338489.html
加入WAP書簽
<?xml version="1.0"?> <!DOCTYPE CHARACTERISTIC-LIST SYSTEM "/DTD/characteristic_list.xml"> <CHARACTERISTIC-LIST> <CHARACTERISTIC TYPE="ADDRESS"> <PARM NAME="BEARER" VALUE="GPRS"/> <PARM NAME="PROXY" VALUE="10.0.0.172"/> <PARM NAME="PORT" VALUE="9201"/> <PARM NAME="GPRS_ACCESSPOINTNAME" VALUE="wap.02826.com"/> <PARM NAME="PPP_AUTHTYPE" VALUE="PAP"/> </CHARACTERISTIC> <CHARACTERISTIC TYPE="NAME"> <PARM NAME="NAME" VALUE="wmzsoft GPRS"/> </CHARACTERISTIC> <CHARACTERISTIC TYPE="BOOKMARK"/> <PARM NAME="NAME" VALUE="02826"/> <PARM NAME="URL" VALUE=" http://wap.02826.com "/> </CHARACTERISTIC> </CHARACTERISTIC-LIST> |
<body>
<?php
$title=$_POST["title"];
$text=$_POST["text"];
$name=$_POST["name"];
$count=$_POST["i"];
include_once("數據庫連接文件");
if (empty($title)||empty($text)||empty($bar_name)){
?echo "標題或內容不能空
?die("<br /><a href=\"index.php\">重新來過</a>");
}
/*這里是數據查詢語言取出要用的數據*/
if(in_array("0", $_FILES['userfile']['error'])){//上傳文件開始
?$uploaddir= 'attfile/';//設置上傳的文件夾地址
?$FILES_EXT=array('.gif','.jpg','.mp3','.3gp');//設置允許上傳文件的類型
?$MAX_SIZE = 20000000;//設置文件上傳限制20000000byte=2M
?for ($i=0;$i<$count;$i++){
??$FILES_NAME=$_FILES['userfile']['name'][$i];//客戶端文件名
??//取出文件后綴名,strrpos()從標記開始前字節個數(不算標記),substr()顯示從第strrpos()之后的字符
??$file_ext=substr($FILES_NAME,strrpos($FILES_NAME,"."));
??//檢查文件大小
??if($_FILES['userfile']['size'][$i]>$MAX_SIZE){
???echo "文件大小超程序允許范圍!";
???exit;
??}
??//檢查文件類型
??if(in_array($file_ext, $FILES_EXT)){
???$_FILES['userfile']['name'][$i]=date("YmdHis").rand(10000,1000000).$file_ext;
???//echo $_FILES['userfile']['name'][$i];
???$uploadfile = $uploaddir.$_FILES['userfile']['name'][$i];//上傳后文件的路徑及文件名
???//echo $uploadfile;
???//用move函數生成臨時文件名,并按照 $_FILES['userfile']['name']上傳到$uploaddir下
???if (move_uploaded_file($_FILES['userfile']['tmp_name'][$i], $uploadfile)) {
????//將上傳后的路徑寫入到數據庫中
????$post_id=(int)$post_id;
????$uploadfile="attfile/".$_FILES['userfile']['name'][$i];
????$sql=插入語句
????$stmt=$db->prepare($sql);
????$stmt->execute();
????print "<br />文件\n{$FILES_NAME}\n上傳成功!";
???} else {
????print "上傳錯誤!? 以下是上傳的信息:\n";
????print_r($_FILES);
???}
??}
??else{
???echo "{$FILES_NAME}\n不是允許上傳的文件類型!";
???exit;
??}
?}
}
?>
</body>
</html>
Echo?? "上傳文件大小:";
echo $_FILES['userfile']['size'];
//已上傳文件的大小,單位為字節。
echo "<br>";
Echo?? "文件上傳后被臨時儲存為:";
echo $_FILES['userfile']['tmp_name'];
//文件被上傳后在服務端儲存的臨時文件名。
echo "<br>";
$Erroe=$_FILES['userfile']['error'];
switch($Erroe){
?case 0:
?Echo?? "上傳成功"; break;
?case 1:
?Echo?? "上傳的文件超過了 php.ini 中 upload_max_filesize 選項限制的值."; break;
?case 2:
?Echo?? "上傳文件的大小超過了 HTML 表單中 MAX_FILE_SIZE 選項指定的值。";?? break;
?case 3:
?Echo?? "文件只有部分被上傳";break;
?case 4:
?Echo?? "沒有文件被上傳";break;
}
?>
(2)<?php
$uploaddir= '../attfile/';//設置上傳的文件夾地址
$FILES_EXT=array('.gif','.jpg','.bmp');//設置允許上傳文件的類型
$MAX_SIZE = 20000000;//設置文件上傳限制20000000byte=2M
for ($i=0;$i<count($userfile);$i++){
?$FILES_NAME=$_FILES['userfile']['name'][$i];//客戶端文件名
}
//echo $FILES_NAME;
//取出文件后綴名,strrpos()從標記開始前字節個數(不算標記),substr()顯示從第strrpos()之后的字符
$file_ext=substr($FILES_NAME,strrpos($FILES_NAME,"."));
//echo $file_ext;
//檢查文件大小
if($_FILES['userfile']['size']>$MAX_SIZE){
?echo "文件大小超程序允許范圍!";
?exit;
}
//檢查文件類型
if(in_array($file_ext, $FILES_EXT)){
?$_FILES['userfile']['name']=date("YmdHis").rand().$file_ext;
?$uploadfile = $uploaddir.$_FILES['userfile']['name'];//上傳后文件的路徑及文件名
?//將上傳后的路徑寫入到數據庫中
?//用move函數生成臨時文件名,并按照 $_FILES['userfile']['name']上傳到$uploaddir下
?if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
??print "\n上傳成功!";
?} else {
??print "上傳錯誤!? 以下是上傳的信息:\n";
??print_r($_FILES);
?}
}
else{
?echo "{$file_ext}不是允許上傳的文件類型!";
?exit;
}
?>
小技巧>>1、當在提交時,希望確認是否要提交可以在<form>里如這樣加入:<form onsubmit="return confirm('你真的要提交嗎?')">
2、在php里希望返回時所有在文本框里的東西都保留可以這樣:<a href=javascript:history.back(1)>重新來過</a>。