### 使用分支
在這一點上,你必須理解每一次提交是怎樣建立整個新的文件系統樹(叫做“修訂版本”)的,如果沒有,可以回頭去讀[“修訂版本”一節]( "修訂版本")。
對于本章節,我們會回到第2章的同一個例子,還記得你和你的合作者Sally分享一個包含兩個項目的版本庫,`paint`和`calc`。注意[圖4.2 “開始規劃版本庫”]( "圖4.2.開始規劃版本庫"),然而,現在每個項目的都有一個`trunk`和`branches`子目錄,它們存在的理由很快就會清晰起來。
**圖4.2.開始規劃版本庫**

像以前一樣,假定Sally和你都有“calc”項目的一份拷貝,更準確地說,你有一份`/calc/trunk`的工作拷貝,這個項目的所有的文件在這個子目錄里,而不是在`/calc`下,因為你的小組決定使用`/calc/trunk`作為開發使用的“主線”。
假定你有一個任務,將要對項目做基本的重新組織,這需要花費大量時間來完成,會影響項目的所有文件,問題是你不會希望打擾Sally,她正在處理這樣或那樣的程序小Bug,一直使用整個項目(`/calc/trunk`)的最新版本,如果你一點一點的提交你的修改,你一定會干擾Sally的工作。
一種策略是自己閉門造車:你和Sally可以停止一個到兩個星期的共享,也就是說,開始作出本質上的修改和重新組織工作拷貝的文件,但是在完成這個任務之前不做提交和更新。這樣會有很多問題,首先,這樣并不安全,許多人習慣頻繁的保存修改到版本庫,工作拷貝一定有許多意外的修改。第二,這樣并不靈活,如果你的工作在不同的計算機(或許你在不同的機器有兩份`/calc/trunk`的工作拷貝),你需要手工的來回拷貝修改,或者只在一個計算機上工作,這時很難做到共享你即時的修改,一項軟件開發的“最佳實踐”就是允許審核你做過的工作,如果沒有人看到你的提交,你失去了潛在的反饋。最后,當你完成了公司主干代碼的修改工作,你會發現合并你的工作拷貝和公司的主干代碼會是一件非常困難的事情,Sally(或者其他人)也許已經對版本庫做了許多修改,已經很難和你的工作拷貝結合―當你單獨工作幾周后運行**svn update**時就會發現這一點。
最佳方案是創建你自己的分支,或者是版本庫的開發線。這允許你保存破壞了一半的工作而不打擾別人,盡管你仍可以選擇性的同你的合作者分享信息,你將會看到這是怎樣工作的。
### 創建分支
建立分支非常的簡單―使用**svn copy**命令給你的工程做個拷貝,Subversion不僅可以拷貝單個文件,也可以拷貝整個目錄,在目前情況下,你希望作`/calc/trunk`的拷貝,新的拷貝應該在哪里?在你希望的任何地方―它只是在于項目的政策,我們假設你們項目的政策是在`/calc/branches`建立分支,并且你希望把你的分支叫做`my-calc-branch`,你希望建立一個新的目錄`/calc/branches/my-calc-branch`,作為/calc/trunk的拷貝開始它的生命周期。
有兩個方法作拷貝,我們首先介紹一個混亂的方法,只是讓概念更清楚,作為開始,取出一個工程的根目錄,`/calc`:
~~~
$ svn checkout http://svn.example.com/repos/calc bigwc
A bigwc/trunk/
A bigwc/trunk/Makefile
A bigwc/trunk/integer.c
A bigwc/trunk/button.c
A bigwc/branches/
Checked out revision 340.
~~~
建立一個備份只是傳遞兩個目錄參數到**svn copy**命令:
~~~
$ cd bigwc
$ svn copy trunk branches/my-calc-branch
$ svn status
A + branches/my-calc-branch
~~~
在這個情況下,**svn copy**命令迭代的將`trunk`工作目錄拷貝到一個新的目錄branhes/my-calc-branch,像你從**svn status**看到的,新的目錄是準備添加到版本庫的,但是也要注意A后面的“+”號,這表明這個準備添加的東西是一份*備份*,而不是新的東西。當你提交修改,Subversion會通過拷貝`/calc/trunk`建立`/calc/branches/my-calc-branch`目錄,而不是通過網絡傳遞所有數據:
~~~
$ svn commit -m "Creating a private branch of /calc/trunk."
Adding branches/my-calc-branch
Committed revision 341.
~~~
現在,我們必須告訴你建立分支最簡單的方法:**svn copy**可以直接對兩個URL操作。
~~~
$ svn copy http://svn.example.com/repos/calc/trunk \
http://svn.example.com/repos/calc/branches/my-calc-branch \
-m "Creating a private branch of /calc/trunk."
Committed revision 341.
~~~
其實這兩種方法沒有什么區別,兩個過程都在版本341建立了一個新目錄作為`/calc/trunk`的一個備份,這些可以在[圖4.3 “拷貝后的版本庫”]( "圖4.3.拷貝后的版本庫")看到,注意第二種方法,只是執行了一個*立即*提交。 這是一個簡單的過程,因為你不需要取出版本庫一個龐大的鏡像,事實上,這個技術不需要你有工作拷貝。
**圖4.3.拷貝后的版本庫**

**代價低廉的拷貝**
Subversion的版本庫有特殊的設計,當你復制一個目錄,你不需要擔心版本庫會變得十分巨大―Subversion并不是拷貝所有的數據,相反,它建立了一個*已存在*目錄樹的入口,如果你是Unix用戶,可以把它理解成硬鏈接,在這里,這個拷貝被可以被認為是“懶的”,如果你提交一個文件的修改,只有這個文件改變了―余下的文件還是作為原來文件的鏈接存在。
這就是為什么經常聽到Subversion用戶談論“廉價的拷貝”,與目錄的大小無關―這個操作會使用很少的時間,事實上,這個特性是Subversion提交工作的基礎:每一次版本都是前一個版本的一個“廉價的拷貝”,只有少數項目修改了。(要閱讀更多關于這部分的內容,訪問Subversion網站并且閱讀設計文檔中的“bubble up”方法)。
當然,拷貝與分享的內部機制對用戶來講是不可見的,用戶只是看到拷貝樹,這里的要點是拷貝的時間與空間代價很小,所以你可以隨意做想要的分支。
### 在分支上工作
現在你已經在項目里建立分支了,你可以取出一個新的工作拷貝來開始使用:
~~~
$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch
A my-calc-branch/Makefile
A my-calc-branch/integer.c
A my-calc-branch/button.c
Checked out revision 341.
~~~
這一份工作拷貝沒有什么特別的,它只是版本庫另一個目錄的一個鏡像罷了,當你提交修改時,Sally在更新時不會看到改變,她是`/calc/trunk`的工作拷貝。(確定要讀本章后面的[“轉換工作拷貝”一節]( "轉換工作拷貝"),**svn switch**命令是建立分支工作拷貝的另一個選擇。)
我們假定本周就要過去了,如下的提交發生:
-
你修改了`/calc/branches/my-calc-branch/button.c`,生成版本號342。
-
你修改了`/calc/branches/my-calc-branch/integer.c`,生成版本號343。
-
Sally修改了`/calc/trunk/integer.c`,生成了版本號344。
現在有兩個獨立開發線,[圖4.4 “一個文件的分支歷史”]( "圖4.4.一個文件的分支歷史")顯示了`integer.c`的歷史。
**圖4.4.一個文件的分支歷史**

當你看到`integer.c`的改變時,你會發現很有趣:
~~~
$ pwd
/home/user/my-calc-branch
$ svn log --verbose integer.c
------------------------------------------------------------------------
r343 | user | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
M /calc/branches/my-calc-branch/integer.c
* integer.c: frozzled the wazjub.
------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
A /calc/branches/my-calc-branch (from /calc/trunk:340)
Creating a private branch of /calc/trunk.
------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
M /calc/trunk/integer.c
* integer.c: changed a docstring.
------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
M /calc/trunk/integer.c
* integer.c: adding this file to the project.
------------------------------------------------------------------------
~~~
注意,Subversion追蹤分支上的`integer.c`的歷史,包括所有的操作,甚至追蹤到拷貝之前。這表示了建立分支也是歷史中的一次事件,因為在拷貝整個`/calc/trunk/`時已經拷貝了一份`integer.c`。現在看Sally在她的工作拷貝運行同樣的命令:
~~~
$ pwd
/home/sally/calc
$ svn log --verbose integer.c
------------------------------------------------------------------------
r344 | sally | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
M /calc/trunk/integer.c
* integer.c: fix a bunch of spelling errors.
------------------------------------------------------------------------
r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines
Changed paths:
M /calc/trunk/integer.c
* integer.c: changed a docstring.
------------------------------------------------------------------------
r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines
Changed paths:
M /calc/trunk/integer.c
* integer.c: adding this file to the project.
------------------------------------------------------------------------
~~~
sally看到她自己的344修訂,你做的343修改她看不到,從Subversion看來,兩次提交只是影響版本庫中不同位置上的兩個文件。然而,Subversion*顯示*了兩個文件有共同的歷史,在分支拷貝之前,他們使用同一個文件,所以你和Sally都看到版本號303到98的修改。
### 分支背后的關鍵概念
在這個章節你需要記住兩個重要的經驗。
1.
不像其他版本控制系統,Subversion的分支存在于真實的*正常文件系統*中,并不是存在于另外的維度,這些目錄只是恰巧保留了額外的歷史信息。
1.
Subversion并沒有內在的分支概念―只有拷貝,當你拷貝一個目錄,這個結果目錄就是一個“分支”,只是因為你給了它這樣一個含義而已。你可以換一種角度考慮,或者特別處理,但是對于Subversion它只是一個普通的拷貝的結果。
[[7](#)] Subversion不支持跨版本庫的拷貝,當使用**svn copy**或者**svn move**直接操作URL時你只能在同一個版本庫內操作。
- 第1章介紹
- Subversion的歷史
- Subversion的特性
- Subversion的架構
- 安裝Subversion
- Subversion的組件
- 快速入門
- 第2章基本概念
- 版本模型
- Subversion實戰
- 摘要
- 第3章指導教程
- 導入
- 修訂版本: 號碼、關鍵字和日期,噢,我的!
- 初始化的Checkout
- 基本的工作周期
- 檢驗歷史
- 其他有用的命令
- 摘要
- 第4章分支與合并
- 使用分支
- 在分支間拷貝修改
- 常見用例
- 轉換工作拷貝
- 標簽
- 分支維護
- 摘要
- 第5章版本庫管理
- 版本庫的創建和配置
- 版本庫維護
- 添加項目
- 摘要
- 第6章配置服務器
- 網絡模型
- svnserve,一個自定義的服務器
- httpd,Apache的HTTP服務器
- 支持多種版本庫訪問方法
- 第7章高級主題
- 屬性
- Peg和實施修訂版本
- 外部定義
- 賣主分支
- 本地化
- Subversion版本庫URL
- 第8章開發者信息
- 使用API
- 進入工作拷貝的管理區
- WebDAV
- 使用內存池編程
- 為Subversion做貢獻
- 第9章Subversion完全參考
- svn add
- svn blame
- svn cat
- svn checkout
- svn cleanup
- svn commit
- svn copy
- svn delete
- svn diff
- svn export
- svn help
- svn import
- svn info
- svn list
- svn log
- svn merge
- svn mkdir
- svn move
- svn propdel
- svn propedit
- svn propget
- svn proplist
- svn propset
- svn resolved
- svn revert
- svn status
- svn switch
- svn update
- svnadmin
- svnadmin create
- svnadmin deltify
- svnadmin dump
- svnadmin help
- svnadmin hotcopy
- svnadmin list-dblogs
- svnadmin list-unused-dblogs
- svnadmin load
- svnadmin lstxns
- svnadmin recover
- svnadmin rmtxns
- svnadmin setlog
- svnadmin verify
- svnlook
- svnlook author
- svnlook cat
- svnlook changed
- svnlook date
- svnlook diff
- svnlook dirs-changed
- svnlook help
- svnlook history
- svnlook info
- svnlook log
- svnlook propget
- svnlook proplist
- svnlook tree
- svnlook uuid
- svnlook youngest
- svnserve
- svnversion
- mod_dav_svn Configuration Directives
- 附錄A.Subversion對于CVS用戶
- 目錄的版本
- 更多離線操作
- 區分狀態和更新
- 分支和標簽
- 元數據屬性
- 沖突解決
- 二進制文件和轉化
- 版本化的模塊
- 認證
- 轉化CVS版本庫到Subversion
- 附錄C.WebDAV和自動版本化
- 自動版本化交互性
- Subversion和DeltaV
- 術語表