【发布时间】:2022-03-22 20:40:44
【问题描述】:
在 Git 中,我在模块 A 中有子模块 B。我想转到 B 中的特定提交,然后使用 B 的特定提交(如果有)在 A 中找到相应的提交。例如,使用该特定 B 版本的 A 的最新提交就可以了。如何做到这一点?
【问题讨论】:
标签: git git-submodules
在 Git 中,我在模块 A 中有子模块 B。我想转到 B 中的特定提交,然后使用 B 的特定提交(如果有)在 A 中找到相应的提交。例如,使用该特定 B 版本的 A 的最新提交就可以了。如何做到这一点?
【问题讨论】:
标签: git git-submodules
在合理的限制下,可以使用git bisect 找到此提交:
#!/bin/bash
#
# Find superproject commit introducing given submodule's commit
#
# Usage: git submodule-introduced path/to/submodule <submodule-commit>
#
# Bisect is performed. On success found commit is displayed and written to the
# 'bisect/containing' ref
#
# Limitations:
# 1. Submodule must have monotonous history (all submodule updates are fast-forward)
# 2. Commit adding a submodule must have parents
set -e
export submodule=${1?specify submodule path}
export commit=${2?specify submodule commit}
from=$(git rev-list HEAD -- $submodule | tail -n 1)
git bisect start --no-checkout --term-bad=containing HEAD $from~ -- $submodule
git bisect run sh -c '! git -C $submodule merge-base --is-ancestor $commit $(git rev-parse BISECT_HEAD:$submodule)'
【讨论】:
这个问题和@Max O 的回答促使我创建了mod-from-sub.bash(免责声明:这是我的个人github)。
脚本目标:find the most recent module's ancestor commit for a given submodule's hash/branch。
下面的示例输出 (Module=A, Submodule=B),当前工作目录 (cwd) 作为 A 的根目录:
$ PATH_CONTAINING_SHELL_DIR/shell/bash/git/mod-from-sub.bash --submodule 'relative/path/A/to/B'
INFO: mod-from-sub.bash: module is empty, using cwd='/path/to/A'.
INFO: mod-from-sub.bash: remote_module is empty, using 'origin'.
INFO: mod-from-sub.bash: ref_module is empty, using 'origin/master'.
INFO: mod-from-sub.bash: ref_sub is empty, using 'HEAD'.
################################################################################
INFO: mod-from-sub.bash: SUCCESS: submodule='relative/path/from/A/to/B' with ref='HEAD' has nearest ancestor for module='/path/to/A' on the below line.
90f6c749140e5efe2361b40ae7612c25f869b116
注意事项: 这不会从 A 获得引入 B 的提交的提交(不是 B 的每个提交甚至都会有来自 A 的相应提交)
通过下图,如果我们寻找 B2 的最近祖先,我们会得到 A1,而不是 A2。我还没有获得 A2 的实际目的,但如果需要,请随时发表评论并尝试为您提供有用的东西。
A2 -> B3
| |
| B2
| |
A1 -> B1
注意:即使存在指向 B1 的提交 A0,我们仍然会获得 A1,因为 A1 是 B2 的最新祖先。
此外,截至目前,这不是一个独立的脚本。需要整个 shell 目录(一小部分 .bash 和 .sh 文件);在某些时候,我计划制定一种机制来部署脚本及其包含作为独立脚本,但我还没有开始。
【讨论】:
无法保证存在这样的提交。如果确实存在一个,它可能不是唯一的(可能不止一个)。
当然,不能保证任何事情都会奏效,但这不是我的意思。子模块背后的设计原则是相反的:超级项目,在你的例子中是“模块 A”,被认为是控制项。您检查一些给定的超级项目提交——通过分支名称、标签名称、或原始哈希 ID 或其他方式——并且在该签出的提交中,超级项目包含子模块中特定提交的哈希 ID。
因此,对于模块 A 中的每个提交(或者每个使用 B 的提交),B 都有一些特定的哈希 ID。我们暂时假设 A 中只有三个提交,编号为 A1、A2 和A3,以及 B 中的三个提交,编号为 B1、B2 和 B3。如果我们检查 A1,我们会得到一个 B 号。假设这是B2。如果我们检查 A2,我们会得到另一个 B 号码。假设又是 B2。最后,如果我们检查 A3,我们会得到另一个 B 数字……很可能又是 B2。
在这种情况下,对应于提交 B1 和 B3 的“A”提交集合都是 空,对应于 B2 的“A”提交集合具有基数 3。对于某些B 提交,有 no A 提交,对于其他 B 提交,没有 unique A 提交。
这对于子模块的使用通常是正确的,所以给定一些 B 提交哈希,不可能找到 对应的 A 提交。但是,您可以查看任意数量的 A 提交并找到它们对应的 B 哈希。这些是提交树中的 gitlink 条目,请参阅How can I get a git submodule's associated commit ID from a past commit in the parent clone?(请注意基于git rev-parse 的答案,如果您知道子模块的 gitlink 的特定路径,这是要走的路)。如果您制作一个包含所有 A->B 映射的完整表,则很容易反转该表并找到给定 B 的所有 A 值的集合 值,因此集合是否为空。
【讨论】:
git rev-list --all | while read ahash; do abhash=$(git rev-parse --quiet --verify ${ahash}:path/to/b/submodule) || continue; test $abhash = $bhash && echo "$bhash found in $ahash"; done。这假设您已将 bhash 设置为所需的哈希 ID。如果您要重复执行此操作,而不是每次检查子模块是否存在时都循环遍历所有哈希,而是通过检查循环一次,将所有 (ahash, abhash) 对写入 [续]
grep ${bhash}\$ mapfile 即可找到以所需 B 哈希结尾的行。