合并git后git-svn dcommit危险吗?


133

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

不建议在打算从中删除的分支上运行git-merge或git-pull。Subversion并不代表以任何合理或有用的方式进行合并;因此使用Subversion的用户无法看到您进行的任何合并。此外,如果您从SVN分支的镜像git分支合并或提取,则dcommit可能会提交到错误的分支。

这是否意味着我无法从svn / trunk(或分支)创建本地分支,修改,合并回svn / trunk,然后进行dcommit?我了解svn用户会看到与svn 1.5.x以前版本合并时相同的混乱局面,但是还有其他缺点吗?最后一句话也让我担心。人们通常会做这类事情吗?


14
这是错误的:Subversion并不代表以任何合理或有用的方式进行合并;因此使用Subversion的用户无法看到您进行的任何合并。此外,如果您从作为SVN分支镜像的git分支合并或提取,则dcommit可能会提交到错误的分支。Subversion不仅可以表示git merge跟踪信息,而且还可以表示更多的细粒度信息。是git-svn无法编写适当的svn:mergeinfo或使dcommit进入适当的分支。
2011年

2
@AlexanderKitaev:此后git-svn文档已更改,此外,还有一个--mergeinfo=<mergeinfo>选项可能能够将合并信息传递给SVN。不确定如何使用它。
Mr_and_Mrs_D 2014年

Answers:


174

实际上,我发现了--no-ffgit 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信息库重新建立master分支的基础(以从SVN服务器更新)

    (work)$> git checkout master
    (master)$> git svn 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
    

4
这正是git n00b正在寻找的工作流程。创建一个本地分支来修复错误,执行任何操作,然后通过一次提交将其发送到svn。在这里使用最高评分的答案弄乱了我在做什么-不能在没有错误的情况下git svn rebase。
若昂·布拉干萨

19
这不是op引用的git-svn文档警告的内容吗?通过使用该--no-ff选项运行merge ,您将显式创建合并提交(具有两个父项的提交),而不是快速前进。为了确保svn-tracking分支中的所有提交都是单亲提交,所有合并必须要么是快进的(--ff-only可以对此提供帮助),或者如果主干已经变了,则a --squash,对吗?
jemmons 2011年

4
这适用于您立即删除的一次性分支,但会git svn dcommit重写您赋予它的git commit。这意味着您失去了它的另一个父级,现在您的git repo没有该分支合并到的记录master。这也会使您的工作目录处于不一致状态。
rescdsk 2011年

9
使用git merge --no-ff work -m "commit message"而不是多余的git commit --amend步骤
tekumara 2012年

3
对我来说我宁愿使用“混帐合并仅--ff工作”,因为我想保留我的所有提交,而不仅仅是最后一个
Moataz Elmasry

49

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

我有一个“ master”分支,用于跟踪svn服务器。这是我拒绝的唯一分支。如果我正在做一些工作,那么我将创建一个主题分支并进行处理。当我要提交它时,请执行以下操作:

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

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

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

我觉得与使用cherry-pick相比,在跳过那些本地更改提交时,必须有一种更好的方法来将更改合并到主题分支上,因此,如果有人有任何想法,将受到欢迎。


如果我正确理解这一点,在git中,将git与svn合并是可以的,但不能将svn与svn合并?所以我不能在git中将svn / branch与svn / trunk合并?
Knut Eldhuset

1
如果您想做些琐碎的事情,那么用SVN重写历史记录就很难了。在非平凡的情况下,这实际上是不可能的。
亚里斯多德·帕加尔兹

19
格雷格·休吉尔(Greg Hewgill)的答案很多,但我认为这是错误的。仅是快速合并,从topic_branch合并到master才是安全的。如果是真正的合并,需要合并提交,那么在您提交时,合并提交将丢失。下次您尝试将topic_branch合并到master时,git认为第一次合并从未发生过,并且一切都变糟了。看到问题git svn dcommit为什么会丢失本地分支的合并提交的历史记录?
亚伦

1
@Greg Hewgill,编辑不清楚。您写道,重新设置基准“使下一个提交成为快速合并。” 我不知道这是什么意思,因为快进合并不涉及提交,但是您的意思可能是“使下一个合并成为快进合并”。但是,如果这就是您的意思,那是不正确的。从topic_branch到master的合并是否为快进合并取决于自分支点以来是否对master进行了任何提交。当你变基高手,创建于掌握新的提交,所以后续的合并是必然不是一个快进合并。
亚伦2010年

1
@Aaron:是的,我不知道我在想什么。我已对其进行了修复,希望这是有道理的。麻烦之一是,在Git中有很多方法可以做事情,以至于需要描述一些特定的动作序列。
格雷格·休吉尔

33

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

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

背景说明: merge / 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更新”)时,我都会:

$> 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 commit的sha-1哈希而不是我的。您可能需要阅读带有'git help rebase'的文档以了解详细信息。简而言之:此命令首先打开一个显示您的提交的编辑器,只需将您要与以前的提交一起压缩的所有提交的“ pick”更改为“ squash”。当然,第一行应保留为“选择”。这样,您可以将许多小的提交压缩为一个或多个有意义的单元。保存并退出编辑器。您将获得另一个编辑器,要求您重写提交日志消息。

简而言之:完成“代码黑客”工作后,我对“工作”分支进行按摩,直到它看起来像如何向其他程序员展示(或者我希望在几周的时间内浏览历史记录时看到它) 。

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

$> git checkout master
$> git svn rebase

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

如果有可能与您的新“工作”更改冲突的更改,则必须在本地解决它们,然后才能推送您的新工作(请参见下面的详细信息)。然后,我们可以将更改推送到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”非常安全:它仅允许您删除不再需要的分支(因为它们已经合并到当前分支中)。如果在将工作与“ master”分支合并之前错误地执行了此命令,则会收到错误消息。

注意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 merge,就可以了。

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


这似乎不必要地复杂。我听说有人在做git merge --squash work,为什么不这样做呢?如果要创建多个“ pick”,我可以看到在合并之前在分支中执行了南瓜提交(例如,您有8个提交,并且将4个提交中的每个都变成1个,并将2个提交合并到master中)。同样,在更新我的“工作”分支时,我这样做rebase,比为我的分支创建另一个分支并进行合并更简单...
void.pointer 2014年

8

格雷格·休吉尔(Greg Hewgill)的回答并不安全!如果在两个“ git svn rebase”之间的主干上出现任何新的提交,则合并将不会快速进行。

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


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分支压缩到一个提交中,等待索引

git commit

但是,正如其他人指出的那样,这确实会丢失提交的粒度。
卡扎伊2010年

@Tchalvak:有时候你只是想要那个。我经常在功能分支ala中提交“固定先前提交的烂摊子”,由于良好的声誉,我喜欢隐藏这些死胡同:-)
schoetbi 2011年

1
是的,尽管我认为这是走钢丝,因为每当您挤压时,您也失去了稍后将各个提交分开的能力。只是不同的提交标准选择问题。
卡扎伊2011年

1
@schoetbi是的,您有时需要在发布之前清除混乱的历史记录。这是'git rebase -i <trunk>'的重要帮助:您可以加入/重新排序/删除甚至split(!)提交,有选择地修复msg。
inger

5

将本地git分支重新建立到master git分支上,然后进行dcommit,这样就好像您按顺序执行了所有这些提交一样,以便svn人们可以按照自己的习惯线性地看到它。因此,假设您有一个名为topic的本地分支,则可以执行

git rebase master topic

然后它将在master分支上播放您的提交,以供您准备提交

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.