只是为了帮助 svn 合并而使用 git-svn (或类似工具)是否可行?

28

我的项目即将进行一些复杂的svn合并:长时间分开的大型分支。Svn给出了太多的冲突,其中一些似乎是虚假的。


考虑到git在合并体验方面的优越性,仅仅出于使合并更可管理的好处,使用git-svn是否有所帮助?


您能推荐其他替代方案(例如svkhgsvn)来减轻合并痛苦吗?

某些冲突很容易解决(例如java导入,空格),因此我也想知道是否有任何自动化解决方案。

将来可能会完全切换到DVCS(我们中的一些人会喜欢这样做),但现在还没有。(更新:这不再是真实情况-团队最近完全转换并对此感到满意)。

预先感谢您。

PS:有一些帖子似乎与此相关(例如git-svn merge 2 svn branches),但它们并没有完全回答这个问题。

更新:在经历过这条路之后,看到我这个新手的答案(上升:)。


为什么不把所有东西都用git-svn呢? - Vi.
@Vi,这听起来像是一个独立的高级问题 - 你可能想将其作为一个单独的问题添加 :-/ 我的问题大约是这样的: "在基于 SVN 的团队中,你会介绍 git-svn 来帮助处理大型合并吗?" - inger
1
刚合并完后,他们可能会想要开始使用它... - Vi.
这就是发生的事情 - 正如您下面所看到的。 - inger
3个回答

35
尝试回答我的问题:使用git进行svn合并似乎很有前途。
更新:这不仅有前途,而且非常成功。简而言之,Linus是正确的
刚完成了两个svn分支的大型合并,这些分支已经分离了1.5年; 3k文件被更改,在svn中遇到了大量冲突(我认为大约有800个)。
我发现git和git-svn非常有用:
  • 自动冲突解决:起初,它给出的冲突文件要少得多(我认为大约减少了一半)
  • 令人难以置信的性能
  • 优秀的repo/branching model,灵活的工作流程:可以轻松地尝试各种方法,例如按块合并(按时间),始终进行完整性检查(编译等);每当遇到问题时:只需回溯。您总是可以在需要时向后退一步。
  • 易用性,出色的工具:
    • git-log(及其基础的git-rev-parse选项),没有比这更强大的东西了。它也很方便:-p可以一次性给出差异;在svn中,您会得到一个日志,然后找到该“修订版-1:修订版”的差异,或使用笨拙的用户界面。同时搜索多个分支,查找字符串何时添加/删除到存储库中
    • gitk:非常有用,可以可视化分支历史记录,结合出色的搜索功能。在其他工具中都没有看到过这样的东西,特别是没有这么快。不要介意它在Tk中,它就是太棒了
    • git gui:即使不是最性感的,也可以正常工作-对于新手发现东西非常有帮助
    • blame:一个奇迹。是的,它会检测原始段来自哪里(复制&粘贴等)
    • mergetool:比启动大型svn merge要愉快得多,后者每次(即每5分钟)遇到冲突时都会停止,按“(p)ostpone”按钮,然后稍后手动搜索冲突文件。更喜欢在git gui中集成此类工具(需要进行小修补程序)。 发现与svn相比,集成外部差异工具更易配置。
    • 可插拔合并驱动程序和对它们的细粒度控制
    • rebase允许过滤掉较混乱的svn历史记录部分
  • 分布式:在这项工作上无需到办公室,可以在火车/飞机上暂停和逐步进展。
    • 使用Unison的USB驱动器使同步工作<->家变得轻而易举
    • 如果没有git的疯狂压缩(具有26k个提交、大量分支和二进制文件的5年项目,主干svn检出:1.9Gb =>所有这些在完整的git存储库中:1.4Gb!),这是不可能的

因此,这确实可以使从噩梦到喜悦的差别——特别是如果你喜欢学习(在这种情况下需要付出一些努力——我想就像从自行车转向摩托车一样)。

尽管我不能强迫公司中的每个人立即切换——我真的没有打算。同样,git-svn通过“先试水”方法拯救了我们。但看到同事们的反应,切换可能会比任何人预期的早:)

我想说——即使我们忘记合并和提交,这些东西已经很适合作为只读前端用于查询、可视化、备份等等。

注意:

"不要向Subversion存储库dcommit Git合并提交。Subversion没有像Git那样处理合并,这会引起问题。这意味着您应该保持Git开发历史线性(即,没有从其他分支合并,只有变基)。"(http://learn.github.com/p/git-svn.html的最后一段)

另一个出色的资源是Pro Git book,其中“切换活动分支”一节基本上说合并确实有效,但dcommit只会存储合并内容,但历史记录将被破坏(这会破坏后续合并),因此您应该在合并后删除工作分支。无论如何,这毕竟是有道理的,在实践中很容易避免陷阱...在svn中,我发现人们通常不重新合并,因此如果您首先来自git世界,则可能只能看作是一步后退。
总之,dcommit对我很有效。我将其放在了自己的svn工作分支上,这只是为了这个目的而保留的,所以那次避免了任何额外的冲突。但是,我决定在svn中从这个工作分支到svn主干进行最终合并(在git中同步所有内容之后);--ignore-ancestry在那里给出了最好的结果。
更新:正如我后来发现的那样,上述最后几个步骤(额外的svn分支和merge--ignore-ancestry)可以通过保持您正在进行的分支线性来轻松避免。正如下面的Gabe所说,merge--squash只会创建一个简单愚蠢的svn友好提交。只有在准备好在我的本地分支上进行大量合并(可能需要数天/数周)时,我现在才会:
git checkout -b dcommit_helper_for_svnbranch  svnbranch
git merge --squash huge_merge_work_with_messy_nonlinear_history
git commit 'nice merge summary' # single parent, straight from the fresh svnbranch
git dcommit

我知道合并跟踪在svn方面不会很好,直到我们完全切换。我等不及了。


更新: @Kevin要求更多关于合并svn分支的整个过程的细节。有很多文章和帖子,但作为一个新手,我发现其中一些令人困惑/误导/已经过时。无论如何,我现在的做法是(当然,在那次合并事件之后被困在git-svn中;就像一些新感染的同事一样)...

git svn clone -s http://svn/path/to/just-above-trunk  # the slowest part, but needed only once ever..you can every single branch from the svn repo since revision #1. 2) 
git svn fetch          # later, anytime: keep it up to date, talking to svn server to grab new revisions. Again: all branches - and yet it's usually a faster for me than a simple 'svn up' on the trunk:)    
# Take a look, sniff around - some optional but handy commands:
git gui   &    # I usually keep this running, press F5 to refresh
gitk --all     # graph showing all branches
gitk my-svn-target-branch svn-branch-to-merge    # look at only the branches in question
git checkout -b my-merge-fun my-svn-target-branch  # this creates a local branch based on the svn one and switches to it..before you notice :)
# Some handy config, giving more context for conflicts
git config merge.conflictstyle diff3
# The actual merge.. 
git merge  svn-branch-to-merge    # the normal case, with managable amount of conflicts
# For the monster merge, this was actually a loop for me: due to the sheer size, I split up the 2 year period into reasonable chunks, eg. ~1 months, tagged those versions ma1..ma25 and mb1..mb25 on each branch using gitk, and then repeated these for all of them
git merge ma1   # through ma25
git merge mb1   # through mb25
# When running into conflicts, just resolve them.. low tech way: keep the wanted parts, then "git add file" but you can
git mergetool   # loops through each conflicted file, open your GUI mergetool of choice..when successful, add the file automatically.
git mergetool  my-interesting-path # limit scope to that path

实际上,我更喜欢使用“git gui”内置的合并工具集成(右键单击冲突文件)。但这有点受限,因此请参见我的小补丁,它允许您插入一个shell脚本,您可以在其中调用任何您喜欢的合并工具(我尝试了各种工具,有时并行使用它们会引起很多麻烦...但通常我卡在kdiff3上)。
当合并步骤顺利进行(没有冲突)时,自动执行合并提交;否则,您需要解决冲突。
git commit  # am usually doing this in the git gui as well.. again, lightning fast.

最后一个阶段..请注意,到目前为止我们只有本地提交,还没有与svn服务器通信。除非您使用了--squash或其他技巧,否则您现在会得到一个图形,其中您的合并提交有2个父级:您的svn-mirror分支的提示。现在这是常见的问题:svn只能接受线性历史记录..所以'git-svn'通过仅删除第二个父级(上述情况中的svn-branch-to-merge)来简化它..因此,在svn方面,真正的合并跟踪已经消失..但在这种情况下,它仍然可以正常工作。
如果您想要更安全/更清洁的方法,这就是我早期片段的用途:只需使用--squash进行最终合并。将早期片段调整为此流程:
git checkout -b dcommit_helper_for_svnbranch my-svn-target-branch  # another local workbranch.. basically needed as svn branches (as any other remote branch) are read-only
git merge --squash my-merge-fun  
git commit 'nice merge summary' # single parent, straight from the fresh svn branch
git dcommit  # this will result in a 'svn commit' on the my-svn-target-branch

抱歉,这篇文章已经太长了,我要停止了。祝你好运。

嗨Inger,太好了,你能让它工作!我是一个git新手,正在评估git是否适用于这种情况——合并具有大量文件和许多错误SVN冲突的两个分支。您能否编写一个简短的代码清单,说明如何设置存储库并执行工作?例如:git svn clone myrep; git checkout -b branchA; git checkout -b branchB; git merge branchA branchB; git commit,git svn dcommit等?在开始之前,我想确保我完全理解您的方法,因为git svn clone需要几天时间:-(提前致谢。 - Kevin P.
更新了我的答案..希望能有所帮助。我猜这种东西对于新手来说可能会有点混淆(对我来说肯定是这样),但过一段时间它就会自然而然地变得清晰明了。关于你的示例命令,只需要一个checkout即可。那个命令有点类似于'svn fetch'。在最简单的情况下,你只需要在你想要合并的位置,然后执行'git merge otherbranch'。 - inger
晚些时候我发现自己打错了字:“那个命令有点类似于'svn fetch'。”应该是“.. svn switch”。 - inger
我刚刚(大部分)成功地完成了这个任务,但我注意到在我的合并过程中,源分支上执行了“git mv”操作的文件的历史记录丢失了。有没有办法避免这种情况?我想我需要在移动文件之前进行dcommit,然后在svn工作副本上使用tortoisesvn来修复它。 - gtrak
也许 Gabe 提到的设置 git config diff.renamelimit 0 对你有用? - inger

3

现在有新的工具可用,可以解决git-svn的许多问题,并为使用Subversion和Git提供更好的体验。

除其他问题外,这些工具还可以解决一些分支和合并的问题。以下是概述:

  1. git-svn

    From the documentation:

    CAVEATS

    ...

    Running git merge or git pull is NOT recommended on a branch you plan to dcommit from. Subversion does not represent merges in any reasonable or useful fashion; so users using Subversion cannot see any merges you've made. Furthermore, if you merge or pull from a git branch that is a mirror of an SVN branch, dcommit may commit to the wrong branch.

    There are primarily three reasons not to dcommit merge commits:

    • git-svn doesn't automatically send svn:mergeinfo property for merged branches. As result Subversion is not able to track those merges performed by git. This includes normal Git merges and cherry-picks.

    • as git-svn does not convert svn:ignore, svn:eol-style and other SVN properties automatically, merge commit does not have corresponding metadata in Git. As result, dcommit does not send these properties to SVN repository, so they get lost.

    • dcommit always sends changes to the branch referenced by a first parent of a merge commit. Sometimes changes appear where user doesn't expect them.

  2. SubGit

    SubGit is a Git-SVN bi-directional server-side mirror.

    If one has local access to Subversion repository, one can install SubGit into it:

    $ subgit configure $SVN_REPOS
    # Adjust $SVN_REPOS/conf/subgit.conf to specify your branches and tags
    # Adjust $SVN_REPOS/conf/authors.txt to specify git & svn authors mapping
    $ subgit install $SVN_REPOS
    ...
    $ INSTALLATION SUCCESSFUL
    

    At this moment SubGit converts Subversion repository into Git (it works in opposite direction as well) and installs SVN and Git hooks. As result Subversion and Git repositories are synchronized: every commit and push starts hooks that convert incoming modifications immediately.

    SubGit converts svn:ignore properties into .gitignore files, svn:eol-style and svn:mime-type properties to .gitattributes, so merge commits in Git retain this metadata.

    When one pushes merge commit, SubGit converts all the new commits into Subversion revisions. It honors svn:mergeinfo property, so merge operation is properly tracked by SVN afterwards.

    Even if user pushes very complex Git history, SubGit converts all the commits keeping the merge tracking data valid. We once pushed the whole history of git.git repository at once and it was properly converted into SVN.

    SubGit is a commercial product. It is free for open-source and academic projects and also for projects with up to 10 committers.

    For more details please refer to SubGit documentation and git-svn comparison page.

  3. SmartGit

    SmartGit is a client-side alternative for git-svn.

    SmartGit also supports svn:ignore, svn:eol-style and svn:mime-type properties conversion. And it also sets svn:mergeinfo property for merge commits. It even updates necessary merge tracking data for cherry-pick commits.

    SmartGit is a commercial Git and Mercurial client. It is free for non-commercial usage.

完全透露:我是SubGit开发人员之一。


谢谢,我之前听说过SubGit。它似乎是一种强大的方法,但更针对完全转换。上面的问题更像是一个试水实验,即在没有服务器访问权限和公司任何人注意到的情况下进行单个svn合并。无论如何,在此期间,我成功地将团队转移到了Git,并且还有其他一些人,IT人员正在计划进行完整的迁移,双模式可能会很有用。所以,感谢您的提示。顺便问一下,mergeinfo在所有版本的Svn中都可靠吗? - inger
我们现在正在开发SubGit 2.0,它支持不同主机上Git和SVN仓库的双向同步。基本上,您可以在任何地方创建Git仓库,指定SVN仓库URL并启用这些仓库的SubGit同步。Git端会稍微延迟一些,而SVN端则没有任何延迟。 - vadishev
关于svn:mergeinfo,自1.5版本起,SVN已经支持此属性,也就是说已经有4年多的历史了。在此之前,SVN根本不会跟踪合并历史。对于SVN 1.8,他们计划引入“对称”合并功能,这应该可以增强合并跟踪。希望这能让SVN在这方面与Git/Hg等工具齐头并进。 - vadishev

3

我刚刚自己完成了这个。一种更简单的方法是传递git merge选项--squash,这将执行合并而不记录合并提交,使历史线性化以避免混淆git-svn。

我的合并也非常大,我必须设置git config diff.renamelimit 0,以便git可以正确找到所有重命名。


1
谢谢你的回复。是的,我有时也使用--squash合并。当您想要一个单独的组合提交而不是许多小提交(作为“rebase -i”的替代方法)时,它也很方便。 - inger
--no-ff合并选项似乎是为此而设计的:git merge --no-ff mybranch - Shadow Man

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