对一个项目同时使用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仓库,并往里面添加一些提交:


mkdir local-git-repos
cd local-git-repos/
git init
touch test.txt
git add test.txt
git commit -am "Added test.txt."
echo aaaa >> test.txt
git commit -am "Added aaaa."
echo bbbb >> test.txt
git commit -am "Added bbbb."

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


$ ls
test.txt
$ cat test.txt 
aaaa
bbbb
$ git log
commit 15db9719b735c65b4d8ffc2cbf08b54ebad09e38
Author: Zhixiang Zhu <zzxiang21cn@hotmail.com>
Date:   Wed Jul 5 20:36:36 2017 +0800
 
    Added bbbb.
 
commit e1d6c1556fa210ca0a0b12e661ac5d23e5d215d2
Author: Zhixiang Zhu <zzxiang21cn@hotmail.com>
Date:   Wed Jul 5 20:36:35 2017 +0800
 
    Added aaaa.
 
commit d769c9a48932782a683336d478356ccd124077cd
Author: Zhixiang Zhu <zzxiang21cn@hotmail.com>
Date:   Wed Jul 5 20:36:35 2017 +0800
 
    Added test.txt.

接着创建一个svn仓库:


cd ..
svnadmin create svn-repos

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


svn co file://`pwd`/svn-repos svn-client
cd svn-client/
mkdir trunk branches tags
svn add *
svn ci -m "Added trunk, branches and tags."

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

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


cd ../local-git-repos/
git svn init --stdlayout file://`pwd`/../svn-repos/ --prefix=svn/

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


[svn-remote "svn"]
	url = file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos
	fetch = trunk:refs/remotes/svn/trunk
	branches = branches/*:refs/remotes/svn/*
	tags = tags/*:refs/remotes/svn/tags/*

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


$ git svn rebase
Unable to determine upstream SVN information from working tree history

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


$ git svn fetch -rHEAD
r1 = 72572673ab962bd61cd3f244f8cf63beca1bc8f8 (refs/remotes/svn/trunk)

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

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


$ git branch -a
* master
  remotes/svn/trunk

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


$ git log --oneline --decorate --all --graph
* d9b5878 (svn/trunk) Added trunk, branches and tags.
* 15db971 (HEAD -> master) Added bbbb.
* e1d6c15 Added aaaa.
* d769c9a Added test.txt.

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


$ git rebase --onto remotes/svn/trunk --root master
First, rewinding head to replay your work on top of it...
Applying: Added test.txt.
Applying: Added aaaa.
Applying: Added bbbb.

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


$ git svn rebase
Current branch master is up to date.
$ git svn info
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.
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.
Path: .
URL: file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos/trunk
Repository Root: file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos
Repository UUID: 79d69224-e83d-4033-9427-31fe05b277a8
Revision: 1
Node Kind: directory
Schedule: normal
Last Changed Author: 
Last Changed Rev: 
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目录的提交):


$ svn log
------------------------------------------------------------------------

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


$ git svn dcommit
Committing to file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos/trunk ...
	A	test.txt
Committed r2
	A	test.txt
r2 = 89de67ea07dc796a5ce4b96bb4786a890eab86d7 (refs/remotes/svn/trunk)
	M	test.txt
Committed r3
	M	test.txt
r3 = 1aeeb3dcc30e0b4c03e27981ff04dce11472151e (refs/remotes/svn/trunk)
	M	test.txt
Committed r4
	M	test.txt
r4 = 22a31f42be3a51c285b4fa07b3ef4e53bcf07625 (refs/remotes/svn/trunk)
No changes between aa2560cf0de937d4045bc2f30dcab46752d65b94 and refs/remotes/svn/trunk
Resetting to the latest refs/remotes/svn/trunk
$ git svn info
Path: .
URL: file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos/trunk
Repository Root: file:///Users/zhixiangzhu/git-svn-test/local-git-repos/../svn-repos
Repository UUID: 79d69224-e83d-4033-9427-31fe05b277a8
Revision: 4
Node Kind: directory
Schedule: normal
Last Changed Author: zhixiangzhu
Last Changed Rev: 4
Last Changed Date: 2017-07-06 20:37:07 +0800 (四, 06  7 2017)

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


git remote add origin https://github.com/zzxiang/external-media-without-import.git


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


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


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


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 命令可用于该目的,但在我的环境中这两个命令却会报错:

$ git svn tag 1.0
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...
Authentication failed: No more credentials or we tried too many times.
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/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值