Git与Subversion
git svn 这个工具允许你使用 Git 作为连接到 Subversion 有效的客户端,这样你可以使用 Git 所有本地的功能然后如同正在本地使用 Subversion 一样推送到 Subversion 服务器。 这意味着你可以在本地做新建分支与合并分支、使用暂存区、使用变基与拣选等等的事情,同时协作者还在继续使用他们黑暗又古老的方式。
设置
创建一个新的本地 Subversion 仓库
# svnsync init 命令同步这个项目到本地的机器 $ svnsync init file:///tmp/test-svn http://your-svn-server.example.org/svn/
# 克隆代码 $ svnsync sync file:///tmp/test-svn
开始
通过 git svn clone 命令开始,将整个 Subversion 仓库导入到一个本地 Git 仓库
# -T trunk -b branches -t tags 部分告诉 Git Subversion 仓库遵循基本的分支与标签惯例 $ git svn clone file:///tmp/test-svn -T trunk -b branches -t tags # $ git svn clone file:///tmp/test-svn -s 等价于上面命令
这相当于运行了两个命令—— git svn init 以及紧接着的 git svn fetch ——你提供的 URL。
$ git branch -a * master remotes/origin/my-calc-branch remotes/origin/tags/2.0.2 remotes/origin/tags/release-2.0.1 remotes/origin/tags/release-2.0.2 remotes/origin/tags/release-2.0.2rc1 remotes/origin/trunk
注意:Git 在从 Git 服务器克隆时,Git 直接将标签抓取至 refs/tags,而不是将它们看作分支。
提交会Subversion
要推送到一个 Subversion 服务器,运行 git svn dcommit 命令:
$ git svn dcommit
这会拿走你在 Subversion 服务器代码之上所做的所有提交,针对每一个做一个 Subversion 提交,然后重写你本地的 Git 提交来包含一个唯一的标识符。 这很重要因为这意味着所有你的提交的 SHA-1 校验和都改变了。
查看最后一次提交,有新的 git-svn-id 被添加,如果想要既推送到一个Git 服务器又推送到一个 Subversion 服务器,必须先推送(dcommit)到 Subversion 服务器,因为这个操作 会改变你的提交数据。
拉取新改动
在推送时,另一人尝试推送修改会导致冲突。 那次修改会被拒绝直到你合并他们的工作。
可以运行 git svn rebase,它会从服务器拉取任何你本地还没有的改动,并将你所有的工作变基到服务器的内容之上:
$ git svn rebase
然后修改,再dcommit。
注意,和 Git 需要你在推送前合并本地还没有的上游工作不同的是,git svn 只会在修改发生冲突时要求你那样做(更像是 Subversion 工作的行为)。 如果其他人推送一个文件的修改然后你推送了另一个文件的修改,你
的 dcommit 命令会正常工作
注意:因为结果是当你推送后项目的状态并不存在于你的电脑中。 如果修改并未冲突但却是不兼容的,可能会引起一些难以诊断的问题。这与使用 Git 服务器并不同——在 Git 中,可以在发布前完全测试客户端系统的状态,然而在 SVN 中,你甚至不能立即确定在提交前与提交后的状态是相同的。
每隔一会儿运行 git svn rebase 确保你的代码始终是最新的。如果有本地的修改,在运行 git svn rebase 之前要么储藏你的工作要么做一次临时的提交,不然,当变基会导致合并冲突时,命令会终止。
Git分支问题
当适应了 Git 的工作流程,你大概会想要创建主题分支,在上面做一些工作,然后将它们合并入主分支。 如果你正通过 git svn 推送到一个 Subversion 服务器,你可能想要把你的工作变基到一个单独的分支上,而不是将 分支合并到一起。 比较喜欢变基的原因是因为 Subversion 有一个线性的历史并且无法像 Git 一样处理合并,所以 git svn 在将快照转换成 Subversion 提交时,只会保留第一父提交。
创建一个新的 SVN 分支
在 Subversion 中创建一个新分支,运行 git svn branch :
$ git svn branch opera
这与 Subversion 中的 svn copy trunk branches/opera 命令作用相同并且是在 Subversion 服务器中操作。 需要重点注意的是它并不会检出到那个分支;如果你在这时提交,提交会进入服务器的 trunk 分支,而不是 opera分支。
切换活动分支
Git 通过查找在历史中 Subversion 分支的头部来指出你的提交将会到哪一个分支——应该只有一个,并且它应该是在当前分支历史中最后一个有 git-svn-id 的。
如果想要同时在不止一个分支上工作,可以通过在导入的那个分支 Subversion 提交开始来设置本地分支 dcommit 到特定的 Subversion 分支。 如果想要一个可以单独在上面工作的 opera 分支,可以运行
$ git branch opera remotes/origin/opera
现在,如果想要将你的 opera 分支合并入 trunk(你的 master 分支),可以用一个正常的 git merge 来这样做。 但是你需要通过 -m 来提供一个描述性的提交信息,否则合并信息会是没有用的 “Merge branch opera”。
使用了 git merge 后,如果将数据推送回一个Subversion 服务器,Subversion 服务器不支持那些跟踪多个父结点的提交; 所以,当推送完成后,它看起来会是一个将其他分支的所有提交压缩在一起的单独提交。 在合并一个分支到另一个分支后,你并不能像 Git 中那样轻松地回到原来的分支继续工作。 你运行的 dcommit 命令会将哪个分支被合并进来的信息抹掉,所以后续的 合并基础计算会是错的—— dcommit 会使你的 git merge 结果看起来像是运行了 git merge --squash。 不幸的是,没有一个好的方式来避免这种情形—— Subversion 无法存储这个信息, 所以当使用它做为服务器时你总是会被它的限制打垮。 为了避免这些问题,应该在合并到主干后删除本地分支(本例中是 opera)。
Subversion 命令
# git svn log 来查看 SVN 格式的提交历史: $ git svn log
注意:它是离线工作的,它只会显示已经提交到 Subversion 服务器上的提交。 还未 dcommit的本地 Git 提交并不会显示;同样也不会显示这段时间中其他人推送到 Subversion 服务器上的提交。
Git-Svn 总结
当你不得不使用 Subversion 服务器或者其他必须运行一个 Subversion 服务器的开发环境时,git svn 工具很有用。 你应该把它当做一个不完全的 Git,然而,你要是不用它的话,就会在做转换的过程中遇到很多麻烦的问
题。 为了不惹麻烦,尽量遵守这些准则:
- 保持一个线性的 Git 历史,其中不能有 git merge 生成的合并提交。 把你在主线分支外开发的全部工作变基到主线分支;而不要合并入主线分支。
- 不要建立一个单独的 Git 服务器,也不要在 Git 服务器上协作。 可以用一台 Git 服务器来帮助新来的开发者加速克隆,但是不要推送任何不包含 git-svn-id 条目的东西。 你可能会需要增加一个 pre-receive 钩子来检查每一个提交信息是否包含 git-svn-id 并且拒绝任何未包含的提交。
Subversion
通过 git svn ,可以轻松地使用那些指令来 git svn clone 一个仓库,停止使用 Subversion 服务器,推送到一个新的 Git 服务器,然后就可以开始使用了。 如果你想要历史,可以从Subversion 服务器上尽可能快地拉取数据来完成这件事(这可能会花费一些时间)。
- 需要解决作者信息问题:需要一个 Subversion 用户到 Git 用户的映射。
- 导入后的清理工作,清理 git svn 设置的奇怪的引用;清理trunk的额外分支(trunk 引用和 master 指向同一个位置,Git 中 master 最为常用)。
- 将新 Git 服务器添加为远程仓库并推送到上面



