4

我和我的小团队使用 Git,而更大的团队使用 Subversion。我想安排一个 cron 作业,以HEAD每小时将我们的存储库 current 发布到 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

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

4

2 回答 2

9

您的方法存在一些问题:

  • 您似乎正在使用预先存在的 git 存储库,而不是通过git svn init. 变基假设有一个共同的祖先,但是如果您的 git repo 先前是通过 初始化的git init,那么git svn init将创建第二个根(即无父节点)提交,并且从一个提示到另一个提示的变基将无法在没有--onto.
  • 您使用-sto 选项git svn init,这会导致它搜索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 中,即使它们没有t 共享一个共同的祖先:

# 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以前一样做。

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

于 2012-10-24T00:21:21.877 回答
4

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

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

我的建议基于SubGit 2

将 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 3控制的 SVN 存储库。

    您可以在此 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 构建,因此可以尽快尝试。

于 2012-10-27T22:56:46.923 回答