您是否有過這樣子的經驗, 在編修檔案的過程中, 尤其是在撰寫程式檔, 突然改爛了, 而想說: 如果我有辦法知道剛才改了什麼地方有多好!或是在一份大作業或者是專案的情況下, 須要多個人一同改一份檔案, 總須事先說好誰要改哪個檔案, 改的時候別人都不能動,以免在存檔的時候被互相覆蓋.
有這麼苦嗎?!
事實上這些工作, 都可以交給一套完善的版本控制系統 (Version Control System) 來解決. 接下來要介紹的這套系統名做 [subversion] , subversion透過一個集中管理的檔案庫(repository), 記錄下每一次的更動, 於是您可以取得每一次修改的紀錄, 甚至還可以多個人一起改, subversion 會負責把兩個人的修正合併起來!
安裝 Subversion
首先您須要在您所要工作的機器上安裝 Subversion 這套系統. 若您的機器上已經安裝好了, 您可以跳過這一步.
- 若您在 FreeBSD 底下, 請直接 cd /usr/ports/devel/subversion; make install
- 若您在 Microsoft Windows 底下, 請至 http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91 下載最新的 Subversion 安裝檔安裝即可, 或者是選擇跟 Explorer 整合在一起, 可以用右鍵來選擇功能的 TortoiseSVN http://tortoisesvn.tigris.org/download.html
若您使用其他的系統, 請參考 http://subversion.tigris.org/project_packages.html . 目前 Subversion 能在許多的作業系統上面執行.
checkout
對於每一個專案, 通常會有一個專屬的檔案庫. 若您不打算自行架設, 您可以至 https://opensvn.csie.org 申請一個.
首先您需要從檔案庫中取一份資料至目前目錄下. 您需要使用 `svn checkout 檔案庫路徑 [目的地路徑]`, 例如說:
~$ svn checkout http://OpenSVN.csie.org/demo A demo A demo/Makefile A demo/integer.c A demo/button.c ~$ cd demo # 切換到工作目錄 (demo) 下面 ~/demo$
這時 svn會自動建立一個目錄, 若無指定 `目的地路徑` 的話則會以檔案庫路徑最一個目路為準 (以上例來說是 demo) , svn 將會作用在這個目錄底下.
一般操作
有兩個重要的指令, 分別是 svn update , 用來將檔案庫中的版本和本地端同步 (例如說有別人已經修正並上傳了) ; 另一個是 svn status , 可以看到本地端更動尚未上傳的情況. 若您尚不了解這是做什麼的並沒有關係, 接下來就會看到範例.
加入檔案 svn add
若要增加新的檔案進來, 請透過 `svn add <file name>`, 例如說我現在開始撰寫 newfile.c:
~/demo$ vim newfile.c # 開始編輯 newfile.c ~/demo$ svn status # 看本地端的情況 ? newfile.c # 前面加 ?表示該檔案尚未加入版本控制 ~/demo$ svn add newfile.c # 將其加入版本控制 A newfile.c ~/demo$ svn status # 看本地端的情況 A newfile.c # 有新增的檔案, 尚未上傳
若要在這底下開新的目錄, 請透過 `svn mkdir <dir name>` , 或是先開好目錄, 再透過 `svn add <dir name>` 亦可.
修改
這時, 我可以針對我手上這一份檔案進行修改, 在我進行上傳(commit)前, 別人並不會看到這個更動, 所以並不用擔心我修正錯誤時, 別人會無法使用.
~/demo$ vim integer.c # 修改 integer.c ~/demo$ svn status A newfile.c M integer.c # M 表是 modified
commit
一旦修改到一個斷落或完成時 (若是在程式開發的情況下, 通常都是在至少可以順利 compile過時)需要下 commit 這個指令, 將我本地端的修改送回檔案庫; 而在每次 commit 的時候, 可以順便附帶一個訊息, 表示這次commit 時修正了什麼地方, 以方便之後在找尋的時候, 除了透過時間及作者外, 還能有一些其他的資訊. 我們稱這個訊息為 commitlog , 建議最好要填寫, 就算是只有一行五六個字, 也勝於什麼都沒有寫. 若您的 commit log 只有一行, 可以直接透過 -m"<your commit log>" 加在 commit 後面, 如:
~/demo$ svn commit -m "fix bug"
或是直接用 svn commit , 這時它會自動跳出一個編輯器讓您輸入. 接著會看到像是這樣的畫面:
Sending newfile.c Sending integer.c Transmitting file data.. Committed revision 57.
表示完成, 此時在別的地方進行 svn update 時, 就會含有剛才您更新的資訊.
總結
透過 Subversion 進行開發的流程:
1.只有第一次的時候須要進行 svn checkout , 之後請從 2. 起即可
svn checkout <repository url>
2.假設有其他人在之間有進行修正, 所以先 svn update 更新到最新的版本
svn update
3.進行修正
4.再同步一次
svn update
(這時可以再用 svn status 觀看一下目前的結果, 以及可以透過 svn diff 觀看修正內容. 這將在後文介紹)
5.若有問題則回 3 再進行修正. 要不然確認後, 上傳自己的修正
svn commit 生成patch文件: svn diff > patchFile // 整个工程的变动生成patch 或svn diff file > patchFile // 某个文件单独变动的patch svn回滚: svn revert FILE // 单个文件回滚 svn revert DIR --depth=infinity // 整个目录进行递归回滚 打patch: patch -p0 < test.patch // -p0 选项要从当前目录查找目的文件(夹) patch -p1 < test.patch // -p1 选项要从当前目录查找目的文件,不包含patch中的最上级目录(夹) 例如两个版本以a,b开头,而a,b并不是真正有效地代码路径,则这时候需要使用"-p1"参数。 a/src/... b/src/...1. 取消Add/Delete
取消文件svn revert 文件名取消目录svn revert --depth=infinity 目录名2. 回退版本方法1: 用svn merge1) 先 svn up,保证更新到最新的版本,如20;2) 然后用 svn log ,查看历史修改,找出要恢复的版本,如10 。如果想要更详细的了解情况,可以使用svn diff -r 10:20 [文件或目录];3) 回滚到版本号10:svn merge -r 20:10 [文件或目录],注意版本号之间的顺序,这个叫反向合并;4) 查看当前工作版本中的文件,如test.cpp和版本号10中文件的差别:svn diff -r 10 test.cpp, 有差别则手动改之;5) 若无差别,则提交:svn ci -m“back to r 10,xxxxx” [文件或目录]。这时svn库中会生成新的版本,如21。方法2: 用svn up前2步如方法1,然后直接 svn up -r 10。当前的工作版本就是版本10了。但是注意,这时svn库中并不会生成新的版本,下次svn up之后,还是会回到当前的版本。========================改动已经被提交(commit)。用svn merge命令来进行回滚。回滚的操作过程如下:1、保证我们拿到的是最新代码:svn update假设最新版本号是28。2、然后找出要回滚的确切版本号:svn log假设根据svn log日志查出要回滚的版本号是25,此处的something可以是文件、目录或整个项目如果想要更详细的了解情况,可以使用svn diff -r 28:25 ""3、回滚到版本号25:svn merge -r 28:25 ""为了保险起见,再次确认回滚的结果:svn diff ""发现正确无误,提交。4、提交回滚:svn commit -m "Revert revision from r28 to r25,because of ..."提交后版本变成了29。将以上操作总结为三条如下:1. svn update,svn log,找到最新版本(latest revision)2. 找到自己想要回滚的版本号(rollbak revision)3. 用svn merge来回滚: svn merge -r : something更新至某个版本svn update -r 版本号svn help updateupdate (up): 将版本库的修改合并到工作副本中。用法: update [PATH...]如果没有指定版本,则将工作副本更新到 HEAD 版本。否则同步到 -r 选项所指定的版本。每更新一项就输出一行信息,使用首字符来报告执行的动作。这些字符的含义是:A 已添加D 已删除U 已更新C 合并冲突G 合并成功E 已存在R 已替换第一列字符报告项目本身。第二列表示报告项目属性。第三列中的字符“B”表示此文件上的锁被终止或窃取。第四列中的字符“C”表示树冲突,同时“C”出现在第一列或第二列,分别表示内容冲突或属性冲突。如果使用了 “--force” 选项,在工作副本中未版本控制的障碍路径,不会自动导致签出失败。 如果障碍路径与版本库中的对应路径类型相同(文件或目录),它将成为受版本控制的路径,但是内容不改变。它意味着障碍路径的子孙,也可能是障碍路径,同样会受版本控制。对于障碍路径中的文件,如果与版本库内的不同,将视为工作副本发生本地修改。版本库中的所有属性都应用于障碍路径。用第一列字符 “E” 来报告障碍路径。如果工作副本中并没有指定的更新目标,但是有其父目录,那么就在指定路径上将目标检出到其父目录中。如果使用了选项 --parents,就会创建目标的所有不存在的父目录 (使用选项 --depth=empty)。使用“--set-depth”选项设置此操作目标的工作副本之新深度。有效选项:-r [--revision] ARG : ARG (一些命令也接受ARG1:ARG2范围)版本参数可以是如下之一:NUMBER 版本号'{' DATE '}' 在指定时间以后的版本'HEAD' 版本库中的最新版本'BASE' 工作副本的基线版本'COMMITTED' 最后提交或基线之前'PREV' COMMITTED的前一版本-N [--non-recursive] : 过时;尝试 --depth=files 或 --depth=immediates--depth ARG : 限制操作深度是 ARG ('empty', 'files','immediates', 或 'infinity')--set-depth ARG : 设置工作副本的新深度为 ARG('exclude','empty', 'files', 'immediates', 或 'infinity')-q [--quiet] : 不打印信息,或只打印概要信息--diff3-cmd ARG : 使用 ARG 作为合并命令--force : 强制操作运行--ignore-externals : 忽略外部项目--changelist [--cl] ARG : 只能对修改列表 ARG 的成员操作--editor-cmd ARG : 使用 ARG 作为外部编辑器--accept ARG : 指定自动解决冲突动作('postpone', 'working', 'base', 'mine-conflict','theirs-conflict', 'mine-full', 'theirs-full','edit', 'launch')(shorthand: 'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l')--parents : 创建中间目录全局选项:--username ARG : 指定用户名称 ARG--password ARG : 指定密码 ARG--no-auth-cache : 不要缓存用户认证令牌--non-interactive : 不要交互提示--trust-server-cert : 不提示的接受未知的证书颁发机构发行的 SSL 服务器证书(只用于选项 “--non-interactive”)--config-dir ARG : 从目录 ARG 读取用户配置文件--config-option ARG : 以下属格式设置用户配置选项:FILE:SECTION:OPTION=[VALUE]例如:servers:global:http-library=serf