我尝试 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 中合并的相同混乱,但还有其他缺点吗?最后一句话也让我担心。人们是否经常做这些事情?
--mergeinfo=<mergeinfo>
选项可以将合并信息传递给 SVN。不知道应该如何使用它。
实际上,我在 git merge 上使用 --no-ff
选项找到了更好的方法。不再需要我以前使用的所有这些壁球技术。
我的新工作流程现在如下:
我有一个“主”分支,它是我提交的唯一分支,它克隆 SVN 存储库(-s 假设您在存储库主干/、分支/和标签/中有标准 SVN 布局): git svn clone [ -s]
我在本地分支“work”上工作(-b 创建分支“work”) git checkout -b work
在本地提交到“工作”分支(-s 以签署您的提交消息)。在续集中,我假设你做了 3 次本地提交 ... (work)$> git commit -s -m "msg 1" ... (work)$> git commit -s -m "msg 2" ... (工作)$> git commit -s -m "msg 3"
现在你想提交到 SVN 服务器
[最终] 将你不想看到的修改存储在 SVN 服务器上(通常你在主文件中注释了一些代码只是因为你想加速编译并专注于给定的功能)(工作)$> git stash
使用 SVN 存储库重新设置主分支(从 SVN 服务器更新)(工作)$> git checkout master (master)$> git svn rebase
回到工作分支并使用 master (master)$> git checkout work (work)$> git rebase master rebase
确保一切正常,例如: (work)$> git log --graph --oneline --decorate
现在是时候使用这个美妙的 --no-ff 选项 (work)$> git checkout master (master)$> git merge --no-ff work 将“work”分支中的所有三个提交合并到“master”中
您可以注意到日志的状态: (master)$> git log --graph --oneline --decorate * 56a779b (work, master) Merge branch 'work' |\ | * af6f7ae 味精 3 | * 8750643 味精 2 | * 08464ae msg 1 |/ * 21e20fa (git-svn) 上次 svn 提交
现在您可能想要编辑(修改)您的 SVN 帅哥的最后一次提交(否则他们只会看到一个带有消息“合并分支'工作'”(master)$> git commit --amend 的提交
最后在 SVN 服务器 (master) 上提交 $> git svn dcommit
回去工作并最终恢复你隐藏的文件: (master)$> git checkout work (work)$> git stash pop
使用 git-svn 绝对可以创建本地分支。只要您只是为自己使用本地分支,而不是尝试使用 git 在上游 svn 分支之间进行合并,就可以了。
我有一个用于跟踪 svn 服务器的“主”分支。这是我 dcommit 的唯一分支。如果我正在做一些工作,我会创建一个主题分支并着手处理它。当我想提交它时,我会执行以下操作:
将所有内容提交到主题分支 git svn rebase (解决您的工作和 svn 之间的任何冲突) git checkout master git svn rebase (这使下一步成为快速合并,请参阅下面的 Aaron 评论) git merge topic_branch 解决任何合并冲突(此时不应该有) git svn dcommit
我还有另一种情况,我需要维护一些永远不应该推送到 svn 的本地更改(用于调试)。为此,我有上面的 master 分支,但也有一个名为“work”的分支,我通常在那里工作。主题分支从工作中分支出来。当我想在那里提交工作时,我签出 master 并使用cherry-pick 从我想要提交到 svn 的工作分支中挑选提交。这是因为我想避免提交三个本地更改提交。然后,我从 master 分支 dcommit 并重新设置所有内容。
值得首先运行 git svn dcommit -n
以确保您即将提交您打算提交的内容。与 git 不同,在 svn 中重写历史很困难!
我觉得必须有更好的方法来合并主题分支上的更改,同时跳过那些本地更改提交而不是使用cherry-pick,所以如果有人有任何想法,他们会受到欢迎。
简单的解决方案:合并后删除“工作”分支
简短回答:您可以随意使用 git(请参阅下面的简单工作流程),包括合并。只需确保在每个 'git merge work' 后面加上 'git branch -d work' 即可删除临时工作分支。
背景说明: 合并/dcommit 的问题是,每当您“git svn dcommit”一个分支时,该分支的合并历史就会“变平”:git 忘记了进入该分支的所有合并操作:仅保留文件内容,但该内容(部分)来自特定其他分支的事实丢失了。请参阅:Why does git svn dcommit lose the history of merge commits for local branches?
(注意: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
存在更强大的解决方案:呈现的工作流程非常简单:它仅在每一轮“update/hack/dcommit”中使用 git 的功能 --- 但长期项目历史与 svn 存储库一样线性。如果您只是想在遗留 svn 项目中的小第一步中开始使用 git 合并,这是可以的。
当您对 git 合并更加熟悉时,请随意探索其他工作流程:如果您知道自己在做什么,您可以混合使用 git merge 和 svn merge (Using git-svn (or similar) just to help out with svn merge?)
git merge --squash work
,为什么不这样做呢?如果您要创建多个“选择”,我可以看到在合并之前在分支中进行壁球提交(假设您有 8 个提交,并且您将 4 个提交中的每一个转换为 1 个,并将 2 个提交合并到 master)。此外,在更新我的“工作”分支时,我会执行 rebase
,这比为我的分支创建另一个分支并进行合并更简单......
Greg Hewgill 上面的回答是不安全的!如果在两个“git svn rebase”之间的主干上出现任何新的提交,合并将不会快进。
可以通过在 git-merge 中使用“--ff-only”标志来确保,但我通常不在分支中运行“git svn rebase”,只在其上运行“git rebase master”(假设它只是一个本地分支)。然后保证“git merge thebranch”快进。
在 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
将本地 git 分支重新定位到主 git 分支,然后 dcommit ,这样看起来你按顺序完成了所有这些提交,因此 svn 人们可以按照他们习惯的方式线性地看到它。因此,假设您有一个名为 topic 的本地分支,您可以这样做
git rebase master topic
然后它将在主分支上播放您的提交,准备好让您进行 dcommit
--no-ff
选项运行合并,您将显式创建合并提交(具有两个父级的提交)而不是快进。为确保 svn-tracking 分支中的所有提交都是单父提交,所有合并必须是快进(--ff-only
可以帮助解决此问题),或者,如果后备箱在您背后发生了变化,则必须是--squash
,对吗?git svn dcommit
会重写您提供的 git 提交。这意味着您失去了它的另一个父级,现在您的 git 存储库没有您曾经将该分支合并到master
的记录。这也可能使您的工作目录处于不一致的状态。git merge --no-ff work -m "commit message"
而不是额外的git commit --amend
步骤