试图回答我的问题:使用 git 进行 svn 合并似乎很有希望。
更新:这不仅很有希望,而且取得了巨大的成功。简而言之,Linus was right。
刚刚完成了2个已经分开1.5年的svn分支的巨大合并;修改了 3k 个文件,在 svn 中有大量冲突(我认为大约有 800 个)。
我发现 git 和 git-svn 可以救命:
- 自动冲突解决:首先,它提供的冲突文件少了很多(我认为大约有一半)
- 令人难以置信的性能
- 优秀的repo/branching model,灵活的工作流程:轻松尝试各种方法,例如逐块(及时)合并,始终进行完整性检查(编译等);每当遇到麻烦时:只是回溯。您可以随时在需要时退后一步。
- 可用性,出色的工具:
-
git-log(以及底层的git-rev-parse 选项),没有比这更强大的了。它也很方便:-p 一次性为您提供差异;在 svn 中你会得到一个日志,然后找到那个“revision-1:revision”的差异,或者使用笨拙的 UI。查找何时将字符串添加/删除到 repo 中,同时搜索多个分支
-
gitk:对于可视化分支历史非常有用,并结合了强大的搜索功能。还没有在其他工具中看到过这样的东西,尤其是没有这么快。没关系,它在 Tk 中,它很棒
-
git gui:即使不是最性感也能正常工作 - 对新手发现事物有很大帮助
-
blame:奇迹。是的,它会检测原始片段的来源(复制和粘贴等)
-
mergetool:比启动大 svn merge 更愉快的体验,然后每次(即每 5 分钟)遇到冲突时停止,按“(p)ostpone”,而不是稍后手动寻找冲突的文件。首选集成在git gui 中的这种风格(为此需要tiny patch)。
发现集成外部差异工具比在 svn 中配置更好。
- 可插入的合并驱动程序和对它们的细粒度控制
-
rebase 允许过滤掉 svn 历史中比较混乱的部分
- 分布:工作时不需要来办公室,可以在火车/飞机等上暂停和逐步进行。
- 带有Unison 的 USB 驱动器让同步工作变得轻松轻松搞定
- 如果没有 git 的疯狂压缩,这是不可能的(5 年历史的项目,有 26k 次提交,大量的分支和二进制文件,trunk svn checkout:1.9Gb => 所有这些都在完整的 git repo 中:1.4Gb! )
所以,这真的可以让噩梦变成欢乐——尤其是如果你喜欢学习(在这种情况下确实需要一些努力——我想就像在骑自行车之后学习摩托车)。
尽管我不能强迫公司里的每个人都立即切换——我真的不打算这样做。同样,git-svn 通过“先入为主”的方法拯救了我们。但看到同事的反应,这种转变可能会比任何人预期的发生得多:)
我想说 - 即使我们忘记了合并和提交,这些东西作为查询、可视化、备份等的只读前端已经很棒了。
警告:
"Do not 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 分支和合并 --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's builtin mergetool 集成(右键单击文件冲突)。不过这有点有限,所以请看我上面的小补丁,它允许您插入一个 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方面去了..但在这种情况下它很好。
如果您想要更安全/更清洁的方式,这就是我之前的 sn-p 的用武之地:只需与 --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
哎呀,这太长了,太迟了..祝你好运。