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