Git-具有非标准布局的 svn 迁移不显示合并
Git-svn migration with non-standard layout doesn't show merges
在尝试了几个选项以及来自该站点和其他站点的大量提示后,我被卡住了。我的主要问题如下:我想将(部分)SVN 存储库迁移到 Git,保留历史记录。 SVN 布局是非标准的,在 git svn clone
之后我确实看到了正确的分支出现,但是当我尝试例如将 master
合并到一个分支中,我得到的冲突是说都添加了一组文件。如果我看看例如gitg
我看到了分支,但它们似乎从来没有从 master
/trunk 分支出来(所以从这个角度来看 "both added" 冲突似乎是合乎逻辑的),我也没有看到任何合并(例如从主干到分支)在图中(提交在那里,它们只是不 link 到 gitg
的图形显示中的分支)。事实上,对于某些分支,我什至看到两个相同的提交一个接一个(一个给 master,一个给分支)。
我在 SVN 中创建分支的方式是使用 svn copy
。
更多细节:
Repository布局:略微简化的SVN repo布局示意图(结构相同,名称不同,部分目录已省略)
pkg
Project1
Project2
Lib
branches
Project1-feature1
Project1
Lib
Project1-hotfix
Project1
Lib
Lib-feature
tags
Project1
v0.1.0
v0.2.0
Project1
Lib
Project2
v0.1.0
Lib
目录与 Project1 密切相关,但也被其他人使用。这就是为什么我(从 v0.2.0 开始)在分支和标签中创建 Project1
和 Lib
子目录结构。
我的git svn
工作流程:这是我用来克隆SVN repo的最有前途的命令:
git svn clone \
--prefix=svn/ \
--trunk=pkg \
--branches=branches \
--tags=tags/Project1 \
-A authors.txt \
--ignore-paths='^pkg/(?!Project1|Lib)' \
svn+ssh://user@svn.r-forge.r-project.org/svnroot/MyTool SVN2GitMigration
--ignore-paths
选项在那里,因此我只保留我感兴趣的两个目录(Project
和 Lib
)。我不过滤分支,因为只有一个分支与 Project1
没有直接关系。
之后我将远程分支转换为本地分支(并删除远程分支),然后将标签转换为正确的 Git 标签。
编辑开始: 仔细检查提交后发现我有很多空提交。结果证明这是由于 --ignore-paths
选项造成的:空提交是在被忽略的目录树部分中完成的。所以这个选项并没有像我预期的那样表现。
回到绘图板...
编辑结束
EDIT2
实际上,使用 git filter-branch --tag-name-filter cat --prune-empty -- --all
我设法删除了空提交
EDIT2 结束
我的合并问题的可能原因: Branches/Tags 不是单个 SVN 提交,因为它们首先包含我在其中创建 branches/Project1-featureX
目录的提交,然后是两行 svn copy
行,其中我从主干复制 Project1
和 Lib
目录。
非常欢迎就如何正确转换此 SVN 存储库提出建议!如果,不知何故,这意味着失去 Lib
,这没什么大不了的。我打算在迁移完成后将两者分开。
经过反复试验,我通过以下方式解决了我的问题:
准备
首先,我初始化了一个没有任何分支或标签的存储库:
git svn init \
--prefix=svn/ \
--trunk=pkg/Project1 \
svn+ssh://user@svn.r-forge.r-project.org/svnroot/MyTool \
SVN2GitMigration
接下来我添加作者信息:
cd SVN2GitMigration
git config svn.authorsfile ../authors.txt
在此之后,我的 .git/config
文件具有以下内容:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[svn-remote "svn"]
url = svn+ssh://user@svn.r-forge.r-project.org/svnroot/MyTool \
fetch = pkg/Project1:refs/remotes/svn/trunk
[svn]
authorsfile = ../authors.txt
为了获取分支和标签,我将该文件更改为:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[svn-remote "svn"]
url = svn+ssh://user@svn.r-forge.r-project.org/svnroot/MyTool \
fetch = pkg/Project1:refs/remotes/svn/trunk
tags = tags/Project1/{v0.4.2,v0.4.1,v0.4.0,v0.3.0,v0.2.2,v0.2.0}/Project1:refs/remotes/svn/tags/*
tags = tags/Project1/{v0.2.1,v0.1-9e,v0.1.3}:refs/remotes/svn/tags/*
branches = branches/{Project1-v0.4.2-fixes,Project1-v0.4.1-fixes,Project1-refactor,Project1-feature1}/Project1:refs/remotes/svn/*
branches = branches/{Project1-feature2}:refs/remotes/svn/*
[svn]
authorsfile = ../authors.txt
请注意 branches
和 tags
行如何在 {}
中包含目录名称列表,即使它只包含一个目录名称。没有这个,抓取将无法进行。
下载SVN数据
下载并转换 SVN 存储库运行:
git svn fetch
后处理
在此之后,需要进行一些 post 处理。将删除标签和分支转换为适当的本地标签和分支并删除远程标签和分支 运行:
for branch in `git branch -r |grep -v tags| grep -v trunk | sed 's/svn\///'`; do
git branch $branch remotes/svn/$branch;
done
for tag in `git branch -r |grep tags| sed 's;svn/tags/;;'`; do
git tag $tag remotes/svn/tags/$tag;
done
for br in `git branch -r`; do
git branch -d -r $br
done
将 svn:ignore
属性转换为 .gitignore
文件
git svn show-ignore > .gitignore
git add .gitignore
git commit -m "Added .gitignore file based on the svn:ignore properties"
在使用 gitg
或 gitk
检查 git 存储库后,发现许多合并都丢失了(图中未显示),所以我不得不手工移植那些通过将父提交哈希添加到 .git/info/grafts
文件(文件格式为 merge_hash parent1_hash parent2_hash
)。请注意,gitk
显示移植物,而 gitg
直到它们永久化后才显示。
使提交永久使用
git filter-branch --tag-name-filter cat -- --all
并删除 git filter-branch
运行:
创建的备份
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
总结
既然所有内容都已转换,请将存储库克隆为一个裸存储库:
git clone --bare SVN2GitMigration Project1.git
并将其推送到 Github:
cd Project1.git
git push --mirror https://github.com/mygithubuser/Project1.git
参考资料
感谢以下网站指出正确的方向:
- http://john.albin.net/git/convert-subversion-to-git
- http://www.sailmaker.co.uk/blog/2013/05/05/migrating-from-svn-to-git-preserving-branches-and-tags-3/
- http://blokspeed.net/blog/2010/09/converting-from-subversion-to-git/
- http://blog.agavi.org/post/16865375185/fixing-svn-merge-history-in-git-repositories
- http://patrickbougie.com/2013/03/18/convert-svn-to-git-repository/
- git svn specify branches and tags on a non standard svn repository layout
在尝试了几个选项以及来自该站点和其他站点的大量提示后,我被卡住了。我的主要问题如下:我想将(部分)SVN 存储库迁移到 Git,保留历史记录。 SVN 布局是非标准的,在 git svn clone
之后我确实看到了正确的分支出现,但是当我尝试例如将 master
合并到一个分支中,我得到的冲突是说都添加了一组文件。如果我看看例如gitg
我看到了分支,但它们似乎从来没有从 master
/trunk 分支出来(所以从这个角度来看 "both added" 冲突似乎是合乎逻辑的),我也没有看到任何合并(例如从主干到分支)在图中(提交在那里,它们只是不 link 到 gitg
的图形显示中的分支)。事实上,对于某些分支,我什至看到两个相同的提交一个接一个(一个给 master,一个给分支)。
我在 SVN 中创建分支的方式是使用 svn copy
。
更多细节:
Repository布局:略微简化的SVN repo布局示意图(结构相同,名称不同,部分目录已省略)
pkg
Project1
Project2
Lib
branches
Project1-feature1
Project1
Lib
Project1-hotfix
Project1
Lib
Lib-feature
tags
Project1
v0.1.0
v0.2.0
Project1
Lib
Project2
v0.1.0
Lib
目录与 Project1 密切相关,但也被其他人使用。这就是为什么我(从 v0.2.0 开始)在分支和标签中创建 Project1
和 Lib
子目录结构。
我的git svn
工作流程:这是我用来克隆SVN repo的最有前途的命令:
git svn clone \
--prefix=svn/ \
--trunk=pkg \
--branches=branches \
--tags=tags/Project1 \
-A authors.txt \
--ignore-paths='^pkg/(?!Project1|Lib)' \
svn+ssh://user@svn.r-forge.r-project.org/svnroot/MyTool SVN2GitMigration
--ignore-paths
选项在那里,因此我只保留我感兴趣的两个目录(Project
和 Lib
)。我不过滤分支,因为只有一个分支与 Project1
没有直接关系。
之后我将远程分支转换为本地分支(并删除远程分支),然后将标签转换为正确的 Git 标签。
编辑开始: 仔细检查提交后发现我有很多空提交。结果证明这是由于 --ignore-paths
选项造成的:空提交是在被忽略的目录树部分中完成的。所以这个选项并没有像我预期的那样表现。
回到绘图板...
编辑结束
EDIT2
实际上,使用 git filter-branch --tag-name-filter cat --prune-empty -- --all
我设法删除了空提交
EDIT2 结束
我的合并问题的可能原因: Branches/Tags 不是单个 SVN 提交,因为它们首先包含我在其中创建 branches/Project1-featureX
目录的提交,然后是两行 svn copy
行,其中我从主干复制 Project1
和 Lib
目录。
非常欢迎就如何正确转换此 SVN 存储库提出建议!如果,不知何故,这意味着失去 Lib
,这没什么大不了的。我打算在迁移完成后将两者分开。
经过反复试验,我通过以下方式解决了我的问题:
准备
首先,我初始化了一个没有任何分支或标签的存储库:
git svn init \
--prefix=svn/ \
--trunk=pkg/Project1 \
svn+ssh://user@svn.r-forge.r-project.org/svnroot/MyTool \
SVN2GitMigration
接下来我添加作者信息:
cd SVN2GitMigration
git config svn.authorsfile ../authors.txt
在此之后,我的 .git/config
文件具有以下内容:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[svn-remote "svn"]
url = svn+ssh://user@svn.r-forge.r-project.org/svnroot/MyTool \
fetch = pkg/Project1:refs/remotes/svn/trunk
[svn]
authorsfile = ../authors.txt
为了获取分支和标签,我将该文件更改为:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
[svn-remote "svn"]
url = svn+ssh://user@svn.r-forge.r-project.org/svnroot/MyTool \
fetch = pkg/Project1:refs/remotes/svn/trunk
tags = tags/Project1/{v0.4.2,v0.4.1,v0.4.0,v0.3.0,v0.2.2,v0.2.0}/Project1:refs/remotes/svn/tags/*
tags = tags/Project1/{v0.2.1,v0.1-9e,v0.1.3}:refs/remotes/svn/tags/*
branches = branches/{Project1-v0.4.2-fixes,Project1-v0.4.1-fixes,Project1-refactor,Project1-feature1}/Project1:refs/remotes/svn/*
branches = branches/{Project1-feature2}:refs/remotes/svn/*
[svn]
authorsfile = ../authors.txt
请注意 branches
和 tags
行如何在 {}
中包含目录名称列表,即使它只包含一个目录名称。没有这个,抓取将无法进行。
下载SVN数据
下载并转换 SVN 存储库运行:
git svn fetch
后处理
在此之后,需要进行一些 post 处理。将删除标签和分支转换为适当的本地标签和分支并删除远程标签和分支 运行:
for branch in `git branch -r |grep -v tags| grep -v trunk | sed 's/svn\///'`; do
git branch $branch remotes/svn/$branch;
done
for tag in `git branch -r |grep tags| sed 's;svn/tags/;;'`; do
git tag $tag remotes/svn/tags/$tag;
done
for br in `git branch -r`; do
git branch -d -r $br
done
将 svn:ignore
属性转换为 .gitignore
文件
git svn show-ignore > .gitignore
git add .gitignore
git commit -m "Added .gitignore file based on the svn:ignore properties"
在使用 gitg
或 gitk
检查 git 存储库后,发现许多合并都丢失了(图中未显示),所以我不得不手工移植那些通过将父提交哈希添加到 .git/info/grafts
文件(文件格式为 merge_hash parent1_hash parent2_hash
)。请注意,gitk
显示移植物,而 gitg
直到它们永久化后才显示。
使提交永久使用
git filter-branch --tag-name-filter cat -- --all
并删除 git filter-branch
运行:
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
总结
既然所有内容都已转换,请将存储库克隆为一个裸存储库:
git clone --bare SVN2GitMigration Project1.git
并将其推送到 Github:
cd Project1.git
git push --mirror https://github.com/mygithubuser/Project1.git
参考资料
感谢以下网站指出正确的方向:
- http://john.albin.net/git/convert-subversion-to-git
- http://www.sailmaker.co.uk/blog/2013/05/05/migrating-from-svn-to-git-preserving-branches-and-tags-3/
- http://blokspeed.net/blog/2010/09/converting-from-subversion-to-git/
- http://blog.agavi.org/post/16865375185/fixing-svn-merge-history-in-git-repositories
- http://patrickbougie.com/2013/03/18/convert-svn-to-git-repository/
- git svn specify branches and tags on a non standard svn repository layout