svn-sync-external.sh
因為 Subversion 目前沒有 multiple repository 的功能,無法跨 repositories 作 copy/move/switch/diff/merge 等動作,在跨 project 整合時,會有些麻煩。因此,我寫了一個叫 svn-sync-external.sh 的 shell script,希望能去除這些麻煩。
這些是什麼麻煩呢?舉我現在碰到的需求為例,我現在要將 Lady BBS 從古老的 SOB 改成用 pttbbs。剛好 pttbbs 有利用 Subversion 提供 Ptt Source,按照 SVNBook 的說法,利用 vendor branches 理論上可以讓我隨時更新到最新版的 pttbbs。
然而,事情其實並不是這麼簡單地。最大的癥結點,在於我也會改動到 BBS 的 source,即使是使用了 vendor branches 技法,因為是使用 import 將 Ptt Source 拉回來,所以實際上是已經與 Ptt Source 拖勾了,仍然是必須要進行許多手動的操作,以便把 Ptt Source 版本之間的差異,施行到我自己的 source tree 裡。
若只是偶爾在大改版的時候作一遍,這也還好,但若想要緊緊追著 vendor branch,那就麻煩至極了。而且換個角度來說,若僅在大改版時,更新 vendor branch,難保不會因為更動太大,使自己做的那些改變,無法相容於新版 vendor branch。若能常常追著 vendor branch 跑,或是能夠以「步進」的方式,將一次的大幅度版本更新,改以很多次小幅度的版本更新進行,就可以把衝突降低到最小。但無論如何,要這麼做,就必須先把「手動 merge」的麻煩,降到最低,最好是能夠就像 svn:externals 一樣,下一個指令,就做完所有事情。
svn-sync-external.sh 這隻 shell script 的基本原理是,用 svn export 把 Ptt Source 從遠端拉過來,然後 svn add 進自己的 repository 裡,並加上 sync:url 與 sync:revision 這兩個 properties 紀錄來源。之後,要更新 Ptt Source 時,就先用 svn diff 從 sync:url 把新的 revision 與原來的 revision (記錄在 sync:revision) 之間差異拉回來存成 universal diff format,再用 patch 將版本之間的差異,施行進自己的 working directory 裡,然後,該 svn add 的加一加,該 svn rm 的刪一刪,就作成了新的 change set,人工確認過之後,便可以 svn commit 進去。
有別於 svn:externals,我把透過 svn-sync-external.sh 拉進來的遠端 source,稱作 sync-external。建立新的 sync-external 的程序,大致如下 (使用以 + 開頭的行展示內部的運作):
SHELL> svn-sync-external.sh import \ http://opensvn.csie.org/pttbbs/trunk/pttbbs \ pttbbs-trunk+ rev=`svn-maxver.sh http://opensvn.csie.org/pttbbs/trunk/pttbbs`+ svn export --revision $rev http://opensvn.csie.org/pttbbs/trunk/pttbbs pttbbs-trunk+ svn add pttbbs-trunk+ svn propset sync:url http://opensvn.csie.org/pttbbs/trunk/pttbbs+ svn propset sync:revision $rev pttbbs-trunk
而建立好 sync-external 之後,就可以隨時如下面的程序,將 sync-external 更新到最新 (或指定) 的版本:
SHELL> svn-sync-external.sh synchronize pttbbs-trunk+ url=`svn propget sync:url`+ oldrev=`svn propget sync:url`+ newrev=`svn-maxver.sh $url`+ patch_file=`mktemp /tmp/foo`+ patch_suffix=`basename $patch_file`+ svn diff --revision $oldrev:$newrev $url >> $patch_file+ cat $patch_file | patch --unified --quiet --suffix $patch_suffix --directory=pttbbs-trunk+ svn propset sync:revision $newrev+ svn status | grep ^\? | sed -e s/^\?// | xargs svn add;+ svn status | grep ^! | sed -e s/^\!// | xargs svn rm;+ rm -f $patch_file+ find $path -type f -name "*$patch_suffix" | xargs rm -f SHELL> svn commit pttbbs-trunk...
有了 sync-external 之後,我們就可以隨時用 svn 的 copy/move/switch/diff/merge 操作來自 Ptt Source 的程式碼,因為那已經是在自己的 repository 裡了。而當需要更新 Ptt Source 的版本時,就再執行一次 svn-sync-external.sh sync pttbbs-trunk,就可以更新了。又,因為內部是以 svn diff 加上 patch 在運作的,所以搭配 --revision 參數,可以任意指定要 sychronize 到哪一個版本,換句話說,要 down-grade 也是可以做到的。
目前的 svn-sync-external.sh 已經實作了 import 與 sychronize 兩個 sub commands,我計畫再實作 merge、status 和 switch 等 sub commands,這樣應該就蠻完整的了。
在 SVNBook 的 vendor branches 章節後面有提到 svn_load_dirs.pl 這隻程式,也可以做到類似的效果。不過因為其行為有些不太符合我的需求,因此我就沒有直接用,而是自己寫一個 script 來用。一來似乎不能 down-grade,二來 silence commit 也蠻可怕,更重要的是,我寫好了才發現有這東西可以用。不過無論如何,這畢竟只是簡單地將一些原本可以手動完成的工作自動化而已,如要獲得真正的 multiple repository 的能力,還是要依靠如 SVK 之類的 svn-addon 系統,才有辦法。



5 Comments
看到這篇,記得小時候在讀 JeffHung 前輩的大作時,就深深為強度的技術耐性所感動,沒想到 de-centralized version control with Subversion 也能這樣搞個 script 來作,實在太神奇了。
還是用 svk 才是正道阿 :)
不過還是感謝分享。
從 《Version Control System Comparison》看到的:
所以,除了 SVK 之外,Shlomi Fish 的 svn-push 也可以用來做 remote repository replication。
1.那個svn-sync-external.sh的連結已經失效了耶~請問能再貼一次內容嗎?
2.最近最近我受人之託欲更新系上BBS
得知系上BBS版本為早期之台大陽光沙灘(SOB)所架成
我知道Ptt有所謂的"沙灘變身術"可以轉換陽光沙灘之使用者資料
但我看了pttbbs/mbbsd/merge.c之後一頭霧水
如果我欲以merge.c轉換系上之使用者資料的話有哪些地方比較重要需要去重改的?
至少陽光沙灘BBS的路徑需要改吧?!
不過我找不到他在哪裡define....Orz
所以想請教您如果您有成功轉換的話是否可以給我您轉換的程式呢?
如果不方便在此回覆的話也可以回信到我信箱,感激不盡!
www,
現在連結已經修好了。這算是之前 wordpress 的 bug (feature, actually)。
至於 bbs 資料轉換的問題,我要再參詳一下,才能跟您報告。
您好,已過多日,不知道關於bbs資料轉換的問題您研究的如何了?
請原諒小弟不才,無法自行解決這個問題而一再打擾。
One Backlink
雜談 2005
Jserv 兄肯定是謬讚了。小弟既沒做過什麼豐功偉業,也不怎麼懂得造福人群,兢兢業業過日子罷了。倒是看到 Jserv 兄個人首頁上那長長的一串 projects,才不禁讓人肅然起敬。
若真能由器入
Post a Comment