git 文档说:
然后,先前保存到临时区域中的提交将按顺序逐个重新应用于当前分支。请注意,HEAD中引入与HEAD中的提交相同的文本更改的任何提交。上游被省略(即,上游已经接受的具有不同提交消息或时间戳的补丁将被跳过)。
这对我来说似乎有点令人困惑。这是否仅仅意味着要复制的提交集中省略了要复制的分支中任何不更改要重定基的分支中的任何内容?
㞖:
- 如果指定了new_base怎么办? 这是否会更改 HEAD 的提交集。上游到头..new_base ?
- 为什么要使用范围? 为什么不直接说"HEAD中引入与上游提交相同的文本更改的任何提交都被省略"?
除了jthill的回答,它涉及到git rev-list
使用--no-merges
和git patch-id
的棘手的一些细节,我将添加以下注释:
- 如果使用
--rebase-merges
,则会抑制--no-merges
。 - 对于
--fork-point
(有时是默认值),变基将省略根据上游 reflogs中包含的数据在upstream..HEAD
中列出的提交。
多年来,后者经历了多次变化。 起初,分叉点模式仅在git pull
脚本中实现。 然后它被移动到适当的git rebase
,并调整了实现,现在你可以运行git merge-base --fork-point
来定位分叉点提交。
使用"分叉点"是为了在处理上游变基时提供帮助。 也就是说,假设您在origin
处拥有 Git 存储库的克隆。 您已将提交发送给控制该存储库的任何人。 他们可能已经或可能没有接受您的一些提交。 他们可能从其他用户那里获取了也可能没有接受其他一些提交。 但是,在某些时候,他们也自己运行git rebase --interactive
并从他们的提交集中删除了一些他们在某个时候拥有的提交,你在某个时候重新定位
。让我们画一个示例情况。 他们从这个开始:
...--o--o--* <-- main
您克隆了存储库并创建了自己的三个提交,我们将无缘无故地将其称为E-F-G
:
...--o--o--* <-- main, origin/main
E--F--G <-- feature-X
他们选择了四个新的提交,还没有一个与你的匹配,所以当你运行git fetch
时,你得到:
A--B--C--D <-- origin/feature-X
/
...--o--o--* <-- origin/main
E--F--G <-- feature-X
然后,您将feature-X
重新定位在他们的feature-X
(您的origin/feature-X
)之上,以获得:
E'-F'-G' <-- feature-X
/
A--B--C--D <-- origin/feature-X
/
...--o--o--* <-- origin/main
E--F--G [abandoned]
然后,他们决定提交C
是不好的,因此他们重写了feature-X
以删除C
并用新的提交D'
替换D'
。 当您运行git fetch
时,您会得到:
C--D--E'-F'-G' <-- feature-X
/
A--B--D' <-- origin/feature-X
/
...--o--o--* <-- origin/main
E--F--G [abandoned]
然后他们决定他们非常喜欢您的提交G
或G'
(无论哪个),以至于他们将其合并到他们的feature-X
中,这样如果您再次git fetch
,您将获得:
C--D--E'-F'-G' <-- feature-X
/
A--B--D'-G" <-- origin/feature-X
/
...--o--o--* <-- origin/main
E--F--G [abandoned]
其中他们的G"
是他们的G
或G'
的副本:它引入了与您的提交G'
对您的提交F'
所做的相同的更改,但行号不匹配。
理想情况下,您希望git rebase
以某种方式自动确定他们的提交C
和D
,现在看起来像是您的提交,是他们的提交并且被放弃以支持他们的D'
,并且他们的提交G"
"与您的G'
一样好"。 所以你会希望git rebase
产生这个:
C--D--E'-F'-G' [abandoned]
/
A--B--D'-G" <-- origin/feature-X
/
...--o--o--* E"-F" <-- feature-X
E--F--G [abandoned]
也就是说,您希望git rebase
:
- 不复制提交
C
,即使您有一个而他们没有; - 不复制提交
D'
,以及 - 不复制提交
G'
。
git rebase
使用的补丁 ID 技巧可能会处理D'
和G'
,但不会正确省略C
。 分叉代码将正确省略C
,前提是您的origin/feature-X
分支的 reflog 中包含正确的信息。 只要所有这些活动都发生在过去90天左右,通常都是如此。
有关--fork-point
选项的更多信息,请参阅 Git 变基 - 在分叉点模式下提交选择,当然还有git rebase
文档。
是否仅仅意味着要复制的提交集中省略了要复制的分支中任何不更改要重定基的分支中的任何内容?
不。要深入了解铜钉,您可以看到什么是普通git rebase
视为候选人
git rev-list --reverse --no-merges @{upstream}..
以及它正在检查的提交,以避免重新应用已应用的提交,使用
git rev-list --reverse --no-merges ..@{upstream}
检查使用git patch-id
。要看看 git 在看什么,
git rev-list --reverse --first-parent --no-merges @{upstream}..
| git diff-tree --patch --stdin
| git patch-id
和
git rev-list --reverse --no-merges ..@{upstream}
| git diff-tree --patch --stdin
| git patch-id
除了序列在--right-only
的内部,变基真正运行git format-patch --right-only @{u}...
来获取其信息。
对我来说似乎有点令人困惑。这是否仅仅意味着要复制的提交集中省略了要复制的分支中任何不更改要重定基的分支中的任何内容?
是的,主要是因为在正常情况下,Git 永远不会记录空提交,除非明确说明。相反,您会在这里得到的是git status
返回的消息"您的工作目录是干净的"。
如果指定了new_base怎么办? 这是否会更改 HEAD 的提交集。上游到头..new_base ?
我相信这确实是他们所说的"上游"的意思。
为什么要使用范围? 为什么不直接说"HEAD中引入与上游提交相同的文本更改的任何提交都被省略"?
因为这是 Git 实际上区分两个分支的方式。此范围的底部提交是两个分支开始分叉的位置。