我无法理解 git rebase --onto 的行为
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29914052/
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
I can't understand the behaviour of git rebase --onto
提问by Xmanoux
I have noticed that the two blocks of following git commands have different behaviours and I don't understand why.
我注意到以下 git 命令的两个块具有不同的行为,我不明白为什么。
I have a A
and a B
branch that diverge with one commit
我有一个分支A
和一个B
分支commit
---COMMIT--- (A)
\
--- (B)
I want to rebase B
branch on the lastest A
(and have the commit on the B
branch)
我想B
在最新的分支上重新设置分支A
(并在分支上提交B
)
---COMMIT--- (A)
\
--- (B)
No problem if I do:
如果我这样做没问题:
checkout B
rebase A
But if I do:
但如果我这样做:
checkout B
rebase --onto B A
It doesn't work at all, nothing happens. I don't understand why the two behaviours are different.
它根本不起作用,什么也没有发生。我不明白为什么这两种行为不同。
Phpstorm git client use the second syntax, and so seems to me completely broken, that's why I ask for this syntax issue.
Phpstorm git 客户端使用第二种语法,所以在我看来完全坏了,这就是我问这个语法问题的原因。
回答by Enrico Campidoglio
tl;dr
tl;博士
The correct syntax to rebase B
on top of A
using git rebase --onto
in your case is:
正确的语法变基B
之上A
使用git rebase --onto
你的情况是:
git checkout B
git rebase --onto A B^
or rebase B
on top of A
starting from the commit that is the parent of B
referenced with B^
or B~1
.
或在从作为引用的父级的提交开始的基础上变基B
A
B
B^
或B~1
。
If you're interested in the difference between git rebase <branch>
and git rebase --onto <branch>
read on.
如果您对git rebase <branch>
和之间的区别感兴趣,请继续git rebase --onto <branch>
阅读。
The Quick: git rebase
快速:git rebase
git rebase <branch>
is going to rebase the branch you currently have checked out, referenced by HEAD
, on top of the latest commit that is reachablefrom <branch>
but notfrom HEAD
.
This is the most common case of rebasing and arguably the one that requires less planning up front.
git rebase <branch>
将在可从但不能从访问HEAD
的最新提交之上,对您当前已检出的分支进行重新定位,由 引用。
这是最常见的变基情况,可以说是需要较少预先计划的情况。<branch>
HEAD
Before After
A---B---C---F---G (branch) A---B---C---F---G (branch)
\ \
D---E (HEAD) D---E (HEAD)
In this example, F
and G
are commits that are reachable from branch
but not from HEAD
. Saying git rebase branch
will take D
, that is the first commit after the branching point, and rebase it(i.e. change its parent) on top of the latest commit reachable from branch
but not from HEAD
, that is G
.
在这个例子中,F
和G
是可从branch
但不能从访问的提交HEAD
。说git rebase branch
将采取D
,这是分支点之后的第一次提交,并将其变基(即更改其父项)基于可从branch
但不能从到达的最新提交HEAD
,即G
。
The Precise: git rebase --onto with 2 arguments
精确:带有 2 个参数的 git rebase --onto
git rebase --onto
allows you to rebase starting from a specific commit. It grants you exact control over what is being rebased and where. This is for scenarios where you need to be precise.
git rebase --onto
允许您从特定提交开始变基。它使您可以精确控制正在重新定位的内容和位置。这适用于需要精确的场景。
For example, let's imagine that we need to rebase HEAD
precisely on top of F
starting from E
. We're only interested in bringing F
into our working branch while, at the same time, we don't want to keep D
because it contains some incompatible changes.
例如,让我们想象一下,我们需要HEAD
在F
从E
. 我们只对F
引入我们的工作分支感兴趣,同时我们不想保留,D
因为它包含一些不兼容的更改。
Before After
A---B---C---F---G (branch) A---B---C---F---G (branch)
\ \
D---E---H---I (HEAD) E---H---I (HEAD)
In this case, we would say git rebase --onto F D
. This means:
在这种情况下,我们会说git rebase --onto F D
。这意味着:
Rebase the commit reachable from
HEAD
whose parent isD
on top ofF
.
重新确定可从
HEAD
其父级D
在F
.
In other words, change the parentof E
from D
to F
. The syntax of git rebase --onto
is then git rebase --onto <newparent> <oldparent>
.
换句话说,改变父母的E
,从D
到F
。的语法git rebase --onto
是 then git rebase --onto <newparent> <oldparent>
。
Another scenario where this comes in handy is when you want to quickly remove some commits from the current branch without having to do an interactive rebase:
另一个派上用场的情况是,当您想快速从当前分支中删除一些提交而无需进行交互式 rebase 时:
Before After
A---B---C---E---F (HEAD) A---B---F (HEAD)
In this example, in order to remove C
and E
from the sequence you would say git rebase --onto B E
, or rebase HEAD
on top of B
where the old parent was E
.
在这个例子中,为了去除C
与E
来自顺序,你会说git rebase --onto B E
,或重订HEAD
之上B
的老父母在何处E
。
The Surgeon: git rebase --onto with 3 arguments
外科医生: git rebase --onto 带 3 个参数
git rebase --onto
can go one step further in terms of precision. In fact, it allows you to rebase an arbitrary range of commitson top of another one.
git rebase --onto
在精度方面可以更进一步。事实上,它允许您将任意范围的提交重新绑定到另一个提交之上。
Here's an example:
下面是一个例子:
Before After
A---B---C---F---G (branch) A---B---C---F---G (branch)
\ \
D---E---H---I (HEAD) E---H (HEAD)
In this case, we want to rebase the exact range E---H
on top of F
, ignoring where HEAD
is currently pointing to. We can do that by saying git rebase --onto F D H
, which means:
在这种情况下,我们希望E---H
在 之上重新确定确切范围F
,忽略HEAD
当前指向的位置。我们可以通过说 来做到这一点git rebase --onto F D H
,这意味着:
Rebase the range of commits whose parent is
D
up toH
on top ofF
.
将父级
D
最高的提交范围重新H
设置为F
.
The syntax of git rebase --onto
with a range of commitsthen becomes git rebase --onto <newparent> <oldparent> <until>
. The trick here is remembering that the commit referenced by <until>
is includedin the range and will become the new HEAD
after the rebase is complete.
git rebase --onto
with a range of commits的语法就变成了git rebase --onto <newparent> <oldparent> <until>
. 这里的技巧是要记住的是,提交通过引用<until>
被包含在范围,将成为新的HEAD
底垫完成后。
回答by sEver
This is all you need to know to understand --onto
:
这是您需要了解的全部内容--onto
:
git rebase --onto <newparent> <oldparent>
You're switching a parent on a commit, but you're not providing the sha of the commit, only the sha of it's current (old) parent.
您在提交时切换父项,但没有提供提交的 sha,仅提供其当前(旧)父项的 sha。
回答by WloHu
Put shortly, given:
简而言之,给定:
Before rebase After rebase
A---B---C---F---G (branch) A---B---C---F---G (branch)
\ \ \
D---E---H---I (HEAD) \ E'---H' (HEAD)
\
D---E---H---I
git rebase --onto F D H
Which is the same as (because --onto
takes one argument):
这与(因为--onto
需要一个参数)相同:
git rebase D H --onto F
Means rebase commits in range (D, H] on top of F. Notice the range is left-hand exclusive.It's exclusive because it's easier to specify 1st commit by typing e.g. branch
to let git
find the 1st diverged commit from branch
i.e. D
which leads to H
.
表示在 F 之上的范围 (D, H] 内的 rebase 提交。请注意,该范围是左手独占的。它是独占的,因为通过键入 egbranch
来指定第 1 次提交更容易让我们git
找到与branch
ie不同的第 1 次提交,D
这导致H
.
OP case
操作案例
o---o (A)
\
o (B)(HEAD)
git checkout B
git rebase --onto B A
Can be changed to single command:
可以改为单个命令:
git rebase --onto B A B
What looks like error here is placement of B
which means "move some commits which lead to branch B
on top of B
". The questions is what "some commits" are. If you add -i
flag you will see it is single commit pointed by HEAD
. The commit is skipped because it is already applied to --onto
target B
and so nothing happens.
什么样子的错误这里是放置的B
意思是“将一些提交从而导致分支B
之上B
”。问题是“一些提交”是什么。如果添加-i
标志,您将看到它是由HEAD
. 提交被跳过,因为它已经应用于--onto
目标B
,所以没有任何反应。
The command is nonsense in any case where branch name is repeated like that. This is because the range of commits will be some commits which are already in that branch and during rebase all of them will be skipped.
在分支名称像这样重复的任何情况下,该命令都是无意义的。这是因为提交的范围将是一些已经在该分支中的提交,并且在变基期间所有这些提交都将被跳过。
Further explanation and applicable usage of git rebase <upstream> <branch> --onto <newbase>
.
的进一步解释和适用用法git rebase <upstream> <branch> --onto <newbase>
。
git rebase
defaults.
git rebase
默认值。
git rebase master
Expands to either :
扩展为:
git rebase --onto master master HEAD
git rebase --onto master master current_branch
Automatic checkout after rebase.
变基后自动结帐。
When used in standard way, like:
当以标准方式使用时,例如:
git checkout branch
git rebase master
You won't notice that after rebase git
moves branch
to most recently rebased commit and does git checkout branch
(see git reflog
history). What is interesting when 2nd argument is commit hashinstead branch name rebase still works but there is no branch to move so you end up in "detached HEAD" instead being checked out to moved branch.
你不会注意到在 rebasegit
移动branch
到最近 rebase 提交之后git checkout branch
(见git reflog
历史)。当第二个参数是提交哈希而不是分支名称 rebase时,有趣的是没有分支可以移动,因此您最终会进入“分离的 HEAD”,而不是被检出到移动的分支。
Omit primary diverged commits.
省略主要的分歧提交。
The master
in --onto
is taken from 1st git rebase
argument.
将master
在--onto
从第一个拍摄git rebase
参数。
git rebase master
/ \
git rebase --onto master master
So practicaly it can be any other commit or branch. This way you can limit number of rebase commits by taking the latest ones and leaving primary diverged commits.
如此实用,它可以是任何其他提交或分支。通过这种方式,您可以通过获取最新提交并保留主要分歧提交来限制 rebase 提交的数量。
git rebase --onto master HEAD~
git rebase --onto master HEAD~ HEAD # Expanded.
Will rebase single commit pointed by HEAD
to master
and end up in "detached HEAD".
将重订单提交尖HEAD
到master
和“分离的头”而告终。
Avoid explicit checkouts.
避免显式结帐。
The default HEAD
or current_branch
argument is contextually taken from place you're in. This is why most people checkout to branch which they want to rebase. But when 2nd rebase argument is given explicitly you don't have to checkout before rebase to pass it in implicit way.
默认值HEAD
或current_branch
参数是从您所在的位置上下文中获取的。这就是为什么大多数人结帐到他们想要变基的分支的原因。但是当明确给出第二个 rebase 参数时,您不必在 rebase 之前签出以隐式方式传递它。
(branch) $ git rebase master
(branch) $ git rebase master branch # Expanded.
(branch) $ git rebase master $(git rev-parse --abbrev-ref HEAD) # Kind of what git does.
This means you can rebase commits and branches from anyplace. So together with Automatic checkout after rebase.you don't have to separately checkout rebased branch before or after rebase.
这意味着您可以从任何地方重新设置提交和分支。因此,与变基后的自动结帐一起使用。您不必在 rebase 之前或之后单独检出 rebase 分支。
(master) $ git rebase master branch
(branch) $ # Rebased. Notice checkout.
回答by Gauthier
Simply put, git rebase --onto
selects a range of commits and rebases them on the commit given as parameter.
简而言之,git rebase --onto
选择一系列提交并根据作为参数给出的提交将它们变基。
Read the man pages for git rebase
, search for "onto". The examples are very good:
阅读 的手册页git rebase
,搜索“onto”。这些例子非常好:
example of --onto option is to rebase part of a branch. If we have the following situation:
H---I---J topicB
/
E---F---G topicA
/
A---B---C---D master
then the command
git rebase --onto master topicA topicB
would result in:
H'--I'--J' topicB
/
| E---F---G topicA
|/
A---B---C---D master
In this case you tell git to rebase the commits from topicA
to topicB
on top of master
.
在这种情况下,您告诉 git 将提交从topicA
到 重新设置为topicB
基于master
.
回答by womanonrails
To better understand difference between git rebase
and git rebase --onto
it is good to know what are the possible behaviors for both commands. git rebase
allow us to move our commits on top of the selected branch. Like here:
为了更好地理解之间的区别git rebase
和git rebase --onto
这是好事,知道什么是两个命令的可能行为。git rebase
允许我们将提交移动到所选分支的顶部。像这儿:
git rebase master
and the result is:
结果是:
Before After
A---B---C---F---G (master) A---B---C---F---G (master)
\ \
D---E (HEAD next-feature) D'---E' (HEAD next-feature)
git rebase --onto
is more precises. It allows us to choose specific commit where we want to start and also where we want to finish. Like here:
git rebase --onto
更精确。它允许我们选择我们想要开始和结束的特定提交。像这儿:
git rebase --onto F D
and the result is:
结果是:
Before After
A---B---C---F---G (branch) A---B---C---F---G (branch)
\ \
D---E---H---I (HEAD my-branch) E'---H'---I' (HEAD my-branch)
To get more details I recommend you to check out my own article about git rebase --onto overview
要获得更多详细信息,我建议您查看我自己关于git rebase --onto 概述的文章
回答by Michael Mairegger
For onto
you need two additional branches. With that command you can apply commits from branchB
that are based on branchA
onto another branch e.g. master
. In the sample below branchB
is based on branchA
and you want to apply the changes of branchB
on master
without applying the changes of branchA
.
因为onto
你需要两个额外的分支。使用该命令,您可以将branchB
基于的提交应用branchA
到另一个分支上,例如master
. 在下面的示例branchB
是基于branchA
和要应用的改变branchB
上master
,而不应用的变化branchA
。
o---o (master)
\
o---o---o---o (branchA)
\
o---o (branchB)
by using the commands:
通过使用命令:
checkout branchB
rebase --onto master branchA
you will get following commit hierarchy.
您将获得以下提交层次结构。
o'---o' (branchB)
/
o---o (master)
\
o---o---o---o (branchA)
回答by VonC
There is another case where git rebase --onto
is hard to grasp: when you rebase onto a commit resulting of a symmetric difference selector (the three dots '...
')
还有另一种git rebase --onto
难以理解的情况:当您重新基于对称差异选择器(三个点“ ...
”)导致的提交时
Git 2.24 (Q4 2019) does a better job of managing that case:
Git 2.24(2019 年第四季度)在管理该案例方面做得更好:
See commit 414d924, commit 4effc5b, commit c0efb4c, commit 2b318aa(27 Aug 2019), and commit 793ac7e, commit 359eceb(25 Aug 2019) by Denton Liu (Denton-L
).
Helped-by: Eric Sunshine (sunshineco
), Junio C Hamano (gitster
), ?var Arnfj?re Bjarmason (avar
), and Johannes Schindelin (dscho
).
See commit 6330209, commit c9efc21(27 Aug 2019), and commit 4336d36(25 Aug 2019) by ?var Arnfj?re Bjarmason (avar
).
Helped-by: Eric Sunshine (sunshineco
), Junio C Hamano (gitster
), ?var Arnfj?re Bjarmason (avar
), and Johannes Schindelin (dscho
).
(Merged by Junio C Hamano -- gitster
--in commit 640f9cd, 30 Sep 2019)
请参阅提交 414d924、提交 4effc5b、提交 c0efb4c、提交 2b318aa(2019 年 8 月 27 日)和提交 793ac7e、提交 359eceb(2019 年 8 月 25 日)由Denton Liu ( Denton-L
)。
帮助-通过:埃里克·阳光(sunshineco
),JUNIOÇ滨野(gitster
),VAR Arnfj重新Bjarmason(? )avar
,以及约翰内斯Schindelin( )dscho
。
请参阅提交 6330209、提交 c9efc21(2019 年 8 月 27 日)和提交 4336d36(2019 年 8 月 25 日),作者是?var Arnfj?re Bjarmason ( avar
).
帮助-通过:埃里克·阳光(sunshineco
),JUNIOÇ滨野(gitster
),VAR Arnfj重新Bjarmason(? )avar
,以及约翰内斯Schindelin( )dscho
。
(由Junio C gitster
Hamano合并-- --在提交 640f9cd,2019 年 9 月 30 日)
rebase
: fast-forward--onto
in more casesBefore, when we had the following graph,
A---B---C (master) \ D (side)
running '
git rebase --onto master... master side
' would result inD
being always rebased, no matter what.
rebase
:--onto
在更多情况下快进之前,当我们有下图时,
A---B---C (master) \ D (side)
无论如何,运行 '
git rebase --onto master... master side
' 将导致D
总是重新定位。
At this point, read "What are the differences between double-dot '..
' and triple-dot "...
" in Git diff commit ranges?"
此时,请阅读“ Git 差异提交范围中的双点 ' ..
' 和三点之间的区别是什么” ...
“?”
Here: "master...
" refers to master...HEAD
, which is B
: HEAD is side HEAD (currently checked out): you are rebasing onto B
.
What are you rebasing? Any commit notin master, and reachable from side
branch: there is only one commit fitting that description: D
... which is already on top of B
!
此处:“ master...
”指的是master...HEAD
,即B
:HEAD 是侧面 HEAD(当前已检出):您正在重新定位到B
.
你在rebase什么?任何不在master 中且可从side
分支访问的提交:只有一个提交符合该描述:D
...已经在B
!
Again, before Git 2.24, such a rebase --onto
would result in D
being always rebased, no matter what.
同样,在 Git 2.24 之前,无论发生什么,这样的操作rebase --onto
都会导致D
总是重新定位。
However, the desired behavior is that rebase should notice that this is fast-forwardableand do that instead.
但是,所需的行为是rebase 应该注意到这是可快速转发的,并改为这样做。
That is akin to the rebase --onto B A
of the OP, which did nothing.
这类似于rebase --onto B A
OP,它什么也没做。
Add detection to
can_fast_forward
so that this case can be detected and a fast-forward will be performed.
First of all, rewrite the function to use gotos which simplifies the logic.
Next, since theoptions.upstream && !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)
conditions were removed in
cmd_rebase
, we reintroduce a substitute incan_fast_forward
.
In particular, checking the merge bases ofupstream
andhead
fixes a failing case int3416
.The abbreviated graph for t3416 is as follows:
添加检测到
can_fast_forward
以便可以检测到这种情况并执行快进。
首先,重写函数以使用简化逻辑的goto。
接下来,由于options.upstream && !oidcmp(&options.upstream->object.oid, &options.onto->object.oid)
条件被移除了
cmd_rebase
,我们重新引入了一个替代品can_fast_forward
。
特别是,检查合并基础upstream
并head
修复t3416
.t3416的简图如下:
F---G topic
/
A---B---C---D---E master
and the failing command was
失败的命令是
git rebase --onto master...topic F topic
Before, Git would see that there was one merge base (
C
, result ofmaster...topic
), and the merge and onto were the same so it would incorrectly return 1, indicating that we could fast-forward. This would cause the rebased graph to be 'ABCFG
' when we were expecting 'ABCG
'.
之前,Git 会看到有一个合并基数 (
C
, result ofmaster...topic
),并且合并和 on 是相同的,所以它会错误地返回 1,表明我们可以快进。ABCFG
当我们期望“ABCG
”时,这将导致重新定位的图为“ ”。
A rebase --onto C F topic
means any commit afterF
, reachable by topic
HEAD: that is G
only, not F
itself.
Fast-forwarding in this case would include F
in the rebased branch, which is wrong.
Arebase --onto C F topic
表示在 之后的任何提交F
,可通过topic
HEAD访问:G
仅此而已,而不是F
它本身。
在这种情况下,快进将包含F
在重新定位的分支中,这是错误的。
With the additional logic, we detect that upstream and head's merge base is
F
. Since onto isn'tF
, it means we're not rebasing the full set of commits frommaster..topic
.
Since we're excluding some commits, a fast-forward cannotbe performed and so we correctly return 0.Add '
-f
' to test cases that failed as a result of this change because they were not expecting a fast-forward so that a rebase is forced.
通过附加逻辑,我们检测到 upstream 和 head 的合并基数是
F
。由于F
on不是,这意味着我们不会从master..topic
.
由于我们排除了一些提交,因此无法执行快进,因此我们正确返回 0。将“
-f
”添加到由于此更改而失败的测试用例,因为它们不期望快进,因此强制变基。