如何将带有历史记录的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个回答

8

你需要安装

git
git-svn

从此链接中复制:http://john.albin.net/git/convert-subversion-to-git

1. 获取所有Subversion提交者的列表

Subversion仅列出每个提交的用户名。Git的提交具有更丰富的数据,但在其最简单的形式下,提交作者需要有一个列出姓名和电子邮件地址。默认情况下,git-svn工具只会在作者和电子邮件字段中列出SVN用户名。但是通过一点点努力,您可以创建一个包含所有SVN用户及其相应的Git名称和电子邮件的列表。该列表可供git-svn使用,将普通svn用户名转换为正确的Git提交者。

从本地Subversion检出的根目录运行此命令:

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

这将捕获所有日志信息,提取用户名,消除任何重复的用户名,对用户名进行排序,并将它们放入“authors-transform.txt”文件中。现在编辑文件中的每一行。例如,将以下内容转换为:
jwilkins = jwilkins <jwilkins>

把它转化为这样:

jwilkins = John Albin Wilkins <johnalbin@example.com>

2. 使用git-svn克隆Subversion库

git svn clone [SVN repo URL] --no-metadata -A authors-transform.txt --stdlayout ~/temp

这将执行标准的 git-svn 转换(使用你在第一步创建的 authors-transform.txt 文件),并将 git 仓库放置在你的主目录下的“~/temp”文件夹中。

3. 将 svn:ignore 属性转换为 .gitignore

如果你的 svn 仓库正在使用 svn:ignore 属性,你可以轻松地使用以下命令将其转换为 .gitignore 文件:

cd ~/temp
git svn show-ignore > .gitignore
git add .gitignore
git commit -m 'Convert svn:ignore properties to .gitignore.'

4. 将仓库推送到裸的git仓库

首先,创建一个裸仓库,并将其默认分支与svn的“trunk”分支名称匹配。

git init --bare ~/new-bare.git
cd ~/new-bare.git
git symbolic-ref HEAD refs/heads/trunk

然后将临时仓库推送到新的裸仓库。

cd ~/temp
git remote add bare ~/new-bare.git
git config remote.bare.push 'refs/remotes/*:refs/heads/*'
git push bare

你现在可以安全地删除 ~/temp 仓库。
5. 将“trunk”分支重命名为“master”
你的主要开发分支将被命名为“trunk”,这与Subversion中的名称相匹配。您需要使用以下命令将其更名为Git的标准“master”分支:
cd ~/new-bare.git
git branch -m trunk master

6. 清理分支和标签

git-svn将所有Subversion的标签转换为Git中非常短的分支,格式为“tags/name”。您需要使用以下命令将所有这些分支转换为实际的Git标签:

cd ~/new-bare.git
git for-each-ref --format='%(refname)' refs/heads/tags |
cut -d / -f 4 |
while read ref
do
  git tag "$ref" "refs/heads/tags/$ref";
  git branch -D "tags/$ref";
done

这一步需要输入一些文本。 :-) 但是,不用担心;您的Unix shell会为以git for-each-ref开头的超长命令提供一个>二级提示。


8

这个页面很棒,而且在我看来是最好的答案!它可以为你完成大部分繁重的工作。 - PfunnyGuy

7
我已经发布了一个详细的指南(这里),介绍如何将 SVN 转换成 Git,包括将 SVN 标签转换成 Git 标签以及将 SVN 分支转换成 Git 分支。
简短版本:
1)从特定的版本号克隆 SVN。(版本号必须是您要迁移的最旧版本)
git svn clone --username=yourSvnUsername -T trunk_subdir -t tags_subdir -b branches_subdir -r aRevisionNumber svn_url gitreponame

2) 获取svn数据。这一步需要最多的时间。

cd gitreponame
git svn fetch

重复执行 git svn fetch 直至没有错误
3) 获取最新的主分支更新
git svn rebase

4) 通过复制引用从svn分支创建本地分支

cp .git/refs/remotes/origin/* .git/refs/heads/

5) 将svn标签转换为git标签

git for-each-ref refs/remotes/origin/tags | sed 's#^.*\([[:xdigit:]]\{40\}\).*refs/remotes/origin/tags/\(.*\)$#\2 \1#g' | while read p; do git tag -m "tag from svn" $p; done

6) 将存储库放在更好的地方,如github

git remotes add newrepo git@github.com:aUser/aProjectName.git
git push newrepo refs/heads/*
git push --tags newrepo

如果您想了解更多细节,请阅读我的文章或者问我。

7

以下是仅使用git、SVN和bash的较为详细的答案。它包括针对不使用传统布局(即trunk/branches/tags目录布局)的SVN存储库的步骤(SVN绝对不会强制执行这种布局)。

首先使用此bash脚本扫描您的SVN存储库以查找不同贡献者并生成映射文件的模板:

#!/usr/bin/env bash
authors=$(svn log -q | grep -e '^r' | awk 'BEGIN { FS = "|" } ; { print $2 }' | sort | uniq)
for author in ${authors}; do
  echo "${author} = NAME <USER@DOMAIN>";
done

使用此方法创建一个authors文件,在其中将svn用户名映射到开发人员使用git config属性user.nameuser.email设置的用户名和电子邮件(请注意,对于像GitHub这样的服务,仅具有匹配的电子邮件就足够了)。
然后使用git svn将svn存储库克隆到git存储库,并告诉它有关映射的信息: git svn clone --authors-file=authors --stdlayout svn://example.org/Folder/projectroot 这可能需要很长时间,因为git svn将逐个检出每个标记或分支的每个修订版本。(请注意,SVN中的标记实际上是分支,因此在Git中也是如此)。您可以通过删除不需要的旧标记和分支来加快此过程。
在同一网络或服务器上运行此操作也可以加快速度。此外,如果由于某种原因此过程中断,则可以使用以下命令继续恢复: git svn rebase --continue 在许多情况下,您已经完成了。但是,如果您的SVN存储库具有非常规布局,您只需将目录放入git分支中,则可以执行一些额外的步骤。
最简单的方法是在服务器上创建一个符合惯例的新SVN存储库,并使用svn copy将目录放入主干或分支。如果您的目录位于存储库的根部,则这可能是唯一的方法,因为我上次尝试时,git svn拒绝执行检出。
您还可以使用git来执行此操作。对于git svn clone,只需使用要放入git分支中的目录即可。
运行后:
git branch --set-upstream master git-svn
git svn rebase

请注意,这需要Git 1.7或更高版本。

我建议将这个信息与这个链接结合起来:http://www.sailmaker.co.uk/blog/2013/05/05/migrating-from-svn-to-git-preserving-branches-and-tags-3/ - Joan P.S

7

4
GitHub目前的建议是使用在另一个答案中提到的svn2git程序,该程序可以在此处找到建议的链接 - ntc2
刚刚无缺陷地导入了两个相当大的项目。所有 SVN 分支都被导入了(只需记住在 repo 路径中不要使用 \trunk 部分)。我还不知道的一件事是 Github 是否会跟踪新提交。 - Fr0sT

6
我们可以使用以下命令:git svn clone
  • svn log -q <SVN_URL> | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2">"}' | sort -u > authors.txt

上述命令将从SVN提交创建作者文件。

  • svn log --stop-on-copy <SVN_URL>

上述命令将给出您的SVN项目创建时的第一个修订版本号。

  • git svn clone -r<SVN_REV_NO>:HEAD --no-minimize-url --stdlayout --no-metadata --authors-file authors.txt <SVN_URL>

上述命令将在本地创建Git存储库。

问题是它不会将分支和标记转换为推送。 您必须手动执行它们。 例如下面的分支:

$ git remote add origin https://github.com/pankaj0323/JDProjects.git
$ git branch -a
* master
  remotes/origin/MyDevBranch
  remotes/origin/tags/MyDevBranch-1.0
  remotes/origin/trunk
$$ git checkout -b MyDevBranch origin/MyDevBranch
Branch MyDevBranch set up to track remote branch MyDevBranch from origin.
Switched to a new branch 'MyDevBranch'
$ git branch -a
* MyDevBranch
  master
  remotes/origin/MyDevBranch
  remotes/origin/tags/MyDevBranch-1.0
  remotes/origin/trunk
$

对于标签:

$git checkout origin/tags/MyDevBranch-1.0
Note: checking out 'origin/tags/MyDevBranch-1.0'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at 3041d81... Creating a tag
$ git branch -a
* (detached from origin/tags/MyDevBranch-1.0)
  MyDevBranch
  master
  remotes/origin/MyDevBranch
  remotes/origin/tags/MyDevBranch-1.0
  remotes/origin/trunk
$ git tag -a MyDevBranch-1.0 -m "creating tag"
$git tag
MyDevBranch-1.0
$

现在将主分支、分支和标签推送到远程git仓库。
$ git push origin master MyDevBranch MyDevBranch-1.0
Counting objects: 14, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (11/11), done.
Writing objects: 100% (14/14), 2.28 KiB | 0 bytes/s, done.
Total 14 (delta 3), reused 0 (delta 0)
To https://github.com/pankaj0323/JDProjects.git
 * [new branch]      master -> master
 * [new branch]      MyDevBranch -> MyDevBranch
 * [new tag]         MyDevBranch-1.0 -> MyDevBranch-1.0
$

svn2git 工具

svn2git 工具可以帮助您省去分支和标签的手动操作。

使用命令sudo gem install svn2git安装它。然后运行以下命令。

  • $ svn2git <SVN_URL> --authors authors.txt --revision <SVN_REV_NO>

现在,您可以轻松地列出分支、标签并将它们推送。

$ git remote add origin https://github.com/pankaj0323/JDProjects.git
$ git branch -a
  MyDevBranch
* master
  remotes/svn/MyDevBranch
  remotes/svn/trunk
$ git tag
  MyDevBranch-1.0
$ git push origin master MyDevBranch MyDevBranch-1.0

假设你有20个分支和标签,很明显svn2git会为你节省大量时间,这就是为什么我喜欢它胜过本地命令的原因。它是对本地git svn clone命令的良好封装。
要查看完整示例,请参考我的博客文章

5

尝试使用archive.org上的wayback机器。 - CAD bloke

5

对于GitLab用户,我发布了一个关于如何从SVN迁移到GitLab的Gist:

https://gist.github.com/leftclickben/322b7a3042cbe97ed2af

从SVN迁移到GitLab的步骤

设置

  • SVN托管在svn.domain.com.au
  • 可以通过http访问SVN(其他协议也可以)。
  • GitLab托管在git.domain.com.au 并且:
    • 创建了一个命名空间为dev-team的组。
    • 至少创建了一个用户账户,将其添加到该组中,并为正在使用的迁移账户添加了SSH密钥 (使用ssh git@git.domain.com.au进行测试)。
    • dev-team命名空间中创建了项目favourite-project
  • 文件users.txt包含相关用户详细信息,每行一个用户,格式为username = First Last <address@domain.com.au>,其中username 是在SVN日志中给出的用户名。(有关详细信息,请参见引用部分中的第一个链接,特别是用户Casey的回答)。

版本

  • Subversion 版本1.6.17 (r1128011)
  • Git 版本1.9.1
  • GitLab 版本7.2.1 ff1633f
  • Ubuntu服务器14.04

命令

git svn clone --stdlayout --no-metadata -A users.txt http://svn.domain.com.au/svn/repository/favourite-project
cd favourite-project
git remote add gitlab git@git.domain.com.au:dev-team/favourite-project.git
git push --set-upstream gitlab master

就这样!在GitLab Web UI中重新加载项目页面,您将看到所有提交和文件现在已列出。

注意事项

  • 如果有未知用户,则git svn clone命令将停止,在这种情况下,请更新users.txtcd favourite-projectgit svn fetch 将从停止的地方继续执行。
  • 需要符合 SVN 存储库的标准trunk-tags-branches布局。
  • git svn clone命令的 SVN URL 停止在trunk/tags/branches/ 的上一级。
  • git svn clone命令会输出大量内容,包括顶部的一些警告信息;我忽略了这些警告。

虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。 - Blackhole
1
我不同意。链接的内容可能会更改,这里复制的内容将不会更新,因此可能已经过时(实际上我相信自回答以来它已经发生了变化)。指南仅要求为链接包含一些相关上下文,我已经做到了 - 实际问题已经完全由链接回答。无需将整个链接资源复制到这里。我因为这个被踩了吗?! - leftclickben

3
我强烈推荐我刚刚发现的这个短视频系列。作者会向您介绍基本操作,并展示一些更高级的用法。

3
如果您正在使用SourceTree,您可以直接从应用程序中执行此操作。转到“文件” ->“新建/克隆”,然后执行以下操作:
  1. 将远程SVN URL输入为“源路径/URL”。
  2. 提示时输入您的凭据。
  3. 将本地文件夹位置输入为“目标路径”。
  4. 给它一个名称。
  5. 在高级选项中,在“创建类型的本地存储库”下拉菜单中选择“Git”。
  6. 您可以选择指定要克隆的版本。
  7. 点击克隆。

在SourceTree中打开存储库,您会发现您的提交消息也已被迁移。

现在,请转到“存储库”->“存储库设置”,添加新的远程存储库详细信息。如果需要,删除SVN远程,(我通过“编辑配置文件”选项完成了此操作)。

当您准备好时,请将代码推送到新的远程存储库并自由编写代码。


谢谢,超级简单和快速! - Rikard
谢谢。这对我有用。我正在使用SourceTree和Stash。 - nobody

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