Git、SVN和分支合并

16

我正在处理一个有两个分支的svn项目,让我们称之为

trunk
branches/foo

我的想法是克隆整个svn仓库(告诉git哪些文件夹是主干、标签和分支),在git中进行合并,然后将我的合并结果复制到svn工作副本并从svn提交更改。

在这个工作流程中,我能否使用git的合并功能,还是只能用于由git自身创建的分支?


相关帖子:https://dev59.com/LXNA5IYBdhLWcg3wAI3d - zellus
3个回答

23

checkout 命令创建别名:

git config alias.co checkout

确保你的本地分支是最新的:

git co master    # checkout branch that tracks subversion's trunk
git svn rebase 
git co local/foo # checkout branch that tracks subversion's branches/foo
                 # It assumes that  the branch is created with the command:
                 # `git co -b local/foo remotes/foo`
                 # And the repo was created with:
                 # `git svn clone --stdlayout SVN_REPO_URL`
git svn rebase 

合并分支:

# create new local branch based on `master`
git co master
git co -b merging_branch_foo 
# merge, resolve conflicts, etc (pure git)
git merge local/foo  

# rebase `merging_branch_foo` to linearize history for subversion
git rebase master # or `rebase -i`

# merge `merging_branch_foo` into `master`
git co master
git merge merging_branch_foo # --squash to create single commit

# commit changes to svn
git svn dcommit

# (optionally) delete `merging_branch_foo`
git branch -D merging_branch_foo

方法是正确的,但是合并--squash步骤会删除更改的历史记录,是否有可以保留它们的解决方案? - QZHua
@J.F.Sebastian,抱歉我没有找到你所说的评论。你能找出来吗?非常感谢! - QZHua
@QZHua:选项“--squash”在注释中(在命令后面的“#”之后)不起作用,没有影响:“git merge merging_branch_foo # --squash创建单个提交”。 - jfs
好的,我被告知永远不要在git-svn中使用merge,因为当要合并的分支和svn分支没有相同的根时,它会导致非常糟糕的rebase问题。我找到的一个解决方案是在合并时使用--squash,但它会将所有提交打包成一个提交,因此提交历史在svn中丢失了。我的问题是我想在svn存储库中保留每个单独的提交,你是说不带--squash选项的git merge merging_branch_foo可以实现这一点吗?我不这么认为。 - QZHua
2
这个别名真的必要吗?我觉得它会让答案变得更加复杂。 - starbeamrainbowlabs
显示剩余12条评论

4
有一种使用 git 进行合并,但提交(上游)到 Subversion 的方法,虽然设置比较复杂,但实际上很强大(而且比使用 Subversion 进行合并要容易得多!)。首先,阅读 Derick Bailey 的 git+svn 概述,因为您需要按照他的指示设置 git 和 SVN 忽略文件。
请注意,这个方法 不使用 标准的 git-svn 包,但手动复制了它的许多功能。如果您已经在使用 git-svn,请不要使用此方法。此外,只有在您将会反复从分支合并到主干(尤其是在从主干选择性地合并到分支时)时,才值得使用此方法,因为这可以利用 git 的历史记录来执行其他合并操作。
然后,基本步骤如下:
  1. /trunk/ SVN Checkout 到一个工作副本文件夹中;我假设它是 C:\trunk
  2. 在该文件夹中 git init 一个 git 存储库;设置 .gitignoregit add -Agit commit(请参阅上面的 git+svn)。
  3. 创建存储库的 git 克隆(在另一个文件夹中):git clone C:\trunk foo。我假设这个克隆位于 C:\foo 中。
  4. 删除 C:\foo 中除了 .git 子文件夹之外的所有内容,然后在 C:\foo 中 SVN Checkout /branches/foo
  5. 在 C:\foo 中运行 git add -A; git commit 将更改保存到 git 存储库中。这将创建初始 git 历史记录,该历史记录与 C:\trunk 中的历史记录分歧。
现在我们有两个文件夹,它们都是 git 存储库和 Subversion 工作副本;此外,git 认为这些文件夹是同一存储库的克隆。
C:\trunkC:\foo 文件夹中执行工作(或只需使用 svn update 获取其他人的工作)。定期运行 git add -A; git commit 将更改保存到您的 git 存储库中。

现在你想将foo分支合并回主干。在C:\trunk目录下,运行git pull C:\foo命令。这将拉取并合并C:\foo文件夹中的所有更改,该文件夹是跟踪/branches/fooSubversion分支的git存储库。如果必要,解决任何冲突并完成git提交。

现在你可以提交C:\trunk中的更改到Subversion,而无需使用Subversion执行合并操作。


谢谢,这似乎是我正在寻找的东西。这与使用git合并远程svn主干和foo,然后将它们复制到我的svn工作副本并通过svn提交有什么不同吗? - unkownt
@未知: 这种方法的区别在于创建一个包含branches/foo代码的git分支,可以利用git的历史知识执行多次合并(从分支到主干),并避免通常由Subversion在此场景下生成的冲突。此外,git必须知道一个共同的祖先--你不能只告诉它合并两个文件夹。 - Bradley Grainger
Bradley,我已经为你的答案点赞,并很可能很快将其选为正确答案。我只是想再等一会儿,看看其他人是否有建议。你认为Sebastian关于合并而不是dcommit的回答怎么样?在这种情况下,Git不会了解历史记录吗?我想你已经说过它不会,但我只是想百分之百确定。再次感谢你的回答。 - unkownt
我对git-svn并不是很熟悉(我使用“git+svn”方法的原因之一是,就像Derick Bailey一样,我无法让git-svn导入我的上游Subversion仓库而不崩溃),所以我无法确定他的答案是否解决了你的问题。他的答案假设你正在使用git-svn;如果是这样,你可能想要按照他的建议去做;如果不是,我的方法可能对你更好。 - Bradley Grainger

-1
我建议您在SVN项目中使用SmartGit。它对于挑选合并和完整合并都有非常好的支持,并且可以正确地修改svn:mergeinfo。

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