如何重新连接git仓库与svn仓库?

20

我使用git svn将现有的Subversion库导入到git中。然后将其推送到git服务器上的git存储库中。在过去几个月中,软件的更改已在Subversion和git存储库中进行。不幸的是,我的本地副本与svn和git之间的链接已被删除。

我已尝试使用git svn重新创建本地副本,但当我从git服务器进行pull时,会出现“warning: no common conflicts”的提示,然后我最终需要合并两个最初具有相同提交的分支。像这样:

F
|\
| \
E  D
|  |
C  C
|  |
B  B
|  |
A  A

如何让它将SVN更改视为在原始repo的分支上发生的?

F
|\
| \
E  D
| /
|/
C
|
B
|
A

让我纠正一下我的理解,(A) 是在 (F) 之前的早期提交,对吗?你试图更改的是 Git 存储库,而不是 Subversion 吗? - scalopus
没错,A 在时间上先于 F。我最关心的是在 git 仓库中有所有更改。 - alnorth29
5个回答

22
默认情况下,git-svn会将SVN版本与Git提交之间的映射存储在提交消息中。你在原始的Git仓库中看到了这些旧提交的git-svn-id行吗?我指的是托管在Git服务器上而不是最近从SVN获取的那个仓库。
如果是这样,你实际上没有丢失任何链接,git-svn应该能够从历史记录中恢复必要的数据。但由于不同版本的git-svn之间存在一些兼容性问题,这可能有点棘手:
  1. 克隆您的原始Git仓库:

    $ git clone $GIT_SERVER repo
    $ cd repo
    
  2. 更新.git/config中的git-svn配置:

    $ git config svn-remote.svn.url $SVN_URL
    $ git config svn-remote.svn.fetch trunk:refs/remotes/trunk
    $ git config svn-remote.svn.branches branches/*:refs/remotes/*
    $ git config svn-remote.svn.tags tags/*:refs/remotes/tags/*
    
    现在您需要使用git-svn-id行更新refs/remotes/* refs上的最新提交:
  3. $ git log --first-parent refs/heads/master
    commit d566edf5f77ae0a2f7418c40949757e75ef8e83c
    D
    
    commit 4df9f21346526c6505a954d8310637864710308d
    C
    git-svn-id: $SVN_URL .../trunk@3...
    
    commit 116a6760d3e278aa4d54f5bb22e531d30d731661
    B
    git-svn-id: $SVN_URL .../trunk@2...
    
    commit d8bb201c6fd55ea5e645f2d8a07248593d177910
    A
    git-svn-id: $SVN_URL .../trunk@1...
    

    正如您所看到的,提交记录D没有git-svn-id行,但是提交记录C有一个,并且该行引用了主干,因此您需要将refs/remotes/trunk更新到提交记录C:

    如您所见,提交 D 没有 git-svn-id 行,而提交 C 具有该行并且该行引用主干,因此您需要将 refs/remotes/trunk 更新为提交 C。

    $ git update-ref refs/remotes/trunk 4df9f21346526c6505a954d8310637864710308d
    
  4. 如果您有许多分支和标签,请针对我们上面指定的映射分别重复相同的步骤:

    • branches/foo => refs/remotes/foo

    • tags/1.0 => refs/remotes/tags/1.0

  5. 最后一步是在.git/svn目录中恢复映射:

  6. $ git svn fetch
    Migrating from a git-svn v1 layout...
    Data from a previous version of git-svn exists, but
        .git/svn
        (required for this version (X.Y.Z) of git-svn) does not exist.
    Done migrating from a git-svn v1 layout
    Rebuilding .git/svn/refs/remotes/trunk/.rev_map.694389ff-b137-4359-84f9-4d1a25628e89...
    r1 = d8bb201c6fd55ea5e645f2d8a07248593d177910
    r2 = 116a6760d3e278aa4d54f5bb22e531d30d731661
    r3 = 4df9f21346526c6505a954d8310637864710308d
    Done rebuilding .git/svn/refs/remotes/trunk/.rev_map.694389ff-b137-4359-84f9-4d1a25628e89
    

最后一个命令还从 SVN 服务器获取新版本。命令完成后,您将拥有 Subversion 存储库的 git-svn 克隆。该存储库中的历史已经分叉,因此您需要像通常一样在 SVN 和 Git 存储库之间同步更改:

$ git svn rebase
$ git svn dcommit

希望这有所帮助。


这对我很有效。然而,在 git config svn-remote.svn.url=$SVN_URL 行中不应该有等号。 - alnorth29
非常感谢!在使用这种方法时,请确保第二步中的$SVN_URL与第三步的输出确实匹配。我曾经在第2步中意外地指定了“https://”而不是“http://”,所以才会想知道为什么程序没有成功。 - simon04
在我的分支上运行 git filter-branch ... 后,这对我来说非常有用,因为我可以再次执行 git svn rebase。我在我的 svn-remote 配置中使用了很多 --include-paths,这导致了很多空提交,我使用 git filter-branch 过滤掉它们。 - bengineerd

3

首先,我会检查 git svn info 命令的输出。你有没有看到一些奇怪的东西?

其次,问题在于不建议使用 git svn 与除本地存储库以外的第二个 git 存储库一起使用。

原因是每次 git svn dcommit 后,Git 自动 重写您之前所做的所有提交:首先将它们提交回 SVN 然后添加该提交的 SVN 版本号(在一个名为git-svn-id 的唯一标识符中)。

在您进行新的 dcommit 操作后,您的存储库中应该是这样的:

$ git log -1
commit 1234abc...
Author: ...
Date:   ...

Some commit message

git-svn-id: http://your.svn.repo/svn/trunk@10 1234abc

关于详情,这里有一篇ProGit书中的章节

你写的错误信息可能是warning: no common commits(不是问题中你所写的conflicts),我的印象是git没有将这些元数据推送到远程仓库。

我认为你可以通过一个单独克隆的仓库来恢复这个SVN链接,但请小心,仔细阅读文档以了解它需要哪些元数据。在git中,你可以使用plumbing工具做很多事情,请查看它们。

顺便问一下,你不能简单地克隆远程git仓库并使用吗?

希望这能对你有所帮助,或者至少给你一些想法,让你开始寻找解决方案。


1
谢谢,我之前并没有研究过dcommit的工作原理,所以我没有意识到这种配置(远程SVN和GIT仓库)是一个坏主意。在我的情况下,每个分支只有很少量的提交记录,因此我可能会手动复制更改。我将保持问题开放几天,看看是否有人有神奇的工具将所有内容拼接在一起。 - alnorth29
当然,希望您能找到解决问题的方法! - rlegendi

1

从提交图中

F
|\
| \
E   D
|   |
C1  C2
|   |
B1  B2
|   |
A1  A2

请使用以下命令。
git checkout <SHA1-B1>
git checkout -b new
git cherry-pick <SHA1-C2> <SHA1-D>
git checkout <SHA1-F>
git checkout -b position_f
git merge new

完成了。不过,我还没有尝试过。首先备份您的存储库。


这不就等于:git checkout <SHA1-B1>git checkout -b newgit cherry-pick <SHA1-D>吗?为什么你要取<SHA1-F>,当它已经是分支new的一部分并且提交<C2>等于<C1> - Andry

1
这样怎么样:
  • 备份!
  • 将svn历史重新导入到git中
  • 将旧分支和新分支放入同一个仓库中
  • 将您的分支回滚到F之前的提交
  • 在新导入的C上,将自C以来您分支中的所有更改rebase到顶部
  • 重新尝试合并

如果这不起作用,您可以尝试Git replace,但这可能是个坏主意。


1

最简单的解决方案是将本地 git-svn 分支推送到您的 git 服务器,如下所示:

git push <git-server> <branch-name>:<branch-name>

这将用本地分支覆盖 git 服务器上的分支。

只有在确定 git-svn 分支具有 git 服务器所有提交时才执行此操作。


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