git pull --rebase --preserve-merges

时间:2014-01-26 14:32:24

标签: git git-rebase

简短版本: 只有在执行本地提交后明确合并时,是否需要保留合并?究竟会发生什么?它是否将您提交的代码重新应用于合并的分支?

请说明git pull --rebase --preserve-merges与常规git pull --rebase何时有用? 我在这里阅读了git pull --rebase的问题: http://notes.envato.com/developers/rebasing-merge-commits-in-git/ 这可能会导致代码更改重复。

我在这里阅读:When will `git pull --rebase` get me in to trouble?

只有在你提交了一些提交后才基本上进行了修改,才会发生这种情况。

所以我不确定我何时需要git pull --rebase --preserve-merges以及使用git pull --rebase时是否真的很糟糕。

2 个答案:

答案 0 :(得分:44)

从技术上讲 - 我声称这有点愚蠢的git,pull脚本(它是一个shell脚本)应该只为你做这个 - 你必须运行git pull --rebase=preserve而不是试图使用git pull --rebase --preserve-merges。 (或者,正如我在a comment上的Vlad Nikitin's answer中所述,您可以将branch.name.rebase设置为preserve以自动获得相同的效果。)

换句话说,您从不运行git pull --rebase --preserve-merges,因为它(错误地)将--preserve-merges传递到fetch步骤,而不是{{1} } - 或 - merge步骤。但是,您可以运行rebase

何时(以及是否)使用任何类型的rebase,无论是否合并,更多的是意见问题。这意味着它首先在stackoverflow上表现不佳。 : - )

尽管如此,我还是会在这里提出一个主张:如果你知道(在某种意义上)你正在做什么, 1 以及你是否 知道你在做什么,你可能更喜欢合并保留的rebase作为一般规则,虽然当你决定变基是一个好主意时,你可能会发现有一个历史记录它自己的嵌入式分支和合并点不一定是正确的"最终重写的历史记录"。

也就是说,如果根本不适合进行变基,那么至少很可能要重新定位的历史本身是线性的,因此保留与展平的问题没有实际意义。反正。


编辑:添加图纸

这是提交图的一部分图,显示了两个命名分支git pull --rebase=preservemainlineexperimentmainline的公共基础是提交节点experimentA具有不在mainline分支上的提交G

experiment

请注意,...--o--A-------------G <-- mainline \ \ .-C-. B E--F <-- experiment \_D_/ 分支在其中也有分支和合并:这两个分支的基础是experiment,一个分支包含提交B,以及其他分支持有提交C。这两个(未命名的)分支在合并提交D缩减回单个开发线程,然后提交E位于合并提交的顶部,并且是分支F的提示。

如果你在experiment并且正在运行experiment,会发生什么:

git rebase mainline

这里是提交图中现在的内容:

$ git rebase mainline
First, rewinding head to replay your work on top of it...
Applying: B
Applying: C
Applying: D
Applying: F

&#34;结构分支&#34;以前在分支...--o--A--G <-- mainline \ B'-C'-D'-F' <-- experiment 上的那个已经消失了。 experiment操作复制了我在提交rebaseBCD中生成的所有更改 ;这些已成为新提交FB'C'D'。 (提交F'是一个纯粹的合并,没有任何更改,也不需要复制。我没有测试如果我使用嵌入式更改来修改合并会发生什么,要么解决冲突,要么就像有人称之为“#34” ;邪恶的合并&#34;。)

另一方面,如果我这样做:

E

我得到了这张图:

$ git rebase --preserve-merges mainline
[git grinds away doing the rebase; this takes a bit longer
than the "flattening" rebase, and there is a progress indicator]
Successfully rebased and updated refs/heads/experiment.

这保留了合并,因此保留了...--o--A--G <-- mainline \ \ .-C'. B' E'-F' <-- experiment \_D'/ 的内部分支&#34;。这样好吗?坏?冷漠?阅读(很长)脚注!


1 学习&#34;什么是rebase&#34;这是一个好主意。无论如何,在git中(唉!)几乎需要学习&#34;它是如何做到的#34;同样,至少在中等水平上。基本上,rebase会生成(之前的更改)提交的副本,然后您可以将这些提交应用于(您或其他人)以后的提交,使其成为&#34;似乎&#34; ;你以其他顺序完成了工作。一个简单的例子:两位开发人员,让我们说Alice和Bob都在同一个分支上工作。让我们说营销已经要求一个代号为Strawberry的功能,而且Alice和Bob都正在做一些工作来实现experiment,两者都在一个名为strawberry的分支上。

Alice和Bob都运行strawberrygit fetch开始strawberry

Alice发现文件origin需要进行一些更改才能为新功能做准备。她写道并提交,但尚未推出。

Bob编写了新功能的描述,它更改了文件abc,但没有其他效果。鲍勃承诺改变并推动。

Alice然后更新文件README以提供实际功能。她(并单独)编写并提交,现在已经准备好了。但是,哦不,鲍勃打败了她:

feat
然后,Alice应该获取更改并查看它们(而不仅仅是盲目合并或变基):

$ git push origin strawberry
...
! [rejected]        strawberry -> strawberry (non-fast-forward)

(或使用$ git fetch ... $ git log origin/strawberry 或其他 - 我倾向于自己使用git lola,如果需要,我会gitk个人提交。

她可以从中看到Bob只更改了git show,因此她的更改无论如何都不会受到影响。在这一点上,她可以说将她的更改变为README是安全的:

origin/strawberry

(请注意,没有要保留的合并),这使得看起来(就git历史而言)就像她第一次等待Bob更新文档一样,然后才真正开始实施更改 - 仍然分为两个单独的提交,以便以后很容易判断文件$ git rebase origin/strawberry 的更改是否打破了其他任何内容。但是,这两个单独的提交现在是相邻的,因此稍后可以很容易地告诉abc更改的是否允许更改为文件{{ 1}}。而且,由于abc的更改首先出现,因此更明确的是这是feat更改的重点。并不是说即使爱丽丝这么做也很难说:

README

相反,虽然这会创建一个合并提交,其唯一的一点似乎就是&#34; Alice在abc开始更新$ git merge origin/strawberry 之前开始进入abc并在&之后完成README #34;,这不是真的有用。

在更复杂的情况下,Bob所做的不仅仅是更新文档,Alice可能会发现最好将自己的提交(在这种情况下可能超过两个)重新排列为一个新的,不同的线性历史记录,所以鲍勃的一些变化(这次,可能不止一次提交)在中间&#34;例如,就好像他们实时合作一样(谁知道,也许他们这样做了)。或者她可能会发现,将她的变化作为一个单独的开发线来更好,这个开发线可以与Bob的变化合并,甚至可能不止一次。

所有这一切都是为了向某人提供最有用的信息 - 可能是Alice和Bob,可能是其他开发人员 - 将来,如果有必要回去看看(明显的,如果是重新的,或者实际的,如果不是)事件序列。有时每次提交都是有用的信息。有时,重新排列和组合提交或完全删除一些提交会更有用:例如,变更被证明是个坏主意。 (但考虑让他们只是为了指出&#34;这是一个坏主意,所以不要再试一次&#34;以及!)

答案 1 :(得分:-2)

git pull --rebasegit fetch相同,然后是git rebase,因此git pull --rebase --preserve-mergesgit fetch相同,然后git rebase --preserve-merges。您可以在此处git rebase --preserve-merges

获得有关{{1}}的良好答案