如何将带有历史记录的SVN存储库迁移到新的Git存储库?

1619

我读了Git手册、常见问题解答、Git-SVN快速入门等等,它们都解释了这个和那个,但是没有任何地方可以找到像这样简单的说明:

SVN存储库位于:svn:// myserver / path / to / svn / repos

Git存储库位于:git:// myserver / path / to / git / repos

git-do-the-magic-svn-import-with-history \
svn://myserver/path/to/svn/repos \
git://myserver/path/to/git/repos

我不认为这会那么简单,也不指望这是一条命令就搞定。但我期望它不要去解释任何事情——只需要说明根据这个示例应该采取哪些步骤。


10
越来越简单了,我刚刚自己完成了这个过程,并在 Stack Overflow 的帮助下记录了我的发现。http://jmoses.co/2014/03/21/moving-from-svn-to-git.html - John Moses
使用下面的Casey答案,但在运行“svn clone ...”命令之前,请查看如何将额外的“Visual SVN Server”行添加到您的user.txt文件中...在这里:https://dev59.com/_V_Va4cB1Zd3GeqPUpSS - Entree
2
此外,如果您在GitHub个人资料中勾选了“使电子邮件保密”选项,请使用yourgituser@users.noreply.github.com作为users.txt中的电子邮件地址进行匹配,以避免您的真实电子邮件地址出现在提交记录中。 - Entree
要从Google Code迁移,请阅读:如何恢复Google Code SVN项目并迁移到Github - kenorb
34个回答

1656

创建一个用户文件(例如 users.txt),用于将 SVN 用户映射到 Git:

user1 = First Last Name <email@address.com>
user2 = First Last Name <email@address.com>
...

您可以使用以下一行代码从您现有的 SVN 存储库构建模板:

svn log -q | awk -F '|' '/^r/ {gsub(/ /, "", $2); sub(" $", "", $2); print $2" = "$2" <"$2">"}' | sort -u > users.txt

如果 SVN 发现一个缺失的 SVN 用户,它将停止工作,不会继续执行。但之后,你可以更新该文件,并从你离开的地方继续操作。

现在从代码库中拉取 SVN 数据:

git svn clone --stdlayout --no-metadata --authors-file=users.txt svn://hostname/path dest_dir-tmp

这个命令会在dest_dir-tmp里创建一个新的Git仓库,并开始拉取SVN仓库。注意,“--stdlayout”标志意味着您有常见的“trunk /,branches /,tags /” SVN布局。如果您的布局不同,请熟悉--tags--branches--trunk选项(通常使用git svn help)。

所有常见的协议都可以使用:svn://http://https://。URL应该指向基本仓库,类似于http://svn.mycompany.com/myrepo/repository。URL字符串应包括/trunk/tag/branches

请注意,在执行此命令后,它经常看起来像操作正在“挂起/冻结”,在初始化新仓库后很长时间可能会卡住,这很正常。最终,您将看到指示正在迁移的日志消息。

还要注意,如果省略了--no-metadata标志,则Git将将相应的SVN修订信息附加到提交消息中(即git-svn-id: svn://svn.mycompany.com/myrepo/<branchname/trunk>@<RevisionNumber> <Repository UUID>

如果找不到用户名,请更新您的users.txt文件,然后执行以下操作:

cd dest_dir-tmp
git svn fetch

如果你有一个大项目,直到所有Subversion提交被获取,你可能需要重复执行最后一个命令多次:

git svn fetch

当完成时,Git将把SVN的trunk检出到一个新分支中。任何其他分支都设置为远程分支。您可以使用以下命令查看其他SVN分支:

git branch -r

如果你想在仓库中保留其他远程分支,你需要手动为每个分支创建一个本地分支。(跳过trunk/master) 如果不这样做,这些分支在最后一步将无法被克隆。

git checkout -b local_branch remote_branch
# It's OK if local_branch and remote_branch are the same names

标签被导入为分支。您需要创建一个本地分支,打上标签,然后删除该分支来将其作为 Git 中的标签。要使用标签“v1”执行此操作:

git checkout -b tag_v1 remotes/tags/v1
git checkout master
git tag v1 tag_v1
git branch -D tag_v1

将您的GIT-SVN存储库克隆到一个干净的Git存储库中:

git clone dest_dir-tmp dest_dir
rm -rf dest_dir-tmp
cd dest_dir

你之前从远程分支创建的本地分支只会作为远程分支复制到新克隆的存储库中。(跳过主干/主分支。)对于每个想要保留的分支:

git checkout -b local_branch origin/remote_branch

最后,从指向已删除临时仓库的干净 Git 仓库中删除远程引用:

git remote rm origin

38
Eelke的这篇博客文章是上面回答的一个很好的参考。http://blokspeed.net/blog/2010/09/converting-from-subversion-to-git/ - kgriffs
4
这个方法真的很厉害,按照这些步骤操作后,我把所有东西都整理好了,除了分支:最后一步完成后,它们只存在于远程(所以当我输入命令:git remote rm origin时,它们就消失了)。 - Dirty Henry
5
GitHub具有非常方便的逐步操作指南:https://github.com/nirvdrum/svn2git#readme - Dan Nissenbaum
12
针对使用 Windows 的用户,我基于以下方法创建了一个 PowerShell 脚本: https://gist.github.com/Gimly/90df046dc38181bb18de - Gimly
9
对于具有大量历史记录的大型仓库,这个过程是缓慢而乏味的。我放弃了尝试迁移所有旧分支,只迁移了主干。 - Jess
显示剩余28条评论

551

神奇:

$ git svn clone http://svn/repo/here/trunk

Git和SVN操作非常不同。你需要学习Git,并且如果你想跟踪来自SVN上游的变更,你需要学习git-svngit-svn 主页面有一个很好的示例部分

$ git svn --help

146
@Casey的答案更好地回答了原始问题。 - Doug Wilson
3
这会保留分支和所有内容吗?还是只克隆主干? - Heetola
7
这将只克隆主干。有一个替代方案,请参见Casey的答案。 - sleske
4
@DougWilson,但我在这里看不到Casey的答案。下面的回答是13位作者开始于“创建一个用户文件”的答案吗? - Andrey Regentov
85
对于其他想知道“Casey's answer”是什么的人,这是指在这里的许多评论中提到的这个答案(Casey将其昵称更改为cmcginty)。 - Stefan Monov
显示剩余4条评论

205

清洁地将您的Subversion仓库迁移到Git仓库。首先您需要创建一个将您的Subversion提交作者名称映射到Git提交者的文件,比如说~/authors.txt

jmaddox = Jon Maddox <jon@gmail.com>
bigpappa = Brian Biggs <bigpappa@gmail.com>

然后,您可以将Subversion数据下载到Git存储库中:

mkdir repo && cd repo
git svn init http://subversion/repo --no-metadata
git config svn.authorsfile ~/authors.txt
git svn fetch

如果你使用的是Mac系统,可以通过安装git-core +svn来从MacPorts获取git-svn

如果你的Subversion仓库与所需的Git仓库在同一台机器上,那么你可以使用这种初始化步骤的语法,否则都一样:

git svn init file:///home/user/repoName --no-metadata

1
正如我在另一个答案中所评论的,我不得不删除users.txt文件中=周围的空格,因为导入操作一直失败,并且我获取到了一个空的存储库。 - Sebastián Grignoli
8
啊!简单而有效的解释。在我的情况下,file:/// 拒绝工作了,我只好使用 svnserve.exe --daemon 然后使用 svn://localhost/home/user/repo 代替。 - Daniel Reis
在我的运行Mountain Lion的Mac上,git svn无法工作,直到我进入Xcode并安装了偏好设置中下载选项卡中找到的命令行工具。或者,我可以只安装在Apple开发者网站上找到的OS X Mountain Lion命令行工具。 - Drew
4
针对我的情况,我需要将文件authors.txt转换为无BOM的utf-8编码。 - Silvan
1
这对我非常有帮助!一旦我有了本地仓库,我使用了cmcginty的帖子,从“将您的GIT-SVN存储库克隆到干净的Git存储库:”开始。我认为我喜欢@zoul的答案的主要原因是他使用了git svn initgit svn config然后最终是git svn fetch,因为这样做更容易,我不得不多次获取才能做到这一点。cmcginty的单行git svn clone,它执行所有三个操作,对我来说太混乱了。 - mike
这是唯一对我非常有效的方法。然而,它不仅迁移存储库,还会创建工作区(将存储库文件提取到磁盘上)。有没有办法避免这种情况,只保留.git文件夹? - NoOne

74

4
这个翻译的内容是:“这个修复会解决在SVN中允许但在Git中不允许的标签和分支名称中的空格问题吗?” - spazm
2
这个使用指南很有帮助:http://www.troyhunt.com/2014/08/migrating-from-subversion-to-git-with.html - Morten Holmgaard
这对我来说失败了,出现了一个问题:https://groups.google.com/forum/#!topic/msysgit/7MQVwRO-2N4 - 另请参阅:https://github.com/nirvdrum/svn2git/issues/50。解决方案在这里:https://dev59.com/8XA75IYBdhLWcg3w8-LZ#4434188。 - Dave
最好解释答案,否则我们就会造出脚本小子。 - vhs
如果你的分支都在 SVN 的根目录下,而没有主干或标签,那该怎么办? - Kal
看起来这个仓库现在不活跃了。该仓库有145个未解决的问题和30个未解决的Pull Request。 - wonsuc

62

在尝试频繁使用git-svn之前,建议先熟悉Git的基本操作,即保持SVN为中央仓库,本地使用Git。

但是,如果需要进行包含全部历史纪录的简单迁移,以下是几个简单步骤:

初始化本地仓库:

mkdir project
cd project
git svn init http://svn.url

标记您希望从哪个时间点开始导入修订版本:

git svn fetch -r42

(或者只需使用“git svn fetch”获取所有版本)

实际上,获取从那时起的所有内容:

git svn rebase
您可以使用Gitk检查导入的结果。我不确定它是否适用于Windows,但它适用于OSX和Linux:

您可以使用Gitk检查导入的结果。我不确定它是否适用于Windows,但它适用于OSX和Linux:

gitk

当您在本地克隆了SVN仓库后,您可能希望将其推送到集中式Git仓库以便于协作。

首先创建空的远程仓库(可以在GitHub上创建吗?):

git remote add origin git@github.com:user/project-name.git

然后,可选择同步您的主分支,以便在两者均包含新内容时,拉取操作将自动将远程主分支与本地主分支合并:

git config branch.master.remote origin
git config branch.master.merge refs/heads/master

之后,您可能会对尝试我的独有的git_remote_branch工具感兴趣,该工具有助于处理远程分支:

第一篇解释性文章:“Git远程分支

最新版本的跟进:“与git_remote_branch协作的时机到了


非常有用,这个运行得很完美。如果您正在将代码库与远程代码库同步,则需要执行最后一步。在进行git配置步骤之后,我需要执行 git push origin master - mag382

33

现在有一种新的解决方案,可以平滑地从Subversion迁移到Git(或同时使用两者):SubGit

我自己正在开发这个项目。我们在我们的代码库中使用SubGit - 我的一些队友使用Git,另一些使用Subversion,到目前为止它运行得非常好。

要使用SubGit从Subversion迁移到Git,您需要运行:

$ subgit install svn_repos
...
TRANSLATION SUCCESSFUL 

之后,您将在svn_repos/.git中获得Git存储库,并可以克隆它,或者继续使用Subversion和这个新的Git存储库:SubGit将确保两者始终保持同步。

如果您的Subversion存储库包含多个项目,则将在svn_repos/git目录中创建多个Git存储库。在运行翻译之前自定义翻译,请执行以下操作:

$ subgit configure svn_repos
$ edit svn_repos/conf/subgit.conf (change mapping, add authors mapping, etc)
$ subgit install svn_repos

使用SubGit,您可以迁移到纯Git(而不是git-svn),并在仍然保留Subversion的同时开始使用它(例如,对于已配置的构建工具)。

希望这有所帮助!


4
请注意,一次性导入(使用 subgit import 命令)似乎甚至不需要许可证。同时也包括将 svn:ignore 属性准确翻译为 .gitignore 文件。 - krlmlr
2
SubGit无法识别我的私钥,也无法识别我在命令行中设置的任何标志。文档非常差。这不是“git svn”的可行替代方案。 - pfnuesel
1
错误:'svn_repos' 不是一个有效的配置位置;SubGit 配置文件丢失。 - Jon Davis

20
请参阅官方git-svn手册。特别是,在“基本示例”下查看:

跟踪和贡献整个Subversion管理的项目(包括主干,标签和分支):

# Clone a repo (like git clone):
    git svn clone http://svn.foo.org/project -T trunk -b branches -t tags

你的克隆命令有效,而上面的命令只给了我空的git存储库。唯一的区别似乎是显式的“-T trunk”。 - user1984717

15

SubGit(与蓝屏比较)

subgit import --svn-url url://svn.serv/Bla/Bla  directory/path/Local.git.Repo

就这些了。

+ 要从SVN更新,首先使用第一条命令创建一个Git存储库。

subgit import  directory/path/Local.git.Repo

我曾使用一种方法,快速将一个大型仓库迁移到Git。当然,你需要做一些准备工作,但完全不需要停止开发过程。

这是我的解决方案:

  • 将SVN迁移到Git仓库
  • 在团队切换之前更新Git仓库

对于一个大型的SVN仓库,迁移可能需要很长时间。但完成迁移后的更新只需几秒钟。

当然,我使用了SubGit,因为git-svn让我经常遭遇Git的蓝屏死机,而且还会让我遇到Git的“文件名过长”的致命错误。

步骤:

1. 下载SubGit

2. 准备迁移和更新命令。

假设我们在Windows上进行操作(转移到Linux非常简单)。在SubGit的安装目录下bin目录(subgit-2.X.X\bin)中,创建两个.bat文件。

迁移命令的文件/内容:

start    subgit import --svn-url url://svn.serv/Bla/Bla  directory/path/Local.git.Repo

在Windows中,“start”命令是可选的。它可以让您在启动时查看错误,并在SubGit完成后保留一个终端。

您可以在此处添加类似于git-svn的其他参数。 我只使用--default-domain myCompanyDomain.com来修复SVN作者电子邮件地址的域。
我有标准的SVN存储库结构(trunk/branches/tags),并且我们没有遇到“作者映射”的问题。所以我什么也不用做了。

(如果您想迁移像分支一样的标签,或者您的SVN有多个分支/标签文件夹,则可以考虑使用更详细的SubGit方法

提示1:使用--minimal-revision YourSvnRevNumber快速查看事物如何解决(某种调试方式)。 特别有用的是查看已解析的作者姓名或电子邮件地址。
或者限制迁移历史记录深度。

提示2:迁移可能会被中断(Ctrl+C),并通过运行下一个更新命令/文件进行恢复。
我不建议在大型存储库上这样做。我曾经收到“内存不足的Java+Windows异常”。

提示3:最好创建您的结果裸仓库的副本。

更新文件/命令的内容:

start    subgit import  directory/path/Local.git.Repo

想要获取最新团队提交到您的Git仓库,您可以随时运行它。

警告!不要触碰裸仓库(例如创建分支)。
否则会出现以下致命错误:

无法恢复的错误:失去同步,无法同步...将Subversion修订版转化为Git提交...

3. 运行第一个命令/文件。对于大型仓库,需要很长时间。我的小仓库需要30个小时。

就这些。
您可以通过运行第二个文件/命令随时随地更新您的Git仓库并切换开发团队到Git。
这只需几秒钟。



还有一项有用的任务。

将本地Git仓库推送到远程Git仓库

是您的情况吗?那么让我们开始吧。

  1. 配置您的远程仓库

运行:

$ git remote add origin url://your/repo.git
  1. 准备将您庞大的本地Git仓库发送到远程仓库

默认情况下,您的Git无法发送大块数据。 fatal: 远程主机意外挂断

让我们开始吧:

git config --global http.postBuffer 1073741824

524288000 - 500 MB
1073741824 - 1 GB, 等等。

修复本地的证书问题。如果你使用的是有问题的证书的Git服务器。

我已经禁用了证书

此外,你的Git服务器可能存在需要纠正的请求次数限制

  1. 将所有迁移推送到团队的远程Git仓库

使用本地的Git运行:

git push origin --mirror

(对于旧版本的Git,使用 git push origin '*:*'

如果出现以下错误:error: cannot spawn git: No such file or directory... 对我来说,完整重建我的存储库解决了这个错误(花费30小时)。您可以尝试下面的命令。

git push origin --all
git push origin --tags

或者尝试 重新安装 Git(对我来说是无用的)。 或者您可以从所有标签创建分支并将它们推送。 或者,或者,或者...


14

1
Pro Git 的解释包括迁移标签和分支。它使用本地 mv 而不是 svn 命令。聪明的做法。 - spazm

13

reposurgeon

对于复杂情况,Eric S. Raymond 的 reposurgeon 是首选工具。除了 SVN,它还支持许多其他版本控制系统,通过 fast-export 格式,以及 CVS。作者报告了成功转换古老的存储库,如 EmacsFreeBSD

该工具显然旨在实现近乎完美的转换(例如将 SVN 的 svn:ignore 属性转换为 .gitignore 文件),即使是具有悠久历史的仓库布局也可以进行转换。对于许多情况,其他工具可能更容易使用。

在深入阅读reposurgeon命令行文档之前,请务必阅读优秀的DVCS迁移指南,该指南逐步介绍了转换过程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接