27

我的项目中出现了一些复杂的颠覆合并:已经分开很长时间的大分支。Svn 提供了太多的冲突——其中一些似乎是虚假的。


鉴于它git以卓越的合并体验而受到称赞,git-svn 仅仅为了使合并更易于管理而使用它会有什么好处吗?


您能否推荐其他替代方案(例如svk, hgsvn)来减轻合并的痛苦?

有些冲突很容易解决(例如 java 导入、空格)——所以我也想知道是否有针对这些的自动化解决方案。

将来可能会完全切换到 DVCS(我们中的一些人会喜欢的),但不是现在。(更新:这不再是真的 - 团队最近完全切换并且对此感到高兴)。

提前致谢。

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

更新:在这条路下(和上)之后看到我的-新手回答。

4

3 回答 3

34

试图回答我的问题:使用 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

哎呀,这太长了,在为时已晚之前停止......祝你好运。

于 2010-06-05T19:27:01.917 回答
3

我自己刚刚解决了这个问题。一个更简单的方法是传递选项,它将执行合并而不记录合并提交,保持历史线性,以免混淆 git-svn。git merge--squash

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

于 2010-08-27T15:27:52.617 回答
3

有一些新工具可以修复 git-svn 的许多问题,并为使用 Subversion 和 Git 提供更好的体验。

除其他外,这些工具修复了一些分支和合并问题。这是一个概述:

  1. git-svn

    从文档中:

    注意事项

    ...

    不建议在您计划从中进行 dcommit 的分支上运行 git merge 或 git pull 。Subversion 不代表任何合理或有用的合并;因此使用 Subversion 的用户看不到您所做的任何合并。此外,如果您从作为 SVN 分支镜像的 git 分支合并或拉取,dcommit 可能会提交到错误的分支。

    不使用 dcommit 合并提交主要有三个原因:

    • git-svn 不会自动发送合并分支的 svn:mergeinfo 属性。结果,Subversion 无法跟踪 git 执行的那些合并。这包括正常的 Git 合并和挑选。

    • 由于 git-svn 不会自动转换 svn:ignore、svn:eol-style 和其他 SVN 属性,因此合并提交在 Git 中没有对应的元数据。结果,dcommit 不会将这些属性发送到 SVN 存储库,因此它们会丢失。

    • dcommit 总是将更改发送到合并提交的第一个父级引用的分支。有时更改会出现在用户不期望的地方。

  2. 子Git

    SubGit 是一个 Git-SVN 双向服务器端镜像。

    如果您可以本地访问 Subversion 存储库,则可以将 SubGit 安装到其中:

    $ 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
    

    此时 SubGit 将 Subversion 存储库转换为 Git(它也以相反的方向工作)并安装 SVN 和 Git 挂钩。结果 Subversion 和 Git 存储库是同步的:每次提交和推送都会启动钩子,这些钩子会立即转换传入的修改。

    SubGit 将 svn:ignore 属性转换为 .gitignore 文件,将 svn:eol-style 和 svn:mime-type 属性转换为 .gitattributes,因此 Git 中的合并提交会保留此元数据。

    当推送合并提交时,SubGit 将所有新提交转换为 Subversion 修订版。它尊重 svn:mergeinfo 属性,因此之后 SVN 会正确跟踪合并操作。

    即使用户推送了非常复杂的 Git 历史记录,SubGit 也会转换所有提交,保持合并跟踪数据有效。我们曾经一次推送了 git.git 存储库的整个历史,它被正确地转换为 SVN。

    SubGit 是一个商业产品。它对开源和学术项目以及最多 10 个提交者的项目都是免费的。

    有关详细信息,请参阅SubGit 文档git-svn比较页面。

  3. SmartGit

    SmartGit 是 git-svn 的客户端替代方案。

    SmartGit 还支持 svn:ignore、svn:eol-style 和 svn:mime-type 属性转换。它还为合并提交设置 svn:mergeinfo 属性。它甚至会更新必要的合并跟踪数据以进行挑选提交。

    SmartGit 是一个商业 Git 和 Mercurial 客户端。它可免费用于非商业用途。

全面披露:我是 SubGit 开发人员之一。

于 2012-10-06T02:04:56.207 回答