git rebase 和 git merge --ff-only 之间有区别吗

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28140434/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 02:49:55  来源:igfitidea点击:

Is there a difference between git rebase and git merge --ff-only

gitgit-mergegit-rebase

提问by phoenix

From what I read, both of them help us get a linear history.

从我读到的内容来看,它们都帮助我们获得了线性历史。

From what I experimented, rebase works all the time. But merge --ff-only works only in scenarios where it can be fast forwarded.

根据我的实验,rebase 一直有效。但是merge --ff-only 只适用于可以快进的场景。

I also noticed, git merge creates a merge commit, but if we use --ff-only, it gives a linear history which essentially is equal to git rebasing. So --ff-only kills the purpose of git merge, right?

我还注意到,git merge 创建了一个合并提交,但是如果我们使用 --ff-only,它会给出一个线性历史,它基本上等于 git rebase。所以 --ff-only 扼杀了 git merge 的目的,对吧?

So what is the actual difference between them?

那么它们之间的实际区别是什么?

回答by torek

Note that git rebasehas a different jobthan git merge(with or without --ff-only). What rebasedoes is to take existing commits and copythem. Suppose, for instance, that you're on branch1and have made two commits Aand B:

请注意,与(有或没有)git rebase有不同的工作。什么所做的是利用现有的提交和复制它们。例如,假设您已开启并进行了两次提交,并且:git merge--ff-onlyrebasebranch1AB

...-o--o--A--B   <-- HEAD=branch1
        \
         o--C    <-- branch2

and you decide that you'd rather have those two commits be on branch2instead. You can:

并且你决定你宁愿让这两个提交发生branch2。你可以:

  • get a list of changes you made in A(diff Aagainst its parent)
  • get a list of changes you made in B(diff Bagainst A)
  • switch to branch2
  • make the same changes you made in Aand commit them, copying your commit message from A; let's call this commit A'
  • and then make the same changes you made in Band commit them, copying your commit message from B; let's call this B'.
  • 获取您所做的更改列表AA与其父项不同)
  • 获取您所做的更改列表BB针对A
  • 切换到 branch2
  • 进行与您所做的相同的更改A并提交它们,从 复制您的提交消息A;让我们称之为提交A'
  • 然后进行与您所做的相同的更改B并提交它们,复制您的提交消息B;让我们称之为B'

There's a git command that does this diff-and-then-copy-and-commit for you: git cherry-pick. So:

还有,这是否DIFF和-然后复制和提交给你个混蛋命令:git cherry-pick。所以:

git checkout branch2      # switch HEAD to branch2 (commit C)
git cherry-pick branch1^  # this copies A to A'
git cherry-pick branch1   # and this copies B

Now you have this:

现在你有这个:

...-o--o--A--B         <-- branch1
        \
         o--C--A'-B'   <-- HEAD=branch2

Now you can switch back to branch1and delete your original Aand B, using git reset(I'll use --hardhere, it's more convenient that way as it cleans up the work-tree too):

现在,您可以切换回branch1并删除原来的Aand B,使用git reset(我将--hard在这里使用,这样更方便,因为它也清理了工作树):

git checkout branch1
git reset --hard HEAD~2

This removes the original Aand B,1so now you have:

这将删除原来的Aand B, 1所以现在你有:

...-o--o               <-- HEAD=branch1
        \
         o--C--A'-B'   <-- branch2

Now you just need to re-check-out branch2to continue working there.

现在您只需要重新退房branch2即可继续在那里工作。

This is what git rebasedoes: it "moves" commits (though not by actually moving them, because it can't: in git, a commit can never be changed, so even just changing the parent-ID requires copying it to new and slightly different commit).

这就是git rebase它的作用:它“移动”提交(虽然不是通过实际移动它们,因为它不能:在 git 中,提交永远不能改变,所以即使只是改变父 ID 也需要将它复制到新的和略有不同的犯罪)。

In other words, while git cherry-pickis an automated diff-and-redo of onecommit, git rebaseis an automated process of redoing multiplecommits, plus, at the end, moving labels around to "forget" or hide-away the originals.

换句话说,虽然git cherry-pick一次提交的自动diff-and-redo ,git rebase是重做多次提交的自动化过程,另外,最后,移动标签以“忘记”或隐藏原始标签。

The above illustrates moving commits from one local branch branch1to another local branch branch2, but git uses the exact same processto move commits when you have a remote-tracking branch that acquires some new commits when you do a git fetch(including the fetchthat is the first step of git pull). You might start by working on branch feature, that has an upstream of origin/feature, and make a couple of commits of your own:

上面说明了将提交从一个本地分支移动branch1到另一个本地分支branch2,但是当您有一个远程跟踪分支时,git 使用完全相同的过程来移动提交,该分支在您执行 a 时获取一些新提交git fetch(包括fetch这是第一步git pull)。你可以先在 branch 上工作feature,它有一个上游origin/feature,然后自己做一些提交:

...-o        <-- origin/feature
     \
      A--B   <-- HEAD=feature

But then you decide you should see what has happened upstream, so you run git fetch,2and, aha, someone upstream wrote a commit C:

但你决定你应该看到发生了什么事上游,让你跑git fetch2和,啊哈,有人上游写了一个承诺C

...-o--C     <-- origin/feature
     \
      A--B   <-- HEAD=feature

At this point you can simply rebase your feature's Aand Bonto C, giving:

在这一点上,你可以简单地衍合featureA,并BC,赠送:

...-o--C     <-- origin/feature
        \
         A'-B'  <-- HEAD=feature

These are copies of your original Aand B, with the originals being thrown away (but see footnote 1) after the copies are complete.

这些是您的原件AB的副本,副本完成后原件将被扔掉(但请参阅脚注 1)。



Sometimes there's nothing to rebase, i.e., no work that you yourself did. That is, the graph before the fetchlook like this:

有时没有什么可以变基的,即没有你自己做过的工作。也就是说,之前的图形fetch看起来像这样:

...-o      <-- origin/feature
           `-- HEAD=feature

If you then git fetchand commit Ccomes in, though, you're left with yourfeaturebranch pointing to the old commit, while origin/featurehas moved forward:

但是,如果你然后git fetch提交C进来,你feature分支会指向旧的提交,而origin/feature已经向前移动:

...-o--C   <-- origin/feature
     `---- <-- HEAD=feature

This is where git merge --ff-onlycomes in: if you ask to merge your current branch featurewith origin/feature, git sees that it's possible to just slide the arrow forward, as it were, so that featurepoints directly to commit C. No actual merge is required.

这就是git merge --ff-only进来的地方:如果您要求将当前分支feature与合并origin/feature,git 会看到可以像以前一样向前滑动箭头,以便feature直接指向 commit C。不需要实际合并。

If you had your own commits Aand B, though, and you asked to merge those with C, git would do a real merge, making a new merge commit M:

如果你有自己的提交AB,虽然,你问合并那些C,混帐会做一个真正的合并,做一个新的合并提交M

...-o--C        <-- origin/feature
     \   `-_
      A--B--M   <-- feature

Here, --ff-onlywill stop and give you an error. Rebase, on the other hand, can copy Aand Bto A'and B'and then hide away the original Aand B.

在这里,--ff-only会停止并给你一个错误。底垫中,而另一方面,可以复制ABA'B',然后躲起来原来的AB

So, in short (ok, too late :-) ), they simply do different things. Sometimes the result is the same, and sometimes it's not. If it's OK to copy Aand B, you can use git rebase; but if there's some good reason notto copy them, you can use git merge, perhaps with --ff-only, to merge-or-fail as appropriate.

所以,简而言之(好吧,太晚了:-)),他们只是做不同的事情。有时结果是一样的,有时却不是。如果可以复制Aand B,则可以使用git rebase; 但如果有充分的理由复制它们,您可以使用git merge,也许与--ff-only,适当地合并或失败。



1Git actually keeps the originals for some time—normally a month in this case—but it hides them away. The easiest way to find them is with git's "reflogs", which keep a history of where each branch pointed, and where HEADpointed, before each change that updated the branch and/or HEAD.

1Git 实际上会保留原件一段时间——在这种情况下通常是一个月——但它会将它们隐藏起来。找到它们的最简单方法是使用 git 的“reflogs”,它HEAD在更新分支和/或HEAD.

Eventually the reflog history entries expire, at which point these commits become eligible for garbage collection.

最终 reflog 历史条目过期,此时这些提交有资格进行垃圾收集

2Or, again, you can use git pull, which is a convenience script that starts by running git fetch. Once the fetch is done, the convenience script runs either git mergeor git rebase, depending on how you configure and run it.

2或者,您可以再次使用git pull,这是一个从运行git fetch. 获取完成后,便利脚本将运行git mergegit rebase,具体取决于您如何配置和运行它。

回答by abligh

Yes, there is a difference. git merge --ff-onlywill abort if it cannot fast forward, and takes a commit (normally a branch) to merge in. It will only create a merge commit if it can't fast forward (i.e. will never do so with --ff-only).

是,有一点不同。git merge --ff-only如果它不能快进,它将中止,并接受一个提交(通常是一个分支)来合并。如果它不能快进,它只会创建一个合并提交(即永远不会这样做--ff-only)。

git rebaserewrites history on the current branch, or can be used to rebase an existing branch onto an existing branch. In that instance it won't create a merge commit because it's rebasing, rather than merging.

git rebase重写当前分支上的历史记录,或者可用于将现有分支变基到现有分支上。在这种情况下,它不会创建合并提交,因为它正在重新定位,而不是合并。

回答by abligh

Yes, --ff-onlywill always fail where a plain git mergewould fail, and might fail where a plain git mergewould succeed. That's the point - if you're trying to keep a linear history, and the merge can't be done that way, you wantit to fail.

是的,--ff-only在平原git merge会失败的地方总是会失败,而在平原git merge会成功的地方可能会失败。这就是重点 - 如果您试图保留线性历史记录,并且无法以这种方式进行合并,则您希望它失败。

An option that adds failure cases to a command isn't useless; it's a way of validating a precondition, so if the current state of the system isn't what you expect, you don't make the problem worse.

向命令添加失败案例的选项并非无用;这是验证先决条件的一种方式,因此如果系统的当前状态不是您所期望的,您不会使问题变得更糟。