对一个项目同时使用svn和git两个远程仓库

以前曾经和朋友讨论过在一个项目中同时使用svn和git两个远程仓库的可能,如今真的遇到了这样的实际需求。

在开发WordPress插件External Media without Import的时候,因为希望插件能在WordPress官方渠道发布,所以需要在WordPress官方提供的svn远程仓库上托管这个项目。WordPress官方给我提供的svn仓库的地址是https://plugins.svn.wordpress.org/external-media-without-import/

但另一方面,考虑到github作为开源社区的人气,以及fork、pull request等代码贡献的便利性,我也想在github上托管这个项目。因此我在github上也创建了一个仓库:https://github.com/zzxiang/external-media-without-import.git

与此同时,我希望本地只需要维护一个项目文件夹,或者绝大部分操作只需要在一个文件夹中执行。这个文件夹由git管理,并可以方便地与WordPress的svn和github双方同步。不过后来经过一段时间摸索,我似乎只能做到让git和svn仓库的trunk分支同步,但这也足够了。

也就是说,我希望实现的应用场景如下图所示:




WordPress官方提供的svn仓库和github上的仓库在刚创建好的时候都是空库,里面没有任何文件;而在此之前我已经在本地的git管理的文件夹中工作了一段时间,里面已经有多条提交记录和多份文件。我希望将这些历史记录和文件都同步到远程的svn和git仓库。最后要达成的效果是远程svn仓库的trunk分支、远程git仓库的master分支和本地git仓库的master分支中的提交一一对应。这三个分支保持同步,完全相同。

与github的同步较为简单,毕竟本地和远程都是git仓库。因此先来解决和svn仓库同步的问题。我要将本地的git仓库和远程的svn仓库之间关联起来。

出于示例的目的,我在本地从零开始建立一个git仓库和一个svn仓库,并用它们模拟这种情况,让git仓库已有的提交全部同步到svn仓库,并且在此之后能直接向svn仓库提交以及直接从svn仓库更新。实际应用的时候将下文以 file:// 为前缀的svn仓库URL替换为实际的 svn:// 或 https:// 为前缀的URL即可。

先在本地创建一个git仓库,并往里面添加一些提交:


[plain]  view plain  copy
  1. mkdir local-git-repos  
  2. cd local-git-repos/  
  3. git init  
  4. touch test.txt  
  5. git add test.txt  
  6. git commit -am "Added test.txt."  
  7. echo aaaa >> test.txt  
  8. git commit -am "Added aaaa."  
  9. echo bbbb >> test.txt  
  10. git commit -am "Added bbbb."  

现在git仓库里应该有一个text.txt并且已经有三条提交:


[plain]  view plain  copy
  1. $ ls  
  2. test.txt  
  3. $ cat test.txt   
  4. aaaa  
  5. bbbb  
  6. $ git log  
  7. commit 15db9719b735c65b4d8ffc2cbf08b54ebad09e38  
  8. Author: Zhixiang Zhu <[email protected]>  
  9. Date:   Wed Jul 5 20:36:36 2017 +0800  
  10.    
  11.     Added bbbb.  
  12.    
  13. commit e1d6c1556fa210ca0a0b12e661ac5d23e5d215d2  
  14. Author: Zhixiang Zhu <[email protected]>  
  15. Date:   Wed Jul 5 20:36:35 2017 +0800  
  16.    
  17.     Added aaaa.  
  18.    
  19. commit d769c9a48932782a683336d478356ccd124077cd  
  20. Author: Zhixiang Zhu <[email protected]>  
  21. Date:   Wed Jul 5 20:36:35 2017 +0800  
  22.    
  23.     Added test.txt.  

接着创建一个svn仓库:


[plain]  view plain  copy
  1. cd ..  
  2. svnadmin create svn-repos  

用svn客户端在仓库里创建好trunk、branches和tags这样的标准目录:


[plain]  view plain  copy
  1. svn co file://`pwd`/svn-repos svn-client  
  2. cd svn-client/  
  3. mkdir trunk branches tags  
  4. svn add *  
  5. svn ci -m "Added trunk, branches and tags."  

接下来可以开始将git和svn关联起来,从现在起基本上所有操作都全部在git中执行,如果不需要开辟svn分支和标签的话,甚至可以将svn-client目录删掉。

进入git仓库,先用 git svn init 命令将svn仓库的地址添加到git目录的配置中:


[plain]  view plain  copy
  1. cd ../local-git-repos/  
  2. git svn init --stdlayout file://`pwd`/../svn-repos/ --prefix=svn/  

本地git目录里的.git/config文件中应该增加了如下一段配置:


[plain]  view plain  copy
  1. [svn-remote "svn"]  
  2.     url = file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos  
  3.     fetch = trunk:refs/remotes/svn/trunk  
  4.     branches = branches/*:refs/remotes/svn/*  
  5.     tags = tags/*:refs/remotes/svn/tags/*  

在日常工作中,一般是通过 git svn rebase 将svn仓库里的提交更新到本地的git仓库,并且将本地git仓库中还未同步到远程的提交rebase到已从远程仓库更新下来的提交之上。但现在就执行这条命令的话会报错:


[plain]  view plain  copy
  1. $ git svn rebase  
  2. Unable to determine upstream SVN information from working tree history  

git svn info 命令也会报同样的错误。这是因为git仓库中尚不存在远程svn仓库的对应分支。我们要先通过fetch命令获取svn仓库的内容,从而创建出这个远程分支。


[plain]  view plain  copy
  1. $ git svn fetch -rHEAD  
  2. r1 = 72572673ab962bd61cd3f244f8cf63beca1bc8f8 (refs/remotes/svn/trunk)  

实际应用中我曾遇到过fetch无效的现象,似乎是因为svn仓库中没有提交也没有文件。用svn客户端往仓库里提交一份文件(内容任意,注意不是文件夹)后,git这边就能fetch到东西了。

现在本地git目录中应该多了个远程分支对应svn仓库:


[plain]  view plain  copy
  1. $ git branch -a  
  2. * master  
  3.   remotes/svn/trunk  

此时也可以通过git日志看到本地git目录中svn分支的存在:


[plain]  view plain  copy
  1. $ git log --oneline --decorate --all --graph  
  2. * d9b5878 (svn/trunk) Added trunk, branches and tags.  
  3. * 15db971 (HEAD -> master) Added bbbb.  
  4. * e1d6c15 Added aaaa.  
  5. * d769c9a Added test.txt.  

现在将git仓库中已有的提交全部rebase到svn远程分支上:


[plain]  view plain  copy
  1. $ git rebase --onto remotes/svn/trunk --root master  
  2. First, rewinding head to replay your work on top of it...  
  3. Applying: Added test.txt.  
  4. Applying: Added aaaa.  
  5. Applying: Added bbbb.  

现在 git svn rebase 和 git svn info 应该能正常工作了:


[plain]  view plain  copy
  1. $ git svn rebase  
  2. Current branch master is up to date.  
  3. $ git svn info  
  4. Use of uninitialized value $lc_author in concatenation (.) or string at /usr/local/Cellar/git/2.8.1/libexec/git-core/git-svn line 1639.  
  5. Use of uninitialized value $lc_rev in concatenation (.) or string at /usr/local/Cellar/git/2.8.1/libexec/git-core/git-svn line 1640.  
  6. Path: .  
  7. URL: file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos/trunk  
  8. Repository Root: file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos  
  9. Repository UUID: 79d69224-e83d-4033-9427-31fe05b277a8  
  10. Revision: 1  
  11. Node Kind: directory  
  12. Schedule: normal  
  13. Last Changed Author:   
  14. Last Changed Rev:   
  15. Last Changed Date: 2017-07-06 14:22:31 +0800 (四, 06  7 2017)  

上面 git svn info 的输出中出现 Use of uninitialized value $lc_author 的提示是因为 Last Changed Author 和 Last Changed Rev 为空,从svn客户端中也看不到提交记录(其实我对这一点不是很明白,因为明明应该有一个创建trunk、branches和tags目录的提交):


[plain]  view plain  copy
  1. $ svn log  
  2. ------------------------------------------------------------------------  

将git仓库中的提交推送到svn仓库中,这个错误消息就会消失:


[plain]  view plain  copy
  1. $ git svn dcommit  
  2. Committing to file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos/trunk ...  
  3.     A   test.txt  
  4. Committed r2  
  5.     A   test.txt  
  6. r2 = 89de67ea07dc796a5ce4b96bb4786a890eab86d7 (refs/remotes/svn/trunk)  
  7.     M   test.txt  
  8. Committed r3  
  9.     M   test.txt  
  10. r3 = 1aeeb3dcc30e0b4c03e27981ff04dce11472151e (refs/remotes/svn/trunk)  
  11.     M   test.txt  
  12. Committed r4  
  13.     M   test.txt  
  14. r4 = 22a31f42be3a51c285b4fa07b3ef4e53bcf07625 (refs/remotes/svn/trunk)  
  15. No changes between aa2560cf0de937d4045bc2f30dcab46752d65b94 and refs/remotes/svn/trunk  
  16. Resetting to the latest refs/remotes/svn/trunk  
  17. $ git svn info  
  18. Path: .  
  19. URL: file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos/trunk  
  20. Repository Root: file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos  
  21. Repository UUID: 79d69224-e83d-4033-9427-31fe05b277a8  
  22. Revision: 4  
  23. Node Kind: directory  
  24. Schedule: normal  
  25. Last Changed Author: zhixiangzhu  
  26. Last Changed Rev: 4  
  27. Last Changed Date: 2017-07-06 20:37:07 +0800 (四, 06  7 2017)  

现在我们已经将本地git仓库和svn仓库成功关联起来。接下来要给本地git仓库添加远程git仓库的引用。这个工作比较简单,在本地git仓库目录中执行以下命令:


[plain]  view plain  copy
  1. git remote add origin https://github.com/zzxiang/external-media-without-import.git  


.git/config文件中应该会增加下面一段内容:


[plain]  view plain  copy
  1. [remote "origin"]  
  2.     url = https://github.com/zzxiang/external-media-without-import.git  
  3.     fetch = +refs/heads/*:refs/remotes/origin/*  


最后把本地git仓库中的提交全部push到远程git仓库:


[plain]  view plain  copy
  1. git push --set-upstream origin master  


这样本地git仓库和svn仓库及远程git仓库就都同步了。日常工作中使用以下四个命令在这三个仓库之间同步:

  • 本地git仓库向svn仓库推送提交: git svn dcommit 
  • 本地git仓库从svn仓库获取更新: git svn rebase 
  • 本地git仓库向远程git仓库推送提交: git push 
  • 本地git仓库从远程git仓库获取更新: git pull 

一般不需要再使用svn客户端。唯一的例外是需要在svn仓库中创建分支和标签的时候。虽然git也有 git svn branch 和 git svn tag 命令可用于该目的,但在我的环境中这两个命令却会报错:

[plain]  view plain  copy
  1. $ git svn tag 1.0  
  2. Copying https://plugins.svn.wordpress.org/external-media-without-import/trunk at r1669373 to https://plugins.svn.wordpress.org/external-media-without-import/tags/1.0...  
  3. Authentication failed: No more credentials or we tried too many times.  
  4. Authentication failed at /usr/local/Cellar/git/2.8.1/libexec/git-core/git-svn line 1196.  


后来我还是使用了svn客户端来创建svn仓库中的分支和标签。

本文参考了这篇文章:Using Git and Subversion Together


本文在我的独立博客上的地址:http://zxtechart.com/2017/07/16/use-two-remote-repos-of-svn-and-git-at-the-same-time-for-the-same-project/

猜你喜欢

转载自blog.csdn.net/thanklife/article/details/80162938