[GIT][SVN] 把 Git 的資料倒回 SVN

緣起

話說目前在開發的習慣是使用 GIT 來當做版本控制系統,但有的時候會有需要把原來在 Git 開發的東西丟到內部的 SVN 上。

雖然有 git-svn 可以使用,但 git-svn 一般的流程是抓取 SVN 原有的資料,再繼續往上堆疊,而且到最後會變成以 SVN 為主(每次 svn rebase 後 commit 的編號都會變)。

而對我而言,理想的工作流程是:

  1. 在本機使用 git 來開發,所有流程還是都照 Git Workflow 來做。
  2. 開一個 forSVN 的 branch,這個 branch 是專門和 SVN 同步用的。
  3. 等到開發到一定的程度後(例如完成某個大功能),先把他 merge 到 forSVN 後再用 git svn dcommit 丟到 SVN 上。

在網上找了半天,照著這篇的步驟,大致上是可以成功的,但中間會有一些小問題需要解決,在這邊稍微筆記一下。

建立 SVN 目錄結構

Note

在開始之前,SVN 環境假設如下:

  1. 你已經有一個新建立的、空的 SVN 倉儲在 svn://svnserver/myProject/
  2. 你打算把 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 分枝同步。

在之後開發的流程中,我們依然是以原本的 masterdevelop branch 來進行開發,這樣就不用改變原本使用 git 開發的任何習慣。

等到你覺得差不多該把目前的進度倒回 SVN 的時候,只要先切到 forSVN 分支,merge 進原本的 masterdevelop 分枝,再 svn dcommit 回 SVN 上就可以了。

只不過在做合併的時候,記得要使用 subtree 這個合併策略,不然 svn dcommit 會認為你沒更新。

$ git checkout forSVN
$ git merge -s subtree develop
$ git svn dcommit

透過以上的方法,我們就可以把最新的進度同步到 SVN 上囉!

回響