29

我想将 Subversion 存储库子目录(由module此处表示)转换为具有完整历史记录的 git 存储库。在我的 Subversion 存储库的历史中,有许多svn copy操作(Subversion 人称它们为分支)。发布策略是,在每个发布或其他分支创建后,旧 URL 将不使用,新 URL 替换旧 URL 以包含工作。

最佳情况下,根据我的阅读,这似乎应该可以解决问题:

$ git svn clone --username=mysvnusername --authors-file=authors.txt \
    --follow-parent \
    http://svnserver/svn/src/branches/x/y/apps/module module

(其中branches/x/y/描绘了最新的分支)。但我收到一个错误,看起来像这样:

W: Ignoring error from SVN, path probably does not exist: (160013): Filesystem has no item: '/svn/src/!svn/bc/100/branches/x/y/apps/module' path not found
W: Do not be alarmed at the above message git-svn is just searching aggressively for old history.

更新:在上面添加选项--no-minimize-url不会删除错误消息。)

该目录module被创建并填充,但未导入最新提交之后的 Subversion 历史记录svn copy(创建的 git 存储库最终只有两个提交,而我预计会有数百个提交)。

问题是,在这种情况下如何导出完整的 Subversion 历史记录?

可能的原因

  1. 搜索错误消息,我发现:git-svn 匿名结帐失败并带有 -s 链接到此 Subversion 问题:http ://subversion.tigris.org/issues/show_bug.cgi?id=3242

    根据我的阅读理解,Subversion 1.5 中的某些内容改变了客户端访问存储库的方式。对于较新的 Subversion,如果没有对 URL 路径的某些超级目录的读取访问权限(对我来说是真的,svn ls http://svnserver/svn失败为403 Forbidden),那么我们会因某些 Subversion 操作而失败。

  2. Jeff Fairley 在他的回答中指出 Subversion URL 中的空格也可能导致此错误消息(由用户 Owen 确认)。看看他的解决方案,看看他是如何解决这个案子的,如果你git svn clone在同样的情况下失败了。

  3. Dejay Clayton 在他的回答中透露,如果分支和标签 svn url 中最深的子目录组件名称相同(例如.../tags/release/1.0.0.../branches/release-candidates/1.0.0),则可能会发生此错误。

4

5 回答 5

10

当我在分支或标签中有同名的子目录时遇到了这个问题。

例如,我有标签candidates/1.0.0and releases/1.0.0,这导致了记录的错误,因为子目录1.0.0同时出现在candidatesandreleases中。

每个git-svn 文档

当使用多个 --branches 或 --tags 时,git svn 不会自动处理名称冲突(例如,如果来自不同路径的两个分支具有相同的名称,或者一个分支和一个标签具有相同的名称)。在这些情况下,使用 init 设置您的 Git 存储库,然后在您第一次获取之前,编辑 $GIT_DIR/config 文件,以便分支和标签与不同的名称空间相关联。

因此,虽然以下命令由于名称candidatesreleases标签相似而失败:

git svn clone --authors-file=../authors.txt --no-metadata \
    --trunk=/trunk --branches=/branches --tags=/candidates \
    --tags=/releases --tags=/tags -r 100:HEAD \
    --prefix=origin/ \
    svn://example.com:3692/my-repos/path/to/project/

以下命令序列确实有效:

git svn init --no-metadata \
    --trunk=/trunk --branches=/branches --tags=/tags \
    --prefix=origin/ \
    'svn://example.com:3692/my-repos/path/to/project/'

git config --add svn-remote.svn.tags \
    'path/to/project/candidates/*:refs/remotes/origin/tags/Candidates/*'

git config --add svn-remote.svn.tags \
    'path/to/project/releases/*:refs/remotes/origin/tags/Releases/*'

git svn fetch --authors-file=../authors.txt -r100:HEAD

branches请注意,这仅有效,因为和中没有其他冲突tags。如果有,我将不得不以类似方式解决它们。

成功克隆 SVN 存储库后,我执行以下步骤: 将 SVN 标签转换为 GIT 标签;变成; trunk_ master将其他引用变成分支;并重新定位远程路径:

# Make tags into true tags
cp -Rf .git/refs/remotes/origin/tags/* .git/refs/tags/
rm -Rf .git/refs/remotes/origin/tags

# Make other references into branches
cp -Rf .git/refs/remotes/origin/* .git/refs/heads/
rm -Rf .git/refs/remotes/origin
cp -Rf .git/refs/remotes/* .git/refs/heads/ # May be missing; that's okay
rm -Rf .git/refs/remotes

# Change 'trunk' to 'master'
git checkout trunk
git branch -d master
git branch -m trunk master
于 2015-06-25T21:08:36.870 回答
6

不是一个完整的答案,但可能是您缺少的片段(我也对迁移感兴趣,所以我找到了难题的那一部分)。

当您查看git-svn 的文档时,您会发现以下选项:

--no-minimize-url 

当跟踪多个目录(使用 --stdlayout、--branches 或 --tags 选项)时,git svn 将尝试连接到 Subversion 存储库的根目录(或允许的最高级别)。如果整个项目在存储库中移动,此默认设置允许更好地跟踪历史记录,但可能会导致存在读取访问限制的存储库出现问题。传递 --no-minimize-url 将允许 git svn 按原样接受 URL,而无需尝试连接到更高级别的目录。当只跟踪一个 URL/分支时,此选项默认关闭(它不会有什么好处)。

这适合您的情况,因此git svn不会尝试读取更高级别的目录树(将被阻止)。

至少你可以试一试...

于 2013-02-26T14:12:21.800 回答
3

我最近将一长串 SVN 存储库迁移到 Git 中,最后遇到了这个问题。我们的 SVN 结构非常草率,所以我不得不使用--no-minimize-url相当多的。通常,我会运行如下命令:

$ git svn clone http://[url]/svn/[repo]/[path-to-code] \
            -s --no-minimize-url \
            -A authors.txt

我运行的最后几次迁移在 URL 中有一个空格。我不知道是空间还是其他原因,但我遇到了与您看到的相同的错误。如果没有必要,我不想修改配置文件,幸运的是我最终找到了解决方案。我最终跳过了-s --no-minimize-url选项,转而以不同的方式明确声明路径。

$ git svn clone http://[url]/svn/[repo]/ \
            --trunk="/[path-to-code]/trunk" \
            --branches="/[path-to-code]/branches" \
            --tags="/[path-to-code]/tags" \
            -A authors.txt \
            --follow-parent
  • 请注意,我--follow-parent从您的示例中添加,但我也不确定它有什么不同。
  • 请记住,这些存储库中有空格,因此在""主干/分支/标签路径周围。
于 2013-05-20T17:56:36.163 回答
2

[我意识到这应该是对 Jeff Fairley 的回答的评论,但我没有这样发布它的声誉。由于原始海报确实要求确认该方法有效,因此我将其作为答案提供。]

我可以确认他的解决方案适用于他(和我)遇到的由路径中的空格引起的问题。我有相同的要求(从具有历史记录的 SVN 存储库中克隆单个模块),只是我没有需要担心的分支或标签。

我尝试了几种在 URL 中提供模块完整路径的排列方式(例如使用--no-minimise-url、指定--trunk--stdlayout),但均未成功。对我来说,结果通常是一个带有完整历史日志但没有任何文件的 git repo。这可能是也可能不是 FooF 遇到的相同问题(在 SVN 中没有读取访问权限),但这肯定是由于我的模块路径中有空格造成的。

仅使用 SVN 回购库作为 URL 和我的模块的路径再次尝试--trunk完美无缺。之后我的 .git/config 看起来像这样:

[core]
        repositoryformatversion = 0
        filemode = false
        bare = false
        loggallrefupdates = true
        symlinks = false
        ignorecase = true
        hideDotFiles = dotGitOnly
[svn-remote "svn"]
        url = https://[url]/svn/[repo]
        fetch = trunk/[path-to-code]:refs/remotes/trunk
[svn]
        authorsfile = ~/working/authors-transform.txt

并且随后的gitgit svn命令根本没有抛出任何错误。谢谢杰夫!

于 2013-09-06T19:48:36.337 回答
1

[这是原版海报说话的写作。以下内容曾经是对该问题的更新,但由于它解决了这个问题 - 尽管我的口味并不令人满意 - 我会将其发布为缺乏更好解决方案的答案。]

我不喜欢这样,但我最终在 ( , ) 之间进行了clone 一些编辑:initfetch.git/configrepopath=apps/modulegitreponame=module

$ git svn init--username=mysvnusername \
            --branches=/src/branches/ \
            --trunk=/src/trunk/${repopath} \
            --tags=/src/tags/ \
            http://svnserver/svn/src ${gitreponame}
$ cd ${gitreponame}
$ sed -i.bak "s|*:|*/${repopath}:|" .git/config
$ git svn fetch --authors-file=../authors.txt --follow-parent

我找不到如何为子目录迁移指定分支git svn- 因此无法编辑.git/config文件。以下统一差异说明了使用 编辑的效果sed

 [svn-remote "svn"]
        url = http://svnserver/svn/src
        fetch = trunk/apps/module:refs/remotes/trunk
-       branches = branches/*:refs/remotes/*
-       tags = tags/*:refs/remotes/tags/*
+       branches = branches/*/apps/module:refs/remotes/*
+       tags = tags/*/apps/module:refs/remotes/tags/*

由于实际需要HEAD的是另一个 URL,所以我只添加了另一个[svn-remote]部分.git/config

+ [svn-remote "svn-newest"]
+       url = http://svnserver/svn/src
+       fetch = branches/x/y/apps/module:refs/remotes/trunk
+       branches = branches/*/apps/module:refs/remotes/*
+       tags = tags/*/apps/module:refs/remotes/tags/*

(在现实生活中的实验中,我还在这里添加了一些第一次获取未拾取的分支),然后再次获取:

$ git svn fetch --authors-file=../authors.txt --follow-parent svn-newest

这样我就结束了将完整的 Subversion 历史迁移到新生成的 git 存储库。

注1:我可能只是告诉我的“trunk”是branches/x/y/apps/module“trunk”的意思,因为git-svn似乎基本上有git的意思HEAD(主干、分支、标签的颠覆概念没有深厚的技术基础,它们很重要社会认可的惯例)。

注2:可能--follow-parent不需要git svn fetch,但我现在无法知道或试验。

Note-3:虽然早期阅读svn2git似乎是一个包装器git-svn,但我没有看到动机,但看到标签的混乱呈现我现在有点明白了。如果我不得不再次尝试这样做,我会尝试svn2git下一次。

PS这是一种相当尴尬的操作方式。这里的次要问题(为什么.git/config需要外部编辑)似乎是

  1. Subversion 分支没有任何必要的技术意义( Subversion 中的分支标签只是版本化文件系统副本的社会认可标签以及完成副本的“标准”或其他社会认可的约定 -主干也没有技术意义) , 和
  2. git svn实现严格假定在一定程度上遵循社交 Subversion 约定(如果您只想迁移子目录而不是整个 Subversion 存储库,这是不可能的)。

TODO:.git/config在这里解释文件的格式会有所帮助git svn- 例如,我现在(在写了原始答案一年半之后)不知道[svn-remote "svn-newest"]上面的意思。此外,该方法可以通过编写脚本来自动化,但这超出了我目前对该问题的兴趣,我无权访问原始 Subversion 存储库或复制该问题。

于 2014-03-05T04:36:17.367 回答