135

我尝试 git-svn 的动机是轻松的合并和分支。然后我注意到 man git-svn(1) 说:

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

这是否意味着我不能从 svn/trunk(或一个分支)创建一个本地分支,破解,合并回 svn/trunk,然后 dcommit?我知道 svn 用户会看到与 svn pre 1.5.x 中合并的相同混乱,但还有其他缺点吗?最后一句话也让我担心。人们是否经常做这些事情?

4

6 回答 6

175

--no-ff实际上,我在 git merge 上找到了一个更好的方法。不再需要我以前使用的所有这些壁球技术。

我的新工作流程现在如下:

  • 我有一个“主”分支,它是我 dcommit 的唯一分支,它克隆了 SVN 存储库(-s假设您在存储库中有一个标准的 SVN 布局trunk/、、branches/tags/):

    git svn clone [-s] <svn-url>
    
  • 我在本地分支“工作”上工作(-b创建分支“工作”)

    git checkout -b work
    
  • 在本地提交到“工作”分支(-s以签署您的提交消息)。在续集中,我假设你做了 3 次本地提交

    ...
    (work)$> git commit -s -m "msg 1"
    ...
    (work)$> git commit -s -m "msg 2"
    ...
    (work)$> git commit -s -m "msg 3"
    

现在你想提交到 SVN 服务器

  • [最终] 将你不想看到的修改存储在 SVN 服务器上(通常你在主文件中注释了一些代码只是因为你想加速编译并专注于给定的特性)

    (work)$> git stash
    
  • 使用 SVN 存储库 rebase 主分支(从 SVN 服务器更新)

    (work)$> git checkout master
    (master)$> git svn rebase
    
  • 回到工作分支并与 master 进行 rebase

    (master)$> git checkout work
    (work)$> git rebase master
    
  • 确保一切正常,例如:

    (work)$> git log --graph --oneline --decorate
    
  • 现在是时候使用这个美妙的--no-ff选项将所有三个提交从“工作”分支合并到“主”

    (work)$> git checkout master
    (master)$> git merge --no-ff work
    
  • 您可以注意到日志的状态:

    (master)$> git log --graph --oneline --decorate
    * 56a779b (work, master) Merge branch 'work'
    |\  
    | * af6f7ae msg 3
    | * 8750643 msg 2
    | * 08464ae msg 1
    |/  
    * 21e20fa (git-svn) last svn commit
    
  • 现在您可能想要编辑 ( amend) 您的 SVN 帅哥的最后一次提交(否则他们只会看到一个带有“Merge branch 'work'”消息的提交

    (master)$> git commit --amend
    
  • 最后在 SVN 服务器上提交

    (master)$> git svn dcommit
    
  • 回去工作并最终恢复您隐藏的文件:

    (master)$> git checkout work
    (work)$> git stash pop
    
于 2010-11-21T15:05:31.930 回答
49

使用 git-svn 绝对可以创建本地分支。只要您只是为自己使用本地分支,而不是尝试使用 git 在上游 svn 分支之间进行合并,就可以了。

我有一个用于跟踪 svn 服务器的“主”分支。这是我 dcommit 的唯一分支。如果我正在做一些工作,我会创建一个主题分支并着手处理它。当我想提交它时,我会执行以下操作:

  1. 将所有内容提交到主题分支
  2. git svn rebase (解决你的工作和svn之间的任何冲突)
  3. git结账大师
  4. git svn rebase (这使下一步成为快进合并,请参阅下面 Aaron 的评论)
  5. git 合并主题分支
  6. 解决任何合并冲突(此时不应该有)
  7. git svn dcommit

我还有另一种情况,我需要维护一些永远不应该推送到 svn 的本地更改(用于调试)。为此,我有上面的 master 分支,但也有一个名为“work”的分支,我通常在那里工作。主题分支从工作中分支出来。当我想在那里提交工作时,我签出 master 并使用cherry-pick 从我想要提交到 svn 的工作分支中挑选提交。这是因为我想避免提交三个本地更改提交。然后,我从 master 分支 dcommit 并重新设置所有内容。

值得git svn dcommit -n首先运行以确保您即将提交您打算提交的内容。与 git 不同,在 svn 中重写历史很困难!

我觉得必须有更好的方法来合并主题分支上的更改,同时跳过那些本地更改提交而不是使用cherry-pick,所以如果有人有任何想法,他们会受到欢迎。

于 2008-10-10T07:47:13.373 回答
33

简单的解决方案:合并后删除“工作”分支

简短回答:您可以随意使用 git(请参阅下面的简单工作流程),包括合并。只需确保在每个“ git merge work ”后面加上“ git branch -d work ”即可删除临时工作分支。

背景说明: 合并/dcommit 问题是,每当您“git svn dcommit”一个分支时,该分支的合并历史就会“变平”:git 忘记了进入该分支的所有合并操作:只保留文件内容,但是该内容(部分)来自特定的其他分支的事实丢失了。请参阅:为什么 git svn dcommit 会丢失本地分支的合并提交历史记录?

(注意:git-svn 对此无能为力:svn 根本无法理解更强大的 git 合并。因此,在 svn 存储库中,无法以任何方式表示此合并信息。)

但这是整个问题。如果您在将“工作”分支合并到“主分支”后删除它,那么您的 git 存储库是 100% 干净的,并且看起来与您的 svn 存储库完全一样。

我的工作流程: 当然,我首先将远程 svn 存储库克隆到本地 git 存储库(这可能需要一些时间):

$> git svn clone <svn-repository-url> <local-directory>

然后所有工作都发生在“本地目录”中。每当我需要从服务器获取更新(如“svn update”)时,我都会:

$> git checkout master
$> git svn rebase

我在一个单独的分支“工作”中完成所有开发工作,该分支的创建方式如下:

$> git checkout -b work

当然,您可以为您的工作创建任意数量的分支,并根据需要在它们之间合并和变基(只需在完成后删除它们 --- 如下所述)。在我的正常工作中,我经常提交:

$> git commit -am '-- finished a little piece of work'

下一步 (git rebase -i) 是可选的 --- 它只是在将其归档到 svn 之前清理历史:一旦我达到了我想与他人分享的稳定里程碑,我会重写这个“工作”的历史分支并清理提交消息(其他开发人员不需要看到我在路上犯的所有小步骤和错误 --- 只是结果)。为此,我做

$> git log

并复制 svn 存储库中最后一次提交的 sha-1 哈希(如 git-svn-id 所示)。然后我打电话

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

只需粘贴我们上次 svn 提交的 sha-1 哈希值,而不是我的。您可能需要阅读带有 'git help rebase' 的文档以了解详细信息。简而言之:这个命令首先会打开一个编辑器来展示你的提交 ---- 只需将所有你想用以前的提交压缩的提交更改为 'pick' 为 'squash'。当然,第一行应该保留为“选择”。通过这种方式,您可以将您的许多小提交压缩成一个或多个有意义的单元。保存并退出编辑器。您将得到另一个编辑器,要求您重写提交日志消息。

简而言之:在我完成“代码黑客”之后,我会按摩我的“工作”分支,直到它看起来我想如何将它呈现给其他程序员(或者当我浏览历史记录时我想在几周内看到工作) .

为了将更改推送到 svn 存储库,我这样做:

$> git checkout master
$> git svn rebase

现在我们回到旧的“master”分支,更新了 svn 存储库中同时发生的所有更改(您的新更改隐藏在“工作”分支中)。

如果有可能与您的新“工作”更改发生冲突的更改,您必须在本地解决它们,然后才能推送您的新工作(请参阅下面的详细信息)。然后,我们可以将我们的更改推送到 svn:

$> git checkout master
$> git merge work        # (1) merge your 'work' into 'master'
$> git branch -d work    # (2) remove the work branch immediately after merging
$> git svn dcommit       # (3) push your changes to the svn repository

注意 1:命令 'git branch -d work' 非常安全:它只允许您删除不再需要的分支(因为它们已经合并到您当前的分支中)。如果在将工作与“主”分支合并之前错误地执行了此命令,则会收到错误消息。

注意 2:确保在合并和 dcommit之间使用 'git branch -d work' 删除您的分支:如果您尝试在 dcommit 之后删除分支,则会收到错误消息:当您执行 'git svn dcommit' 时,git 会忘记您的分支已与“master”合并。您必须使用不进行安全检查的“git branch -D work”将其删除。

现在,我立即创建一个新的“工作”分支,以避免意外入侵“主”分支:

$> git checkout -b work
$> git branch            # show my branches:
  master
* work

将您的“工作”与 svn 上的更改集成: 当“git svn rebase”显示其他人在我的“工作”分支上工作时更改了 svn 存储库时,我会这样做:

$> git checkout master
$> git svn rebase              # 'svn pull' changes
$> git checkout work           # go to my work
$> git checkout -b integration # make a copy of the branch
$> git merge master            # integrate my changes with theirs
$> ... check/fix/debug ...
$> ... rewrite history with rebase -i if needed

$> git checkout master         # try again to push my changes
$> git svn rebase              # hopefully no further changes to merge
$> git merge integration       # (1) merge your work with theirs
$> git branch -d work          # (2) remove branches that are merged
$> git branch -d integration   # (2) remove branches that are merged
$> git svn dcommit             # (3) push your changes to the svn repository

存在更强大的解决方案: 呈现的工作流程非常简单:它仅在每一轮“更新/黑客/dcommit”中使用 git 的功能 --- 但使长期项目历史与 svn 存储库一样线性。如果您只是想在遗留 svn 项目中的小第一步中开始使用 git 合并,这是可以的。

当您对 git 合并更加熟悉时,请随意探索其他工作流程:如果您知道自己在做什么,可以将 git merges 与 svn merges 混合使用(使用 git-svn(或类似的)只是为了帮助 svn merge?

于 2010-12-28T13:34:25.590 回答
8

Greg Hewgill 上面的回答是不安全的!如果在两个“git svn rebase”之间的主干上出现任何新的提交,合并将不会快进。

可以通过在 git-merge 中使用“--ff-only”标志来确保,但我通常不在分支中运行“git svn rebase”,只在其上运行“git rebase master”(假设它只是一个本地分支)。然后保证“git merge thebranch”快进。

于 2010-12-16T11:49:33.077 回答
6

在 git 中合并 svn 分支的一种安全方法是使用 git merge --squash。这将创建一个提交并停止供您添加消息。

假设您有一个主题 svn 分支,称为 svn-branch。

git svn fetch
git checkout remotes/trunk -b big-merge
git merge --squash svn-branch

此时,您已将 svn-branch 中的所有更改压缩为一个在索引中等待的提交

git commit
于 2009-10-06T16:30:11.923 回答
5

将本地 git 分支重新定位到主 git 分支,然后 dcommit ,这样看起来你按顺序完成了所有这些提交,因此 svn 人们可以按照他们习惯的方式线性地看到它。因此,假设您有一个名为 topic 的本地分支,您可以这样做

git rebase master topic

然后它将在主分支上播放您的提交,准备好让您 dcommit

于 2010-03-11T09:12:22.173 回答