Git壁球没有数百次合并冲突?

时间:2017-02-22 17:25:18

标签: git

我已经在功能部门工作了好几个月了。在中间我们将master合并到分支中几次。现在,在分支完成之后,我们意识到很多来自功能分支的提交都有混乱的提交消息,将这些中的几个压缩在一起会很好。

但是,如何在不降入地狱的情况下这样做呢?我已经尝试过变基,但由于master拥有超过一百个新提交,我们的功能分支也是如此,而且事实大师已经在几个接触点合并到我们的分支中,我遇到了数百个在重新定位时合并冲突。有没有更简单的方法?我只想将一些提交消息压缩在一起。

感谢。

2 个答案:

答案 0 :(得分:5)

在临时分支中使用git merge --squash

我只是窃Lars Kellogg-Stedman的beautiful blog post

我认为这比公认的答案更简单,更简洁。

Lars提供了几个选项,但第一个选项是使用临时分支合并提交,如下所示:

稍微修改Lars所说的话:

  1. 签出一个基于master的新分支(如果您的功能分支不是基于master的,则查找相应的基础分支):
git checkout -b work master

(这将创建一个名为work的新分支,并将其作为当前分支。)

  1. 使用git merge --squash从杂乱的拉取请求中引入更改:
git merge --squash my_feature

这会将my_feature分支中的所有更改引入并分段执行,但不会创建任何提交。

  1. 使用适当的提交消息提交更改:
git commit -m "my squash commit message"

这时,您的work分支应该与原始的my_feature分支相同(运行git diff master不应显示任何更改),但在master之后只有一个提交。 / p>

  1. 返回到您的功能分支并将其重置为压缩版本:
git checkout my_feature
git reset --hard work
  1. 更新您的提取请求:
git push -f
  1. (可选)清理您的工作分支:
git branch -D work

答案 1 :(得分:2)

TL; DR摘要:git rebase -i <commit>

基本上,你需要找到你想要处理的分支点&#34;分叉&#34;,然后使用它作为<upstream>的{​​{1}}参数。< / p>

解释

选择性地将各种提交压缩在一起的最简单方法是使用git rebase(然后以交互方式编辑rebase指令)。我认为这就是你在做的事情。

问题当然是你要击中的问题 - 这也是 git rebase 。 :-)如果你天真地运行它,它不仅给你一个交互式的rebase,让你大惊小怪的说明,从而重新构建你的提交链,它还试图更改你的提交的基础

让我们快速看看git rebase -i做什么(交互与否)以及我所谓的&#34;提交链&#34;这里。

绘制图表

与Git的大多数事情一样,坐下来(或站在白板上,或类似的东西)并绘制至少部分提交是个好主意。提交图以向后的方式形成(并因此绘制)。从某个分支上的最新提交开始,我们(或Git)必须读取每个提交,并将其绘制为一个节点(附加或不附加名称/ ID),并从每个提交到其父提交的向外箭头(s )。

大多数提交只有一个父级,正如我们刚才提到的,最近的提交是其ID存储在当前分支名称中的提交。由于Git中的所有内容都会倒退,因此最终看起来像:

git rebase

我们说名称... <- o <- o <- o <-- branch 指向分支上的最终提交。 (最终提交也有一个特殊的名称:它是提示提交。)

虽然这里的所有内部箭头都是向后的,但我们大多不需要关心它,所以对于非白板图纸,我只是将它们排除在外:

branch

当然,对于&#34; rebase&#34;这个分支,它必须分支一些东西。这意味着我们还有一个更多的主线分支:

...--o--o--o   <-- branch

我用...--o--*--o--...--o <-- mainline \ A--B--...--Z <-- branch 标记的一个提交是分支发散的点。提交*两个分支上,就像在主线上的所有早期(向左)提交一样。 *的权限仅在*(顶行提交)上或仅在mainline(底行)上。我给出了底行提交单字母名称而不是Git实际使用的大丑陋ID,因为丑陋的哈希不是人类友好的。当然,这只能让我在这里编写26个提交,而不是数百个,但这应该可以用于示例。

什么是rebase

现在,branch做的是复制提交。它:由于技术原因,几乎不可能改变任何现有提交的内容。当您运行git rebase时,您正在执行此操作以更改某些内容。通常,您要做的是根据图形更改提交的位置(这也会更改它们所基于的源树)。而不是上面的图表,你改变&#34; -i.e。,复制到新的提交 - 旧的提交,结果如下:

git rebase

新副本&#34;同样好&#34;作为原始,或者更确切地说,甚至更好 - 新的和改进的 - 因为它们在不同的点上,并从不同的源基础开始。它们重新基于

什么被复制,在哪里?

您有时可以只运行...--o--*--o--...--o <-- mainline \ \ \ A'-B'-...--Z' <-- branch (copies) \ A--B--...--Z [originals, now abandoned] ,但有时您必须为此运行git rebase。但究竟,git rebase mainline论证的确是什么呢? (就此而言,有时你怎么能把它留下来?)

那个论点 - mainline - 实际上是两个的东西。一个是非常明显的:它的副本。名称mainline指向mainline分支上的提示提交。我们希望副本能够在那之后进行,所以我们说&#34;重新加入主线&#34;。

mainline不太明显的事情就是告诉rebase 不要复制的内容

请记住,就在刚才,我们注意到提交mainline位于两个分支上(就像之前的所有提交一样)。这些是我们 想要复制的提交。我们希望rebase从 *之后的第一次提交开始,即提交* 1 ,然后继续{{}的提示1}},即提交A

你可以,如果你需要 - 有时你做 - 将它拆分,告诉branchZ添加副本的位置,这样剩下的参数可用于&#34;什么不是复制&#34;。在我们的特殊情况下,我们并不需要这样做。

1 请注意,在git rebase --onto之后还有另一个提交。或许,更好的说法是*mainline方向后的第一次提交。但是,Git使用的精确定义是由the gitrevisions two-dot range syntax提供的。

我们想要做的是&#34;复制到位&#34;

在我们的情况下,针对此特定问题,我们希望执行*选择/压缩/编辑事项,而不实际副本移动到新基础。也就是说,而不是:

branch

我们想要更像的东西:

git rebase -i

这样做的方法是命名,而不是...--o--*--o--...--o <-- mainline \ \ \ A'-B'-...--Z' <-- branch (copies) \ A--B--...--Z [originals, now abandoned] ,而是提交...--o--*--o--...--o <-- mainline |\ | AB--CDE--F'--...--XYZ <-- branch (copies, with squashing) \ A--B--...--Z [originals, now abandoned] 本身:

mainline

这意味着我们必须找到提交*的哈希ID。

这样做的一种方法是绘制图形(这是一个很好的练习,但它变得乏味)。另一个是让Git使用git rebase -i <hash-ID-of-commit-*> 为您绘制图形(您可能想要使用 DOG *,它以紧凑和友好的方式呈现输出,尾部 - 摇摆可选)。但是,一般情况下,您可以使用git log --graph找到ID:

--decorate --oneline --graph

这将打印两个分支上的第一个(即最近/最尖端病毒)提交的ID。 (事实上​​,这被称为合并基础也是一个线索:它是git merge-base如何运作的关键。但这完全是另一个答案。)< / p>

因此,你想做:

git merge-base mainline branch

然后在git merge命令之后复制并粘贴ID,以便rebase限制副本基础上的副本,然后紧接着它。由于与您正在复制的原始提交相同的基础,因此它们不会发生合并冲突(除非您重新组织某些提交以不同的顺序进行) ,无论如何)。

最后一分钟的注意事项和警告

关于git merge-base your-branch the-other-branch 需要注意的一点是不会复制合并提交。在一般情况下,它通常不会尝试。它确实有git rebase -i标志,但这实际上重新执行合并。但默认情况下,它只是完全放弃它们。这意味着,如果您有一个图形:

git rebase

并且您要求Git将--preserve-merges重新定位到...--o--*--o--o <-- mainline \ \ C--D \ / \ A--B G--H <-- branch \ / E--F ,新副本为branch(完全省略mainline)或A-B-C-D-E-F-H(仍然省略G 1}}完全,但首先做底行而不是第一行。这有点可能在执行第二行时产生合并冲突,无论以哪种方式完成第二行,或者在添加A-B-E-F-C-D-H时,尤其是在首先生成G时必须解决的合并冲突时

如果您运行H而没有额外参数 - 不说G或提供提交ID - 该命令会查看当前分支的上游设置(由git rebase设置。如果根本没有设置,mainline需要额外的参数。如果上游,它通常是一个远程跟踪分支,如git branch --set-upstream-to,并且rebase假装你输入 - 除了在Git 2.0中使用隐式上游打开 fork point 机器(这太复杂了,不能进入这里)。