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

2

我想为Git社区做出贡献。我编写了一个简单的bash脚本来自动完成完整导入。与其他迁移工具不同,这个工具依赖于原生git而不是jGit。此工具还支持具有大修订历史记录和/或大二进制对象的存储库。您可以通过github获取:

https://github.com/onepremise/SGMS

该脚本将转换以下格式中存储的项目:

/trunk
  /Project1
  /Project2
/branches
     /Project1
     /Project2
/tags
 /Project1
 /Project2

这个方案也很受欢迎并得到支持:

/Project1
     /trunk
     /branches
     /tags
/Project2
     /trunk
     /branches
     /tags

每个项目将按项目名称同步:
Ex: ./migration https://svnurl.com/basepath project1

如果您希望将整个代码库转换,可以使用以下语法:
Ex: ./migration https://svnurl.com/basepath .

2

我在Windows机器上制作了一个小批处理程序,通过调用以下命令将带有历史记录(但不包括分支)的SVN存储库传输到GIT存储库:

transfer.bat http://svn.my.address/svn/myrepo/trunk https://git.my.address/orga/myrepo

也许其他人也可以使用它。它会创建一个临时文件夹,在那里使用git签出SVN存储库并添加新的来源并推送它...然后删除该文件夹。

@echo off 
SET FROM=%1 
SET TO=%2 
SET TMP=tmp_%random%

echo from:  %FROM% 
echo to:    %TO% 
echo tmp:   %TMP%

pause

git svn clone  --no-metadata --authors-file=users.txt %FROM% %TMP%  
cd %TMP% 
git remote add origin %TO% 
git push --set-upstream origin master


cd .. 
echo delete %TMP% ... 
pause

rmdir /s /q %TMP%

你仍然需要用户.txt文件,其中包含用户映射信息,例如

User1 = User One <u.1@xxx.com>

这个答案帮助我顺利地将所有的代码库迁移到了BitBucket。 - Gonzalingui
很高兴听到这个消息。我只有使用Gitea的经验...但是用这种方式转移了大约40个仓库。 - cljk
非常好!谢谢。 - aLx13
警告:我遇到了糟糕的字符集问题。我意识到这个问题实在太晚了,但是我花了几个小时来修复它。请确保您的最终代码库包含完全符合预期的源代码! - cljk

2
这是一个没有依赖的简单的 shell 脚本,可以将一个或多个 SVN 仓库转换为 Git 并推送到 GitHub。

https://gist.github.com/NathanSweet/7327535

大约30行脚本可以完成以下操作:使用git SVN克隆,从SVN :: ignore属性创建.gitignore文件,推送到一个裸的git存储库,将SVN trunk重命名为master,将SVN标签转换为git标签,并在保留标签的情况下将其推送到GitHub。
我经历了很多痛苦,将一打SVN存储库从Google Code移动到GitHub。这并没有帮助我,因为我使用的是Windows。Ruby在我的旧Debian盒子上出了各种问题,在Windows上让它工作是个笑话。其他解决方案无法使用Cygwin路径。即使我找到了可行的解决方案,我也无法弄清楚如何在GitHub上显示标签(秘密是--follow-tags)。
最终,我拼凑了两个简短而简单的脚本,链接如上,它运行得很好。解决方案不需要比这更复杂!

2
我使用了这个脚本。经过一些尝试和错误,它对我起作用了。请注意,你需要 Git 1.8.3+ 来运行此脚本,因为 --follow-tags 只在此版本之后支持。 - nrobey

2
作为另一个说明,当尝试使用git-svn dcommits时,git-stash命令是救世神器。
典型的过程如下:
1.设置git repo。 2.对不同的文件进行一些工作。 3.决定使用git检查其中一些工作。 4.决定svn-dcommit。 5.遇到可怕的"无法提交具有脏索引"错误。
解决方案(需要git 1.5.3+):
git stash; git svn dcommit ; git stash apply

1

将svn子模块/文件夹'MyModule'转换为带有历史记录但没有标签或分支的git。

要保留svn忽略列表,请在第1步后使用上述注释。


1

首先,感谢@cmcginty的回答。对我来说,这是一个很好的起点,我在此基础上借鉴了很多。然而,我要移动的仓库有数年的历史,所以按照那个回答的方法会出现一些问题(例如需要手动移动数百个分支和标签,详情请见后文)。

经过数小时的搜索和试错,我成功编写了一个脚本,可以轻松地将多个项目从SVN迁移到GIT,并且我决定在这里分享我的发现,以防其他人也遇到同样的问题。

<tl;dr> 让我们开始吧


首先,创建一个“作者”文件,将基本的svn用户转换为更复杂的git用户。最简单的方法是使用命令从要移动的svn仓库中提取所有用户。

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

这将生成一个名为authors-transform.txt的文件,其中每个用户在运行它的svn仓库中所做的更改都会有一行记录。
someuser = someuser <someuser>

更新以包括Git的全名和电子邮件。
someuser = Some User <someuser@somewhere.com>

现在使用您的作者文件开始克隆。
git svn clone --stdlayout --no-metadata -r854:HEAD --authors-file=authors-transform.txt https://somesvnserver/somerepo/ temp
  • --stdlayout指示svn仓库遵循标准的 /trunk /branches /tags 布局
  • --no-metadata告诉git不要在每个git提交上盖章与svn提交相关的元数据。如果这不是单向转换,请删除此标记
  • -r854:HEAD只获取从版本854开始的历史记录。这是我遇到的第一个障碍; 我要转换的仓库在853版本处有一个“损坏”的提交,因此无法克隆。使用此参数可以让您仅克隆部分历史记录。
  • temp是将被创建以初始化新git仓库的目录名称

这一步可能需要一段时间,特别是对于大型或旧的仓库(我们其中一个大约需要18小时)。您还可以使用-r开关仅获取少量历史记录以查看克隆情况,并稍后获取其余部分。

移动到新目录

cd temp

如果你只克隆了部分内容,那么请获取任何缺失的历史记录。
git svn fetch

在克隆时,标签会被创建为分支。如果您只有几个标签,可以逐个进行转换。

git 1.0.0 origin/tags/1.0.0

然而,如果你有数百个标签,这将变得很繁琐,所以以下脚本对我很有用。

for brname in `git branch -r | grep tags | awk '{gsub(/^[^\/]+\//,"",$1); print $1}'`; do echo $brname; tname=${brname:5}; echo $tname; git tag $tname origin/tags/$tname; done

你还需要检出你想要保留的所有分支。
git checkout -b branchname origin/branches/branchname

如果您也有许多分支,那么这个脚本可能会有所帮助。
for brname in `git branch -r | grep -v master | grep -v HEAD | grep -v trunk | grep -v tags | awk '{gsub(/^[^\/]+\//,"",$1); print $1}'`; do echo $brname; git checkout -b $brname origin/$brname; done

这将忽略主干分支,因为它已经被检出为主分支,并在稍后删除重复分支时节省了一步,同时忽略我们已经转换的/标签。现在是检查新存储库并确保您拥有本地分支或标记以保留任何要保留的内容的好时机,因为远程分支将很快被删除。现在,让我们将所有已检出的内容克隆到一个干净的存储库(此处命名为temp2)。
cd ..
git clone temp temp2
cd temp2

现在,我们需要再次检出所有分支,然后将它们推送到最终的远程仓库,因此请按照上述方法中您喜欢的方法进行操作。
如果您正在遵循gitflow流程,您可以将工作分支重命名为develop。
git checkout -b WORKING
git branch -m develop
git push origin --delete WORKING
git push origin -u develop

现在,如果一切看起来都很不错,你就可以将其推送到你的git代码库。
git remote set-url origin https://somebitbucketserver/somerepo.git
git push -u origin --all
git push origin --tags

我遇到的最后一个问题是Control Freak最初阻止我推送我没有创建的标签,因此如果您的团队使用Control Freak,则可能需要禁用或调整该设置以进行初始推送。

0

一站式 - shell脚本用于SVNGIT的迁移。提及GITSVN细节,占位符为<>

#!/bin/bash

######## Project name 
PROJECT_NAME="Helloworld"
EMAIL="example mail"

#Credientials Repo
GIT_USER='<git username>'
GIT_PWD='<git password>'
SVN_USER='<svn username>'
SVN_PWD='<svn password>'

######## SVN repository to be migrated # Dont use https - error will be thrown
BASE_SVN="<SVN URL>/Helloworld"

#Organization inside BASE_SVN
BRANCHES="branches"
TAGS="tags"
TRUNK="trunk"

#Credientials
git config --global user.name '<git username>'
git config --global user.password '<git password>'
git config --global credential.helper 'cache --timeout=3600'

######## GIT repository to migrate - Ensure already project created in Git
GIT_URL=https://$GIT_USER:$GIT_PWD@<GIT URL>/Helloworld.git

###########################
#### Don't need to change from here
###########################

#Geral Configuration
ABSOLUTE_PATH=$(pwd)
TMP=$ABSOLUTE_PATH/$PROJECT_NAME

#Branchs Configuration
SVN_BRANCHES=$BASE_SVN/$BRANCHES
SVN_TAGS=$BASE_SVN/$TAGS
SVN_TRUNK=$BASE_SVN/$TRUNK

AUTHORS=$PROJECT_NAME"-authors.txt"

echo '[LOG] Starting migration of '$SVN_TRUNK
echo '[LOG] Using: '$(git --version)
echo '[LOG] Using: '$(svn --version | grep svn,)

mkdir $TMP
echo
echo '[DIR] cd' $TMP
cd $TMP

echo
echo '[LOG] Getting authors'
svn --username $SVN_USER --password $SVN_PWD log -q $BASE_SVN | awk -F '|' '/^r/ {sub("^ ", "", $2); sub(" $", "", $2); print $2" = "$2" <"$2"@"$EMAIL">"}' | sort -u >> $AUTHORS

echo
echo '[RUN] git svn clone --authors-file='$AUTHORS' --trunk='$TRUNK' --branches='$BRANCHES' --tags='$TAGS $BASE_SVN $TMP
git svn clone --authors-file=$AUTHORS --trunk=$TRUNK --branches=$BRANCHES --tags=$TAGS $BASE_SVN $TMP

#Not working so no need to mention it
#--stdlayout $PROJECT_NAME
echo
echo '[RUN] svn ls '$SVN_BRANCHES
svn ls $SVN_BRANCHES

echo 
echo 'git branch -a'
git branch -a

echo
echo '[LOG] Getting first revision'
FIRST_REVISION=$( svn log -r 1:HEAD --limit 1 $BASE_SVN | awk -F '|' '/^r/ {sub("^ ", "", $1); sub(" $", "", $1); print $1}' )

echo
echo '[RUN] git svn fetch -'$FIRST_REVISION':HEAD'
git svn fetch -$FIRST_REVISION:HEAD

#Branches and Tags  
echo
echo '[RUN] svn ls '$SVN_BRANCHES
for BRANCH in $(svn ls $SVN_BRANCHES); do
    echo git branch ${BRANCH%/} remotes/svn/${BRANCH%/}
    git branch ${BRANCH%/} remotes/svn/${BRANCH%/}
done

git for-each-ref --format="%(refname:short) %(objectname)" refs/remotes/origin/tags | grep -v "@" | cut -d / -f 3- |
while read ref
do
  echo git tag -a $ref -m 'import tag from svn'
  git tag -a $ref -m 'import tag from svn'
done

git for-each-ref --format="%(refname:short)" refs/remotes/origin/tags | cut -d / -f 1- |
while read ref
do
  git branch -rd $ref
done
  
echo
echo 'git tag'
git tag

echo
echo 'git show-ref --tags'
git show-ref --tags

echo
echo '[RUN] git remote add origin '$GIT_URL
git remote add origin $GIT_URL

echo
echo '[RUN] git push'
git push origin --all --force
git push origin --tags

#echo git branch -d -r trunk
#git branch -d -r trunk

git config --global credential.helper cache
echo 'Successful.'

当你运行上述脚本时,它将从 SVN 获取分支和标签的详细信息,并将其放置在 .git 文件夹下。 交叉检查所有分支是否都在 SVN 中,应该在此 .git/refs/heads 文件夹下可用。 如果缺少一些在 SVN 中存在的分支,则手动将分支文件从 .git/refs/remotes/origin/<branches> 复制到 .git/refs/heads。 只复制分支(包括 master),忽略任何 tagstrunk。 现在再次运行脚本。您可以在 git 存储库中看到所有的 branchestags

0

GitHub有一个导入器。一旦您创建了存储库,您可以通过其URL从现有存储库导入。如果适用,它将要求您提供凭据,然后继续进行。

在运行时,它会找到作者,您只需将它们映射到GitHub上的用户即可。

我现在已经使用它来管理几个存储库了,它非常准确且速度更快!对于一个包含约4000次提交的存储库,它只需要10分钟,而我的朋友则需要四天!


0

有不同的方法可以实现这个目标。我尝试了其中一些,并发现只需在 Windows 操作系统上安装 git 和 svn 即可使用真正有效的方法。

先决条件:

  1. Windows 上的 git(我使用了这个)https://git-scm.com/
  2. 安装了控制台工具的 svn(我使用了 tortoise svn)
  3. 您的 SVN 存储库的转储文件。 svnadmin dump /path/to/repository > repo_name.svn_dump

实现最终目标的步骤(将所有存储库及其历史记录移动到 git 中,首先是本地 git,然后是远程 git)

  1. 使用控制台工具或TortoiseSVN创建空仓库(在目录REPO_NAME_FOLDER中)cd REPO_NAME_PARENT_FOLDER,将dumpfile.dump放入REPO_NAME_PARENT_FOLDER中。

  2. svnadmin load REPO_NAME_FOLDER < dumpfile.dump 等待此操作完成,可能需要一段时间。

  3. 此命令是静默的,因此打开第二个cmd窗口:svnserve -d -R --root REPO_NAME_FOLDER。为什么不直接使用file:///......?因为下一个命令将失败并显示“无法打开...到URL:”,感谢答案https://dev59.com/yW435IYBdhLWcg3w9FHi#6300968

  4. 创建新文件夹SOURCE_GIT_FOLDER。

  5. cd SOURCE_GIT_FOLDER
  6. git svn clone svn://localhost/ 等待此操作完成。

最后,我们得到了什么?

让我们检查本地仓库:

git log

看到你之前的提交了吗?如果是 - 没问题

现在,您拥有具有源代码和旧的svn历史记录的完全功能本地git存储库。 现在,如果您想将其移动到某个服务器,请使用以下命令:

git remote add origin https://fullurlpathtoyourrepo/reponame.git
git push -u origin --all # pushes up the repo and its refs for the first time
git push -u origin --tags # pushes up any tags

在我的情况下,我不需要 tags 命令,因为我的代码库中没有标签。
祝你好运!

0

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