【问题标题】:Publish Git repository to SVN将 Git 存储库发布到 SVN
【发布时间】:2012-10-07 02:26:53
【问题描述】:

我和我的小团队使用 Git,而更大的团队使用 Subversion。我想安排一个 cron 作业,以每小时将当前 HEADs 的存储库发布到 SVN 存储库中的某个目录。

我以为我已经弄清楚了,但我之前写下的食谱现在似乎不起作用:

git clone ssh://me@gitserver/git-repo/Projects/ProjX px2
cd px2
svn mkdir --parents http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git svn init -s http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git svn fetch
git rebase trunk master
git svn dcommit

当我尝试时会发生以下情况:

% git clone ssh://me@gitserver/git-repo/Projects/ProjX px2
Cloning into 'ProjX'...
...

% cd px2

% svn mkdir --parents http://me@svnserver/svn/repo/play/me/fromgit/ProjX
Committed revision 123.

% git svn init -s http://me@svnserver/svn/repo/play/me/fromgit/ProjX
Using higher level of URL: http://me@svnserver/svn/repo/play/me/fromgit/ProjX => http://me@svnserver/svn/repo

% git svn fetch
W: Ignoring error from SVN, path probably does not exist: (160013): Filesystem has no item: File not found: revision 100, path '/play/me/fromgit/ProjX'
W: Do not be alarmed at the above message git-svn is just searching aggressively for old history.
This may take a while on large repositories

% git rebase trunk master
fatal: Needed a single revision
invalid upstream trunk

我本可以发誓这以前有效,有人有什么建议吗?谢谢。

【问题讨论】:

  • git show remotes 里面有什么?
  • git remotes 只显示origin,这是 git 的来源。 git svn info 返回错误“无法从工作树历史中确定上游 SVN 信息”。
  • 另外,git svn fetch 不报告任何错误,也不产生任何输出。

标签: git svn mirroring


【解决方案1】:

您的方法存在一些问题:

  • 您似乎在使用预先存在的 git 存储库,而不是通过 git svn init 初始化的存储库。变基假设有一个共同的祖先,但是如果您的 git 存储库先前是通过 git init 初始化的,那么 git svn init 将创建第二个根(即无父)提交,并且如果没有 @987654325,从一个提示到另一个提示的变基将无法工作@。
  • 您对git svn init 使用-s 选项,这会导致它搜索branches/tags/trunk/。正如警告 (Using higher level...) 明确指出的那样,这会导致 git-svn 配置指向 svn 存储库的顶部,而不是 fromgit/ProjX 子目录。
  • 您引用trunk,即使这个分支没有充分的理由存在; git svn init 实际上创建了一个名为 remotes/git-svn 的跟踪分支。

所以你想要的实际顺序是:

# 1st time only
svn mkdir --parents http://me@svnserver/svn/repo/play/me/fromgit/ProjX
mkdir px2
cd px2
git svn init http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git svn fetch

现在黑客可以在 git 和 svn 中同时发生。下次你想dcommit 从 git 到 svn,你只需这样做:

cd px2
git svn rebase
git svn dcommit

如果您已经初始化了 git 存储库,开始在其中进行 hack,并且需要将该历史记录移植到 svn 中,那么仅第一次的序列会更加困难,因为您需要将所有 git 历史记录移植到 svn 中,即使他们没有共同的祖先:

# 1st time only
svn mkdir --parents http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git clone ssh://me@gitserver/git-repo/Projects/ProjX px2
cd px2
git svn init http://me@svnserver/svn/repo/play/me/fromgit/ProjX
git svn fetch

# transplant original git branch onto git-svn branch
root_commit=$( git rev-list --reverse HEAD | head -n1 )
git tag original-git
git reset --hard $root_commit
git reset --soft git-svn
git commit -C $root_commit
# N.B. this bit requires git >= 1.7.2
git cherry-pick $root_commit..original-git
# For older gits you could do
#   git rev-list $root_commit..original-git | xargs -n1 git cherry-pick
# or use git rebase --onto but that requires jumping through some
# hoops to stop moving remotes/git-svn.

随后,像以前一样执行svn rebasedcommit

如果有人想在沙盒中测试这种方法,您可以下载my test script。我建议您在运行之前进行视觉安全审核;-)

【讨论】:

  • 非常感谢。对于您的git svn init,我注意到您没有使用-s 开关。那是因为我们不会使用 SVN 风格的分支结构吗?就我个人而言,我很乐意不使用 SVN 分支,但我可能有 Git 分支和发布标签,我想以某种方式公开它们。如果这不漂亮,那么它可能不会破坏交易。
  • 答案顶部附近的第二个要点解释了为什么我放弃了-s 开关。大概您想通过将导入范围缩小到 fromgit/ProjX 子目录来避免整个 svn 存储库的 svn -> git 导入。所以不能使用-s。就像你说的,这不包括使用 SVN 分支,但不包括 git 分支/标签。如果您觉得我的回答有帮助,请点赞并采纳。谢谢;)
  • 糟糕!抱歉,我在第一次阅读时错过了那个项目符号。到目前为止,我已经尝试了大约一半的命令,它们看起来确实足以支持投票。当我能够尝试整个系列时,我会回来接受。
  • 没有办法自动解决合并冲突 - git 不是心灵感应 ;-) 所以是的,你必须自己解决每个问题。你可能可以用git rev-list $root_commit..original-git | while read $sha; do git cherry-pick $sha || git mergetool; done 之类的东西来平滑这个过程,但这完全不在我的脑海中并且未经测试:)
  • 您可能会通过仔细查看 为什么 樱桃挑选失败,即产生的实际冲突来做得更好。它们都是你真正想要挑选的承诺吗?如果有,为什么会有冲突?其中一位父母有错吗?这是以前的历史问题,还是您目前正在尝试完成的问题?或者如果你不想挑选那个提交,你怎么能自动避免它呢?根本原因分析将比盲目实验更有成效……希望有所帮助。如果没有,它可能值得另一个 SO 问题。
【解决方案2】:

1想把问题分成几个问题:

  1. 将 Git 历史记录导入现有的 Subversion 存储库;
  2. 之后自动同步 Git 和 SVN 存储库。

我的提议基于SubGit2

将 Git 仓库导入 SVN

  1. 如果您可以在 svnserver 本地访问 Subversion 存储库,则设置非常简单,并且在 SubGit Book 上有详细记录:

    假设 Subversion 存储库位于 $SVN_REPO,Git 存储库位于 $GIT_REPO。首先,运行以下命令:

    $ subgit configure $SVN_REPO
    

    然后调整生成的$SVN_REPO/conf/subgit.conf文件如下:

     [git "ProjX"]
         repository = $GIT_REPO
         translationRoot = /play/me/fromgit/ProjX
         trunk = trunk:refs/heads/master
         branches = branches/*:refs/heads/*
         shelves = shelves/*:refs/shelves/*
         tags = tags/*:refs/tags/*
    

    我不太确定您的案例的 translationRoot 选项。该值必须是相对于 SVN 存储库根目录的项目路径,请指定正确的。

    不要忘记删除其他“git”部分,这样 SubGit 不会将 SVN 存储库的这些部分转换为 Git。

    您可以选择调整 $SVN_REPO/conf/authors.txt 文件,以指定如何将 Git 提交者名称转换为 SVN 作者名称:

    SvnAuthor = Git Committer <git.committer@company.com>
    

    最后,将您的 Git 存储库导入 SVN:

    $ subgit install $SVN_REPO
    

    此时 $SVN_REPO 和 $GIT_REPO 有 SubGit 安装的特殊钩子。 Subversion 和 Git 服务器会在每次传入修改时触发这些挂钩。这样 SubGit 会自动同步 SVN 和 Git 存储库。创建的镜像是双向的,即一些开发者可能会使用 SVN 客户端,而另一些可能会选择任何 Git 客户端。

    如果您不需要这种同步,只需禁用它:

    $ subgit uninstall [--purge] $SVN_REPO
    
  2. 如果您没有对 Subversion 存储库的本地访问权限,事情会变得更加棘手,但仍有可能:

    首先,将整个 SVN 存储库获取到您的计算机,以便您可以在本地访问它:

    $ svnadmin create repo
    $ svnrdump dump http://me@svnserver/svn/repo | svnadmin load repo
    

    现在请记住获取的存储库的最新版本:

    $ svn info file:///path/to/repo
    Path: repo
    URL: file:///path/to/repo
    Repository Root: file:///path/to/repo
    Repository UUID: cbc56e97-717f-4d50-b705-cb6de2c836eb
    Revision: $LATEST_REVISION
    Node Kind: directory
    Last Changed Author: SvnAuthor
    Last Changed Rev: $LATEST_REVISION
    Last Changed Date: 2012-10-27 14:01:38 +0200 (Sat, 27 Oct 2012)
    

    然后重复上一个子句中的所有步骤。所以本地 SVN 存储库应该存储导入的 Git 历史记录。

    最后,将生成的历史传回SVN:

    $ svnsync initialize http://me@svnserver/svn/repo file://path/to/repo
    $ svn propset --revprop -r0 svn:sync-last-merged-rev $LATEST_REVISION http://me@svnserver/svn/repo
    $ svnsync synchronize http://me@svnserver/svn/repo
    

    请注意,远程存储库必须启用 pre-revprop-change 挂钩才能使 svnsync 工作。

Git 和 SVN 存储库的自动同步

  1. 如果您对 Subversion 存储库具有本地访问权限,则可以在安装 SubGit 后继续工作。正如我已经提到的,它会在将更改发送到其中一个存储库后立即转换它们,因此在这种情况下无需维护任何 cron 作业。

  2. 如果您没有对 Subversion 存储库的本地访问权限但仍想使用 SubGit,我建议您使用 svnsync 来同步托管在 svnserver 上的 SVN 存储库和由 SubGit 控制的 SVN 存储库3

    您可以在this HOW-TO 中找到有关如何设置此类镜像的指南。

    请注意,对于这种情况,您的项目应该有一个单独的 Subversion 存储库,否则很难保持 svnsync 正常工作。

  3. 如果您出于任何原因决定使用git-svn,我建议您在使用 SubGit 导入 Git 历史记录后创建一个全新的 Subversion 存储库克隆:

    $ git svn init -s http://me@svnserver/svn/repo/play/me/fromgit/ProjX
    $ git svn fetch
    

    根据我的经验,将 Git 历史记录导入 SVN 并不是 git-svn 的用途。另一方面,SubGit 处理得很好。

1 全面披露:我是 SubGit 开发人员之一。

2 SubGit 是一种商业产品,但它对最多 10 个提交者的小型团队是免费的。我认为它适用于您的情况。

3 我们的团队正在开发 SubGit 2.0,它支持同步位于不同主机上的 SVN 和 Git 存储库。基本上,您可以在任何地方创建 Git 存储库,并使用指定的 SVN URL 将 SubGit 安装到其中。之后,您可以使用这些存储库中的任何一个——更改会在它们之间自动转换。

我们将发布一个具有该功能的 EAP 版本,因此可以尽快尝试。

【讨论】:

  • 谢谢@radio,看到你的解释很有帮助。我曾考虑使用 SubGit,但之前没有看到关于最多 10 个用户免费的详细信息。就我而言,我没有对 SVN 存储库的本地访问权限,并且该存储库被用于除我的 Git 导出之外的其他事情,所以看起来我可能不适合使用这种策略。此外,我们不会让其他人在这部分 repo 中提交 SVN,因此我们无需担心真正的双向同步。
  • @KenWilliams 感谢您的详细评论。缺乏对 SVN 存储库的本地访问权限是人们无法使用 SubGit 的一个非常常见的原因。希望我们会在即将发布的版本中解决这个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-26
  • 1970-01-01
  • 1970-01-01
  • 2012-07-16
  • 1970-01-01
  • 2011-01-23
相关资源
最近更新 更多