设置和配置
获取和创建项目
基本快照
分支与合并
共享和更新项目
检查和比较
打补丁
调试
电子邮件
外部系统
服务器管理
指南
管理
底层命令
- 2.43.1 → 2.50.1 无更改
-
2.43.0
2023-11-20
- 2.39.1 → 2.42.4 无更改
-
2.39.0
2022-12-12
- 2.24.1 → 2.38.5 无更改
-
2.24.0
2019-11-04
- 2.23.1 → 2.23.4 无更改
-
2.23.0
2019-08-16
- 2.19.3 → 2.22.5 无更改
-
2.19.2
2018-11-21
- 2.16.6 → 2.19.1 无更改
-
2.15.4
2019-12-06
- 2.11.4 → 2.14.6 无更改
-
2.10.5
2017-09-22
- 2.1.4 → 2.9.5 无更改
-
2.0.5
2014-12-17
概要
git merge-base [-a | --all] <commit> <commit>… git merge-base [-a | --all] --octopus <commit>… git merge-base --is-ancestor <commit> <commit> git merge-base --independent <commit>… git merge-base --fork-point <ref> [<commit>]
描述
git merge-base 找到在两个提交之间用于三方合并的最佳共同祖先。如果一个共同祖先是另一个共同祖先的祖先,则前者比后者更好。没有更好共同祖先的共同祖先是最佳共同祖先,即合并基础。请注意,一对提交可能存在多个合并基础。
操作模式
在最常见的特殊情况下,仅在命令行上指定两个提交表示计算给定两个提交之间的合并基础。
更一般地,在用于计算合并基础的两个提交中,一个由命令行上的第一个提交参数指定;另一个提交是一个(可能是假设的)提交,它是命令行上所有其余提交的合并。
因此,如果指定了两个以上的提交,则合并基础不一定包含在每个提交参数中。这与使用 --merge-base
选项时的 git-show-branch[1] 不同。
- --octopus
-
计算所有提供的提交的最佳共同祖先,以准备进行 n 路合并。这模仿了 git show-branch --merge-base 的行为。
- --independent
-
不打印合并基础,而是打印所提供提交的一个最小子集,这些子集具有相同的祖先。换句话说,在给定提交中,列出那些无法从任何其他提交到达的提交。这模仿了 git show-branch --independent 的行为。
- --is-ancestor
-
检查第一个 <commit> 是否是第二个 <commit> 的祖先,如果为真则以状态 0 退出,否则以状态 1 退出。非 1 的非零状态表示错误。
- --fork-point
-
查找分支(或任何指向 <commit> 的历史)从另一个分支(或任何引用)<ref> 分叉的点。这不仅查找两个提交的共同祖先,还会考虑 <ref> 的 reflog,以查看指向 <commit> 的历史是否从分支 <ref> 的早期版本分叉出来(参见下面对此模式的讨论)。
讨论
给定两个提交 A 和 B,git
merge-base
A
B
将输出一个可以通过父子关系从 A 和 B 同时到达的提交。
例如,使用此拓扑:
o---o---o---B / ---o---1---o---o---o---A
A 和 B 之间的合并基础是 1。
给定三个提交 A、B 和 C,git
merge-base
A
B
C
将计算 A 与一个假设提交 M 之间的合并基础,其中 M 是 B 和 C 的合并。例如,使用此拓扑:
o---o---o---o---C / / o---o---o---B / / ---2---1---o---o---o---A
git
merge-base
A
B
C
的结果是 1。这是因为 B 和 C 之间带有合并提交 M 的等效拓扑是:
o---o---o---o---o / \ / o---o---o---o---M / / ---2---1---o---o---o---A
并且 git
merge-base
A
M
的结果是 1。提交 2 也是 A 和 M 之间的共同祖先,但 1 是更好的共同祖先,因为 2 是 1 的祖先。因此,2 不是合并基础。
git
merge-base
--octopus
A
B
C
的结果是 2,因为 2 是所有提交的最佳共同祖先。
当历史涉及交叉合并时,两个提交可能存在多个*最佳*共同祖先。例如,使用此拓扑:
---1---o---A \ / X / \ ---2---o---o---B
A 和 B 的合并基础是 1 和 2。两者之间没有优劣之分(都是*最佳*合并基础)。当未提供 --all
选项时,不确定会输出哪一个最佳合并基础。
一种检查两个提交 A 和 B 之间“快进性”的常见惯用法是(或至少曾经是)计算 A 和 B 之间的合并基础,并检查它是否与 A 相同,如果是,则 A 是 B 的祖先。你会在较旧的脚本中经常看到这种惯用法。
A=$(git rev-parse --verify A) if test "$A" = "$(git merge-base A B)" then ... A is an ancestor of B ... fi
在现代 Git 中,你可以更直接地说:
if git merge-base --is-ancestor A B then ... A is an ancestor of B ... fi
来替代。
关于 fork-point 模式的讨论
在使用 git
switch
-c
topic
origin/master
创建的 topic
分支上工作后,远程跟踪分支 origin/master
的历史可能已被回溯并重建,导致了这种形状的历史:
o---B2 / ---o---o---B1--o---o---o---B (origin/master) \ B0 \ D0---D1---D (topic)
其中 origin/master
曾指向提交 B0、B1、B2,现在它指向 B,而你的 topic
分支是在 origin/master
还在 B0 时在其之上开始的,你在此基础上构建了三个提交 D0、D1 和 D。想象一下,你现在想将你在 topic 分支上所做的工作变基到更新后的 origin/master 之上。
在这种情况下,git
merge-base
origin/master
topic
将返回上图中 B0 的父级,但 B0^..D 不是你希望在 B 之上重放的提交范围(它包含 B0,这不是你编写的内容;它是对方将尖端从 B0 移动到 B1 时丢弃的一个提交)。
git
merge-base
--fork-point
origin/master
topic
旨在帮助解决这种情况。它不仅考虑 B,还考虑 B0、B1 和 B2(即你的仓库 reflog 所知的远程跟踪分支的旧尖端),以查看你的 topic 分支是基于哪个提交构建的,并找到 B0,从而允许你只重放 topic 上的提交,排除对方后来丢弃的提交。
因此,
$ fork_point=$(git merge-base --fork-point origin/master topic)
将找到 B0,并且
$ git rebase --onto origin/master $fork_point topic
将在 B 之上重放 D0、D1 和 D,从而创建这种形状的新历史:
o---B2 / ---o---o---B1--o---o---o---B (origin/master) \ \ B0 D0'--D1'--D' (topic - updated) \ D0---D1---D (topic - old)
需要注意的是,你的仓库中较旧的 reflog 条目可能会被 git
gc
过期。如果 B0 不再出现在远程跟踪分支 origin/master
的 reflog 中,--fork-point
模式显然无法找到它并会失败,从而避免给出随机且无用的结果(例如 B0 的父级,就像没有 --fork-point
选项的相同命令所给出的结果)。
此外,你使用 --fork-point
模式的远程跟踪分支必须是你的 topic 分支从其尖端分叉出来的分支。如果你从比尖端更旧的提交分叉,此模式将无法找到分叉点(想象在上面的示例历史中 B0 不存在,origin/master 从 B1 开始,移动到 B2,然后是 B,而你的 topic 分支是在 origin/master 为 B1 时从 origin/master^ 分叉出来的;历史形状将与上面相同,只是没有 B0,并且 B1 的父级是 git
merge-base
origin/master
topic
正确找到的结果,但 --fork-point
模式不会,因为它不是曾经位于 origin/master 尖端的提交之一)。