git pull --rebase --preserve-merges [英] git pull --rebase --preserve-merges

查看:716
本文介绍了git pull --rebase --preserve-merges的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



请解释何时它对有用与常规 git pull --rebase
我在这里阅读了有关 git pull --rebase 的问题:
http://notes.envato.com/developers/rebasing-merge-commits-in-git/
那可能会导致代码更改被复制。



我在这里阅读:什么时候`git pull --rebase`让我陷入困境?

它只会发生,如果你基本上rebase一些提交后推。



所以我不知道我明白当我需要 git pull - -rebase --preserve-merges ,并且如果它使用糟糕的话< git pull --rebase


技术上 - 我声称这是一个愚蠢的git, pull 脚本(这是一个shell脚本)应该为你做这个 - 你必须运行 git pull --reba se = preserve 而不是尝试使用 git pull --rebase --preserve-merges 。 (或者,正如我在关于 Vik Nikitin的答案,你可以设置分支。 name .rebase 保留自动获得相同的效果。)



换句话说,你应该永远不会运行 git pull --rebase --preserve-merges 因为它(错误地)传递 - preserve-merges fetch 步骤,而不是到 merge - 或 - rebase code>步骤。但是,您可以运行 git pull --rebase = preserve



什么时候(以及是否)使用任何形式的重新组合,无论是否合并保存,都是更重要的问题。这意味着它首先在stackoverflow上确实不太好。 : - )

不过,我会在这里提出一个主张:如果你知道你在做什么, > 1 ,如果你知道你在做什么,那么你可能更喜欢保留合并的基本规则,尽管在你决定重新分配是一个好的时候的想法,你可能会发现,有自己的嵌入分支和合并点的历史不一定是正确的最终重写的历史。



也就是说,如果根本不适合进行rebase,那么至少很有可能重新发布的历史本身就是线性的,所以保存vs平坦的问题无论如何都是无稽之谈。






编辑:添加绘图



以下是提交图的一部分,其中显示了两个命名分支主线实验 mainline experiment 的公共基础是提交节点 A ,并且 mainline 具有不在实验 G c>分支:

  ...-- o  -  A ------------- G<  - 主线
\
\。-C-。
B E - F < - experiment
\_D_ /

注意,实验分支也有一个分支和合并,但是:这两个分支的基础是 B ,一个分支保存提交 C ,另一个分支保存提交 D 。这两个(未命名的)分支在合并提交 E 时缩回到单个开发线程,然后提交 F 坐在合并提交之上,并且是分支实验的提示。



如果您使用试验并运行 git rebase mainline

 <$ 
申请:B
申请:C
申请:D
应用:F

以下是提交图中的内容:

  ...  -  o  -  A  -  G < - 主线
\
B'-C'- D'-F'< - experiment

曾经存在的结构分支分支实验不见了。 rebase 操作复制了我在提交 B 时发生的所有更改< c $ c> C , D F ;这些成为新的提交 B' C' D' F'。 (Commit E 是一个纯粹的合并,没有变化,也不需要复制。我没有测试过如果我通过嵌入式更改重新合并合并来解决冲突, )

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



pre> $ git rebase --preserve-merges mainline
[git grinds away doing the rebase;这需要花费比$扁平化基准稍长的
,并且有一个进度指标]
成功地重新设计和更新了参考文献/标题/实验。

我得到这个图:

  ...  -  o  -  A  -  G < - 主线
\
\。-C'。
B'E'-F'< - 实验
\_D'/

这保存了 experiment 的合并,并因此保留了内部分支。这很好吗?坏?冷漠?阅读(非常长)脚注!






1 学习 rebase会无论如何,在git中(唉!)几乎需要学习它是如何做到的,至少在中等水平上。基本上来说,rebase会对(之前的更改)副本进行提交,然后将其应用于(您或他人的)稍后提交,使其看起来像您以其他顺序。一个简单的例子:两个开发人员,比如Alice和Bob,他们都在同一个分支上工作。假设Marketing要求代号为Strawberry的功能,并且Alice和Bob都在执行草莓的一些工作,它们都位于名为草莓。



Alice和Bob都运行 git fetch 来将草莓超过原产地



Alice发现该文件 abc 需要进行一些更改以准备新功能。她编写并提交,但并未推送。



Bob写了新功能的描述,它改变文件 README ,但没有其他作用。 Bob提交他的更改并推送。



Alice然后更新文件 feat 以提供实际功能。她写和承诺(分开),现在准备推动。但是,哦,不,鲍勃击败了她:$ b​​
$ b

  $ git push origin草莓
...
! [拒绝]草莓 - >草莓(非快进)

然后Alice应该获取更改并查看它们
$ b

  $ git fetch 
... $ b $(不只是盲目地合并或重新定义):

b $ git log origin /草莓

(或使用 gitk 或其他 - 我倾向于使用 git lola 我自己和 git show 个人提交,如果/根据需要)。



她可以看出,Bob只改变了 README ,所以她的改变绝对不会受到任何影响。在这一点上,她可以告诉将她的变更重新分配到 origin / strawberry 是安全的:

  $ git rebase origin /草莓

(请注意,没有合并来保存),这使得它看起来像她第一次等待Bob更新文档,然后才真正开始实现这些更改,这些更改仍然分为两个单独的提交,稍后很容易判断文件 abc 的更改是否破坏了其他任何内容。然而,这两个单独的提交现在是相邻的,所以稍后很容易告诉,对 abc 进行更改的是为了启用更改为文件 feat 。而且,由于对 README 的更改是第一位的,所以更清楚的是这是对 abc 的更改点。 。并不是说即使爱丽丝只是做了这件事情也很难说:

$ $ $ $ git merge origin /草莓

code>

来代替,尽管这会创建一个合并提交,其唯一的一点似乎是Alice开始在在完成 README 的更新并在<之后完成 feat 之后abc ,这不是真的

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



这都是关于什么的问题在将来有必要返回并查看(明显的,如果是重新发布的,或者如果不是的话)序列,将会向某人(可能是Alice和Bob,可能还有其他开发人员)提供最有用的信息,的事件。有时候每个单独提交都是有用的信息有时候重新整理和合并提交,或者完全放弃一些提交更有用:例如,证明是不好主意的更改。 (但考虑让他们只是为了指出这是一个坏主意,所以不要在将来再试一次)。

Short version: Do you need to preserve-merges only if you explicitly merged after you did a local commit? What exactly happens otherwise? Does it reapply your committed code to the merged branch?

Please explain when it is useful to git pull --rebase --preserve-merges vs. a regular git pull --rebase? I read about an issue with git pull --rebase here: http://notes.envato.com/developers/rebasing-merge-commits-in-git/ That could cause code changes to be duplicated.

I read here: When will `git pull --rebase` get me in to trouble?

That it only happens if you basically rebase after some the commits have been pushed.

So I am not sure I understand when I would need git pull --rebase --preserve-merges and if it's ever bad to use vs. git pull --rebase.

解决方案

Technically—and I claim this is a bit stupid of git, the pull script (it's a shell script) should just do this for you—you have to run git pull --rebase=preserve rather than attempting to use git pull --rebase --preserve-merges. (Or, as I noted in a comment on Vlad Nikitin's answer, you can set branch.name.rebase to preserve to get the same effect automatically.)

In other words, you should never run git pull --rebase --preserve-merges as it (incorrectly) passes --preserve-merges to the fetch step, instead of to the merge-or-rebase step. However, you can run git pull --rebase=preserve.

The question of when (and whether) to use any kind of rebase, whether merge-preserving or not, is more a matter of opinion. Which means it really does not go well on stackoverflow in the first place. :-)

Still, I'll make one claim here: you should only rebase if you know (in a sort of general sense) what you are doing,1 and if you do know what you are doing, you would probably prefer a merge-preserving rebase as a general rule, although by the time you've decided that rebasing is a good idea, you will probably find that a history that has its own embedded branch-and-merge-points is not necessarily the correct "final rewritten history".

That is, if it's appropriate to do a rebase at all, it's at least fairly likely that the history to be rebased is itself linear, so that the preserve-vs-flatten question is moot anyway.


Edit: add drawing

Here's a drawing of part of a commit graph, showing two named branches, mainline and experiment. The common base for mainline and experiment is commit node A, and mainline has a commit G that is not on the experiment branch:

...--o--A-------------G   <-- mainline
         \
          \ .-C-.
           B     E--F     <-- experiment
            \_D_/

Note that the experiment branch has a branch-and-merge within it too, though: the base for these two branches is B, one branch holds commit C, and the other branch holds commit D. These two (unnamed) branches shrink back to a single thread of development at merge commit E, and then commit F sits atop the merge commit and is the tip of branch experiment.

Here's what happens if you are on experiment and run 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

Here's what is now in the commit graph:

...--o--A--G               <-- mainline
            \
             B'-C'-D'-F'   <-- experiment

The "structural branch" that used to be there on branch experiment is gone. The rebase operation copied all the changes I'd made in commits B, C, D, and F; these became the new commits B', C', D', and F'. (Commit E was a pure merge with no changes and did not require copying. I have not tested what happens if I rebase a merge with embedded changes, either to resolve conflicts or, as some call it, an "evil merge".)

On the other hand, if I do this:

$ 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.

I get this graph instead:

...--o--A--G               <-- mainline
            \
             \ .-C'.
              B'    E'-F'  <-- experiment
               \_D'/

This has preserved the merge, and hence the "internal branchiness", of experiment. Is that good? Bad? Indifferent? Read the (very long) footnote!


1It's a good idea to learn "what rebase does" anyway, which in git (alas!) pretty much requires learning "how it does it" as well, at least on a medium-level. Basically, rebase makes copies of (the changes from your earlier) commits, which you then apply to (your or someone else's) later commits, making it "seem like" you did the work in some other order. A simple example: two developers, let's say Alice and Bob, are both working on the same branch. Let's say that Marketing has asked for a feature code-named Strawberry, and both Alice and Bob are doing some work to implement strawberry, both on a branch named strawberry.

Alice and Bob both run git fetch to bring strawberry over from origin.

Alice discovers that file abc needs some change to prepare for the new feature. She writes that and commits, but does not push yet.

Bob writes a description of the new feature, that changes file README, but has no other effect. Bob commits his change and pushes.

Alice then updates file feat to provide the actual feature. She writes and commits (separately) that, and is now ready to push. But, oh no, Bob beat her to it:

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

Alice should then fetch the changes and look at them (not just blindly merge or rebase):

$ git fetch
...
$ git log origin/strawberry

(or using gitk or whatever—I tend to use git lola myself, and git show individual commits if/as needed).

She can see from this that Bob only changed the README, so her changes are definitely not affected either way. At this point, she can tell that it's safe to rebase her changes onto origin/strawberry:

$ git rebase origin/strawberry

(note that there are no merges to preserve), which makes it look (in terms of git history) like she first waited for Bob to update the documentation, and only then actually started to implement the changes—which are still split into two separate commits so that it's easy to tell, later, whether the change to file abc broke anything else. Those two separate commits are now adjacent, though, so it's easy to tell, later, that the point of the change to abc was to enable the change to file feat. And since the change to README comes first, it's even more clear that the this was the point of the change to abc. Not that it would be hard to tell even if Alice just did:

$ git merge origin/strawberry

instead, although that creates a merge commit whose only point seems to be to say "Alice started in on abc before Bob finished updating README, and finished feat after", which is not really helpful.

In more complex cases, where Bob did more than just update the documentation, Alice might find that it's best to rearrange her own commits (probably more than two in this case) into a new, different linear history, so that some of Bob's changes (this time, probably more than one commit) are "in the middle", for instance, as if they had co-operated in real time (and who knows, maybe they did). Or she might find that it's better to keep her changes as a separate development line that merges, perhaps even more than once, with Bob's changes.

It's all a matter of what will provide the most useful information to someone(s)—possibly Alice and Bob, possibly other developers—in the future, if and when it becomes necessary to go back and look at the (apparent, if rebased, or actual if not) sequence of events. Sometimes each individual commit is useful information. Sometimes it's more useful to rearrange and combine commits, or drop some commits entirely: for instance, changes that proved to be a bad idea. (But consider leaving them in just for the value of pointing out "this was a bad idea so don't try it again in the future" as well!)

这篇关于git pull --rebase --preserve-merges的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆