在 git 中合并后 git-svn dcommit 是否危险?

我尝试 git-svn 的动机是不费吹灰之力的合并和分支。然后我注意到 git-svn (1)说:

不建议在计划的分支上运行 git-merge 或 git-pull Subversion 不表示任何 合理或有用的方式; 因此使用 Subversion 的用户不能看到任何 合并。此外,如果你合并或从一个 git 拉 分支的镜像,dcommit 可以提交到 错误的分支。

这是否意味着我不能从 svn/躯干(或一个分支)创建一个本地分支,然后删除,合并回 svn/躯干,然后 dcommit?我理解 svn 用户会看到与合并 svn pre 1.5. x 时一样的混乱,但是还有什么其他的缺点吗?最后一句话我也很担心。人们经常做这种事吗?

36934 次浏览

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

我有一个“ master”分支,用于跟踪 svn 服务器。这是我唯一的分支。如果我正在做一些工作,我会创建一个主题分支,然后开始工作。当我想提交时,我会做以下事情:

  1. 将所有内容提交到主题分支
  2. 返回文章页面 git svn rebase (解决工作和 svn 之间的任何冲突)
  3. Git 收银大师
  4. 返回文章页面 git svn rebase (这使得下一步快进合并,见下面 Aaron 的评论)
  5. 主题 _ 分支
  6. 解决任何合并冲突
  7. 快点,快点,快点

我还有另一种情况,需要维护一些本地更改(用于调试) ,而这些更改永远不应该推送到 svn。为此,我有上述主分支,但也有一个分支所谓的“工作”,我通常做的工作。主题分支是工作的分支。当我想在那里提交工作时,我会签出 master 并使用初始选择从我想提交给 svn 的工作分支中挑选提交。这是因为我想避免提交三个本地更改提交。然后,我从主分支提交和重新基础的一切。

首先运行 git svn dcommit -n是值得的,它可以确保您准备提交您想要提交的内容。与 git 不同,在 svn 中重写历史非常困难!

我觉得一定有更好的方法来合并主题分支上的更改,同时跳过那些本地更改提交,而不是使用择优选择,因此,如果有人有任何想法,他们将受到欢迎。

合并 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

将本地 git 分支重新基于 master git 分支,然后 dcommit,这样看起来就像您按顺序执行了所有这些提交,这样 svn 用户就可以线性地看到它,就像他们习惯的那样。因此,假设您有一个名为 subject 的本地分支,那么可以这样做

git rebase master topic

然后,它将在主分支上播放您的提交,这个主分支已经为您准备好了 dcommit

实际上,我找到了一个更好的方法,在 git 合并时使用 --no-ff选项。 我以前用过的所有壁球技术都不再需要了。

我的新工作流程如下:

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

    git svn clone [-s] <svn-url>
    
  • I work on a local branch "work" (-b creates the branch "work")

    git checkout -b work
    
  • commit locally into the "work" branch (-s to sign-off your commit message). In the sequel, I assume you made 3 local commits

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

Now you want to commit onto the SVN server

  • [Eventually] stash the modifications you don't want to see committed on the SVN server (often you commented some code in the main file just because you want to accelerate the compilation and focus on a given feature)

    (work)$> git stash
    
  • rebase the master branch with the SVN repository (to update from the SVN server)

    (work)$> git checkout master
    (master)$> git svn rebase
    
  • go back to the work branch and rebase with master

    (master)$> git checkout work
    (work)$> git rebase master
    
  • Ensure everything is fine using, for instance:

    (work)$> git log --graph --oneline --decorate
    
  • Now it's time to merge all three commits from the "work" branch into "master" using this wonderful --no-ff option

    (work)$> git checkout master
    (master)$> git merge --no-ff work
    
  • You can notice the status of the logs:

    (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
    
  • Now you probably want to edit (amend) the last commit for your SVN dudes (otherwise they will only see a single commit with the message "Merge branch 'work'"

    (master)$> git commit --amend
    
  • Finally commit on the SVN server

    (master)$> git svn dcommit
    
  • Go back to work and eventually recover your stashed files:

    (master)$> git checkout work
    (work)$> git stash pop
    

格雷格 · 休吉尔的答案放在最上面不安全!如果在两个“ git svn rebase”之间的主干上出现任何新的提交,合并将不会快进。

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

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

简短的回答: 您可以按照自己的喜好使用 git (参见下面的简单工作流) ,包括 merge。只要确保在每个“ 合并工作”后面加上“ 工作”就可以删除临时工作分支。

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

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

但这是 完整的问题。如果在“ work”分支合并到“ master”分支之后删除它,那么 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 存储库中同时发生的所有更改(新的更改隐藏在“ 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: 使用‘ git Branch-d work’中间合并和 dcommit 确保删除分支: 如果在 dcommit 之后尝试删除分支,会得到一个错误消息: 当您使用‘ git svn dcommit’时,git 忘记了您的分支已经与‘ master’合并。你必须删除它与’git 分支-D 工作’,不做安全检查。

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

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

将您的“工作”与 svn 上的更改集成: 当‘ git svn rebase’显示其他人在我处理‘ work’分支时更改了 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 合并?)