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
Is there a difference between git rebase and git merge --ff-only
提问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 rebase
has a different jobthan git merge
(with or without --ff-only
). What rebase
does is to take existing commits and copythem. Suppose, for instance, that you're on branch1
and have made two commits A
and B
:
请注意,与(有或没有)git rebase
有不同的工作。什么所做的是利用现有的提交和复制它们。例如,假设您已开启并进行了两次提交,并且:git merge
--ff-only
rebase
branch1
A
B
...-o--o--A--B <-- HEAD=branch1
\
o--C <-- branch2
and you decide that you'd rather have those two commits be on branch2
instead. You can:
并且你决定你宁愿让这两个提交发生branch2
。你可以:
- get a list of changes you made in
A
(diffA
against its parent) - get a list of changes you made in
B
(diffB
againstA
) - switch to
branch2
- make the same changes you made in
A
and commit them, copying your commit message fromA
; let's call this commitA'
- and then make the same changes you made in
B
and commit them, copying your commit message fromB
; let's call thisB'
.
- 获取您所做的更改列表
A
(A
与其父项不同) - 获取您所做的更改列表
B
(B
针对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 branch1
and delete your original A
and B
, using git reset
(I'll use --hard
here, it's more convenient that way as it cleans up the work-tree too):
现在,您可以切换回branch1
并删除原来的A
and B
,使用git reset
(我将--hard
在这里使用,这样更方便,因为它也清理了工作树):
git checkout branch1
git reset --hard HEAD~2
This removes the original A
and B
,1so now you have:
这将删除原来的A
and B
, 1所以现在你有:
...-o--o <-- HEAD=branch1
\
o--C--A'-B' <-- branch2
Now you just need to re-check-out branch2
to continue working there.
现在您只需要重新退房branch2
即可继续在那里工作。
This is what git rebase
does: 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-pick
is an automated diff-and-redo of onecommit, git rebase
is 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 branch1
to 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 fetch
that 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 fetch
,2和,啊哈,有人上游写了一个承诺C
:
...-o--C <-- origin/feature
\
A--B <-- HEAD=feature
At this point you can simply rebase your feature
's A
and B
onto C
, giving:
在这一点上,你可以简单地衍合feature
的A
,并B
到C
,赠送:
...-o--C <-- origin/feature
\
A'-B' <-- HEAD=feature
These are copies of your original A
and B
, with the originals being thrown away (but see footnote 1) after the copies are complete.
这些是您的原件A
和B
的副本,副本完成后原件将被扔掉(但请参阅脚注 1)。
Sometimes there's nothing to rebase, i.e., no work that you yourself did. That is, the graph before the fetch
look like this:
有时没有什么可以变基的,即没有你自己做过的工作。也就是说,之前的图形fetch
看起来像这样:
...-o <-- origin/feature
`-- HEAD=feature
If you then git fetch
and commit C
comes in, though, you're left with yourfeature
branch pointing to the old commit, while origin/feature
has moved forward:
但是,如果你然后git fetch
提交C
进来,你的feature
分支会指向旧的提交,而origin/feature
已经向前移动:
...-o--C <-- origin/feature
`---- <-- HEAD=feature
This is where git merge --ff-only
comes in: if you ask to merge your current branch feature
with origin/feature
, git sees that it's possible to just slide the arrow forward, as it were, so that feature
points 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 A
and B
, though, and you asked to merge those with C
, git would do a real merge, making a new merge commit M
:
如果你有自己的提交A
和B
,虽然,你问合并那些C
,混帐会做一个真正的合并,做一个新的合并提交M
:
...-o--C <-- origin/feature
\ `-_
A--B--M <-- feature
Here, --ff-only
will stop and give you an error. Rebase, on the other hand, can copy A
and B
to A'
and B'
and then hide away the original A
and B
.
在这里,--ff-only
会停止并给你一个错误。底垫中,而另一方面,可以复制A
和B
以A'
和B'
,然后躲起来原来的A
和B
。
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 A
and 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.
所以,简而言之(好吧,太晚了:-)),他们只是做不同的事情。有时结果是一样的,有时却不是。如果可以复制A
and 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 HEAD
pointed, 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 merge
or git rebase
, depending on how you configure and run it.
2或者,您可以再次使用git pull
,这是一个从运行git fetch
. 获取完成后,便利脚本将运行git merge
或git rebase
,具体取决于您如何配置和运行它。
回答by abligh
Yes, there is a difference. git merge --ff-only
will 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 rebase
rewrites 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-only
will always fail where a plain git merge
would fail, and might fail where a plain git merge
would 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.
向命令添加失败案例的选项并非无用;这是验证先决条件的一种方式,因此如果系统的当前状态不是您所期望的,您不会使问题变得更糟。