git-merge-base

原文: https://git-scm.com/docs/git-merge-base

名称

git-merge-base - 为合并找到尽可能好的共同祖先

概要

  1. git merge-base [-a|--all] <commit> <commit>…​
  2. git merge-base [-a|--all] --octopus <commit>…​
  3. git merge-base --is-ancestor <commit> <commit>
  4. git merge-base --independent <commit>…​
  5. git merge-base --fork-point <ref> [<commit>]

描述

git merge-base 找到两个提交之间最好的共同祖先,用于三向合并。如果后者是前者的祖先,一个共同的祖先比另一个共同的祖先更好。没有任何更好的共同祖先的共同祖先是最佳共同祖先,即合并基。请注意,一对提交可以有多个合并基础。

操作模式

作为最常见的特殊情况,在命令行上仅指定两个提交意味着计算给定两个提交之间的合并基础。

更一般地,在计算合并基数的两个提交中,一个由命令行上的第一个提交参数指定;另一个提交是(可能是假设的)提交,它是命令行上所有剩余提交的合并。

因此,如果指定了两个以上的提交,则合并库不一定包含在每个提交参数中。当与--merge-base选项一起使用时,这与 git-show-branch [1] 不同。

  1. --octopus

计算所有提供的提交的最佳共同祖先,为n路合并做准备。这模仿了 git show-branch —merge-base 的行为。

  1. --independent

不是打印合并库,而是使用相同的祖先打印提供的提交的最小子集。换句话说,在给出的提交中,列出那些不能从任何其他提交的提交。这模仿了 git show-branch —independent 的行为。

  1. --is-ancestor

检查第一个< commit>是第二个< commit>的祖先,如果为true则退出,状态为0,否则退出状态为1。错误由非零状态发出信号,该状态不为1。

  1. --fork-point

找到分支(或任何导致< commit>的历史记录)从另一个分支(或任何引用)< ref>分叉的点。这不只是寻找两个提交的共同祖先,而且还考虑了< ref>的reflog。查看导致< commit>的历史记录分支早期的分支< ref>分叉(见下面关于这种模式的讨论)。

OPTIONS

  1. -a
  1. --all

输出提交的所有合并基础,而不是仅输出一个。

讨论

给定两个提交 ABgit merge-base A B将通过父关系输出可从 AB 到达的提交。

例如,使用此拓扑:

  1. o---o---o---B
  2. /
  3. ---o---1---o---o---o---A

AB 之间的合并碱基是 1

给定三次提交 ABCgit merge-base A B C将计算 A 和假设提交之间的合并基数M ,是 BC 之间的合并。例如,使用此拓扑:

  1. o---o---o---o---C
  2. /
  3. / o---o---o---B
  4. / /
  5. ---2---1---o---o---o---A

git merge-base A B C的结果是 1 。这是因为 BC 之间具有合并提交 M 的等效拓扑是:

  1. o---o---o---o---o
  2. / \
  3. / o---o---o---o---M
  4. / /
  5. ---2---1---o---o---o---A

git merge-base A M的结果是 1 。提交 2 也是 AM 之间的共同祖先,但 1 是更好的共同祖先,因为 21 的祖先。因此, 2 不是合并基础。

git merge-base --octopus A B C的结果是 2 ,因为 2 是所有提交的最佳共同祖先。

当历史涉及纵横交错时,两个提交可以有不止一个最佳共同祖先。例如,使用此拓扑:

  1. ---1---o---A
  2. \ /
  3. X
  4. / \
  5. ---2---o---o---B

12 都是A和B的合并碱基。两者都不比另一个好(两者都是最佳合并碱基)。如果未给出--all选项,则未指定输出哪一个最佳选项。

在两个提交A和B之间检查“快进”的常用习惯用法是(或者至少用于)计算A和B之间的合并基础,并检查它是否与A相同,在这种情况下,A是B的祖先。你会看到这个习惯用法经常用在较旧的脚本中。

  1. A=$(git rev-parse --verify A)
  2. if test "$A" = "$(git merge-base A B)"
  3. then
  4. ... A is an ancestor of B ...
  5. fi

在现代git中,您可以更直接地说出这一点:

  1. if git merge-base --is-ancestor A B
  2. then
  3. ... A is an ancestor of B ...
  4. fi

代替。

关于叉点模式的讨论

处理使用git checkout -b topic origin/master创建的topic分支后,远程跟踪分支origin/master的历史记录可能已经倒回并重建,从而导致此形状的历史记录:

  1. o---B2
  2. /
  3. ---o---o---B1--o---o---o---B (origin/master)
  4. \
  5. B0
  6. \
  7. D0---D1---D (topic)

其中origin/master用于指向提交B0,B1,B2,现在它指向B,当origin/master位于B0时,您的topic分支在它上面启动,并且您构建了三个提交,D0, D1和D,在它上面。想象一下,您现在想要在更新的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知道的远程跟踪分支的旧技巧),以查看构建主题分支的提交并找到B0,允许您仅重放对你主题的提交,不包括后来丢弃的提交。

于是

  1. $ fork_point=$(git merge-base --fork-point origin/master topic)

会找到B0,和

  1. $ git rebase --onto origin/master $fork_point topic

将重放B顶部的D0,D1和D以创建此形状的新历史记录:

  1. o---B2
  2. /
  3. ---o---o---B1--o---o---o---B (origin/master)
  4. \ \
  5. B0 D0'--D1'--D' (topic - updated)
  6. \
  7. D0---D1---D (topic - old)

需要注意的是,您的存储库中较旧的reflog条目可能会被git gc过期。如果远程跟踪分支origin/master的reflog中不再出现B0,则--fork-point模式显然无法找到并失败,从而避免给出随机且无用的结果(例如B0的父级,就像同一命令一样没有--fork-point选项给出)。

此外,您使用--fork-point模式的远程跟踪分支必须是您的主题从其提示分叉的主题。如果你从一个比提示更早的提交分叉,这个模式将找不到叉点(想象在上面的示例历史B0不存在,origin / master从B1开始,移到B2然后B,你分叉你的主题at origin / master ^当origin / master为B1时;历史的形状与上面相同,没有B0,B1的父级是git merge-base origin/master topic正确找到的,但--fork-point模式不会,因为它不是曾经位于origin / master的提交之一。

也可以看看

git-rev-list [1]git-show-branch [1]git-merge [1]

GIT

部分 git [1] 套件