緣起
話說目前在開發的習慣是使用 GIT 來當做版本控制系統,但有的時候會有需要把原來在 Git 開發的東西丟到內部的 SVN 上。
雖然有 git-svn 可以使用,但 git-svn 一般的流程是抓取 SVN 原有的資料,再繼續往上堆疊,而且到最後會變成以 SVN 為主(每次 svn rebase 後 commit 的編號都會變)。
而對我而言,理想的工作流程是:
- 在本機使用 git 來開發,所有流程還是都照 Git Workflow 來做。
- 開一個 forSVN 的 branch,這個 branch 是專門和 SVN 同步用的。
- 等到開發到一定的程度後(例如完成某個大功能),先把他 merge 到 forSVN 後再用 git svn dcommit 丟到 SVN 上。
在網上找了半天,照著這篇的步驟,大致上是可以成功的,但中間會有一些小問題需要解決,在這邊稍微筆記一下。
建立 SVN 目錄結構
Note
在開始之前,SVN 環境假設如下:
- 你已經有一個新建立的、空的 SVN 倉儲在 svn://svnserver/myProject/
- 你打算把 Git 裡 forSVN 的 branch 資料同步到 svn://svnserver/myProject/trunk/
首先,因為如果 SVN 倉儲是空的話,Git 沒辦法抓取任何的資料,所以一開始我們先建立 trunk 這個目錄,使用的指令如下:
$ svn mkdir svn://svnserver/myProject/trunk/
這樣會建立 svn://svnserver/myProject/trunk/ 這個目錄,之後我們所有的變動,都會匯出到這個目錄裡,這個目錄相當於 git 中的 master 分支。
設定 Git 與抓取 SVN 的第一個 commit
有了 SVN 中的第一個 commit 之後,我們就可以設定 Git 來抓取這個 SVN。
首先,先編輯 .git/config 這個檔案,在最後面加入如下的設定:
[svn-remote "svn"]
url = svn://svnserver/myProject
fetch = trunk:refs/remotes/svn
之後再使用下列的指令,抓回 SVN 中的第一個 commit:
$ git svn fetch
建立 forSVN 分支
為了避免與 SVN 的同步干擾到一般的開發流程,所以在進行匯出前,我先開了一個 forSVN 的分支,而以後 SVN 上的內容,主要是與這個分支作同步,不去碰其他的分支。
$ git checkout -b forSVN
將 Git 的歷史記錄匯到 SVN
切到 forSVN 這個分支之後,我們現在可以開始匯出原來的歷史記錄了,我們使用下面的指令,將原來 master 分支裡的資料,加到原本 SVN 的第一個 commit 上。
$ git rebase remotes/svn
這個時候要注意的是,如果你原本的 Git 就已經有很多記錄,那很有可能會出現 conflict 的情況。
如果出現 conflict 的話,原來 rebase 的動作就會停下來,並且告訴你哪個檔案有 conflict,這個時候請先修改那個檔案,然後用 git add 來標示 conflict 已解決,再用 git rebase --continue 繼續未完成的 rebase 動作。
等到 rebase 完成後,就可以用下列的指令將目前 forSVN 分支上的 commit 歷史,倒到 SVN 上囉。
$ git svn dcommit
之後的開發流程
經過上述的步驟,現在我們的 SVN 是和 Git 中的 forSVN 分枝同步。
在之後開發的流程中,我們依然是以原本的 master 或 develop branch 來進行開發,這樣就不用改變原本使用 git 開發的任何習慣。
等到你覺得差不多該把目前的進度倒回 SVN 的時候,只要先切到 forSVN 分支,merge 進原本的 master 或 develop 分枝,再 svn dcommit 回 SVN 上就可以了。
只不過在做合併的時候,記得要使用 subtree 這個合併策略,不然 svn dcommit 會認為你沒更新。
$ git checkout forSVN
$ git merge -s subtree develop
$ git svn dcommit
透過以上的方法,我們就可以把最新的進度同步到 SVN 上囉!
回響