如何解决查找没有分支名称/标签的 git "branch" 在该分支上具有提交哈希
有没有办法让 Git 列出特定提交的子提交?也就是说,如果我有 Git 分支:
A---B---C---D---E
而且我知道 C
的提交哈希,有没有办法从 D
获取 C
?
这里更大的问题是我丢失了一个分支,因为我移动了唯一指向它的分支标签。所以我有这样的事情:
A---B---C---D (master,moved-branch-label)
\
\---E---F---G---H
假设我有 E
或 F
的哈希值。我如何恢复 H
?
这个有a similar existing question。最大的区别在于 OP 不知道 E
、F
、G
或 H
中的任何一个。在这种情况下,唯一的答案是使用 reflog
回溯您的步骤并手动查找 H
的哈希值。
但是在这里,我知道我要找的分支在哪里!我只需要跟随我所知道的提交中的孩子们。我不敢相信在 Git 中没有办法做到这一点。对于 Git 来说,这难道不是一个简单的操作吗?给定 E
,它不需要知道 F
吗?看来我应该可以用这样的操作找到E-F-G-H分支的结尾。
顺便说一句,我很震惊地了解到,如果您不为 git log
提供其中一个的哈希值,则无法获取上述节点 E、F、G 或 H 的 Git 日志条目。分支上缺少标签意味着 Git 会忽略该分支。所以 git log --all
不会显示这些提交。我一直认为 git log --all
会从字面上显示对存储库执行的所有提交。但似乎并非如此。如果有人可以反驳这一点,或者告诉我如何强制 git log
向我展示那些孤立的提交,那将非常有帮助。
解决方法
仅在本地搜索:
git reflog --parents | grep {HASH_OF_COMMIT_F}
仅搜索您的远程存储库:
git reflog --parents --remotes | grep {HASH_OF_COMMIT_F}
在本地和远程存储库中搜索:
git reflog --parents --all | grep {HASH_OF_COMMIT_F}
这些将以 COMMIT_F
格式显示以 {COMMIT_HASH} {HASH_OF_PARENT_1} {HASH_OF_PARENT_2} . . .
作为父项的提交列表。这将使您获得 COMMIT_F
的所有直系子代,这将有助于您进行搜索。
请注意,使用了缩短的提交哈希(即前 7 个字符)
,这是一个旁注(因此应该是一个评论,但我需要格式化,还有更多的空间——好吧,远更多的空间——比评论中的空间更多):
顺便说一句,我很震惊地了解到,如果您不为 git log
提供其中一个的哈希值,则无法获取上述节点 E、F、G 或 H 的 Git 日志条目。分支上缺少标签意味着 Git 会忽略该分支。所以 git log --all
不会显示这些提交。我一直认为 git log --all
会从字面上显示对存储库执行的所有提交。
这在一些其他版本控制系统中是有意义的,但在 Git 中却不是:
-
--all
指的是所有引用,而不是所有提交; - Git 通过从给定的哈希 ID 开始查找提交——可能来自参考,或者只是你在命令行中列出的原始哈希 ID——然后在提交本身内向后工作 ; 和
- 每次提交都在零个或多个分支上。在大多数存储库中,(单个)根提交位于每个分支上。
“丢弃”提交,例如 E-F-G-H
,在 Git 中自然发生:它们是 git rebase
的结果,例如,在将 E-F-G-H
链复制到一些新的集合之后- 和改进的提交。例如,您可能希望 E
的副本的父项是 D
而不是 B
,并将旧的 F
+G
压缩在一起,以得到:
E'-FG-H' <-- somebranch
/
A--B--C--D <-- master
\
E--F--G--H ??? [was somebranch,earlier]
git reflog
找到这些的原因和方式是每个 ref 都有一个 log 它曾经保存的值。所以在上面的例子中,somebranch
的 reflog 将显示在某一时刻,它命名为 commit E
;在另一个——可能就在之后——它命名为 commit F
。这将针对 G
和 H
重复,然后变基操作将一次性将名称 somebranch
拉过来以提交 H'
。 E'-FG-H'
链是由 git rebase
使用 分离 HEAD 模式构建的,因此唯一包含这些哈希 ID 的引用日志是 HEAD
本身的引用日志,它也是一个参考1
请注意,“挤压提交”FG
本身是通过首先制作提交 F'
的副本 F
,然后将该副本推到一边以构建 FG
来构建的,因此我们可以很好地将上述内容绘制为:
F' ???
/
E'-FG-H' <-- somebranch
/
A--B--C--D <-- master
\
E--F--G--H ??? [was somebranch,earlier]
事实上,Git 中分支的整个概念往好里说是可疑的,往坏了说,是胡说八道。请注意在上图中,提交 A
如何在“所有分支”上,包括通过从现在丢弃的提交 H
向后工作形成的隐含分支。我们可以随时创建、销毁和/或移动分支,而无需更改任何现有提交。名称只是充当标签,指向进入图形。当名称是分支名称时,人们将导致并包括由指向的提交称为“分支”。如果我们添加两个名称,一个指向 F'
,一个指向 H
,则提交 A
现在位于四个分支上。如果没有这些名称,A
位于两个分支上。但是,如果我们对提交 C
进行 detached-HEAD 检出呢?那是一个分支吗?如果是这样,A
在它上面。
与此同时,无论何时何地,创建临时对象(包括临时提交)的想法在 Git 中无处不在; 不显示所有对象对于完成任何事情至关重要,因为对象太多了。 Git 的垃圾收集器 git gc
会在一段时间后删除它们(如果它们确实未使用)。
git gc
还会删除旧的 reflog 条目。 reflog 条目具有创建时间戳,一段时间后(默认情况下为 30 天或 90 天,尽管您可以调整这两者),reflog 条目被认为足够陈旧而无趣,并被删除。一旦删除了某些内部 Git 对象的all 提及,并且满足了其他几个条件,git gc
将删除该对象。这就是 Git 在各种 Git 操作后在后台剥离 git gc --auto
的原因:清理剩余的垃圾。
这就是以其他方式丢弃的提交的 30 天宽限期的来源。 30 天的时间限制是某些特定引用日志的 reflogExpireUnreachable
设置的结果。 90 天期限是 reflogExpire
设置的结果。请注意,这两个设置至少可能每个 reflog 有两个值:当 ref {{1 的 reflog 过期时,存储在 gc.pattern.reflogExpire
中的时间值会覆盖 gc.reflogExpire
中存储的时间}},如果 name
与 pattern
匹配。 The documentation 是 ... 对此处 name
的构成不甚了解。它也未能正确描述 pattern
和 expireUnreachable
超时之间的区别:
expire
gc.reflogExpire
git reflog expire 删除早于此时间的引用日志条目;
默认为 90 天。值“now”使所有条目过期
立即,“从不”完全抑制过期。和
“
gc.<pattern>.reflogExpire
gc.reflogExpireUnreachable
git reflog expire 删除早于这个时间的 reflog 条目并且
无法从当前提示到达;默认为 30 天。这
值“now”立即使所有条目过期,“never”抑制
完全到期。使用“
notreachable from the current tip 表示 Git 会检查当前存储在 ref 中的实际值。如果这标识了一个提交,该提交返回哈希 ID 存储在 reflog 条目中的提交,则 Git 选择 gc.<pattern>.reflogExpireUnreachable
时间。如果它标识了一个提交,不会返回 reflog 条目中的提交,则 Git 会选择 expire
时间。顾名思义,听起来 expireUnreachable
会同时查看此类条目的两次,但实际上 git gc
只是假设“无法访问”的宽限期将小于或等于用于可达的提交。
正如所有这些暗示的那样,可达性是 Git 中的一个核心概念。太多的 Git 介绍中没有正确教授它。要获得好的解释,请参阅 Think Like (a) Git。
(我自己不知道 git gc
是如何工作的。如果没有在 Git 源代码中摸索或进行实验,我的猜测是 Git 在这里使用了全局样式匹配,但即使是这样,我们也应该想知道:在一端或两端是否有任何隐含的 <pattern>
或 *
globs?也就是说,**
真的是 refs/stash
,还是锚定在 {{1} } 和/或 **/refs/stash/**
结束?我从未尝试调整我的 refs
调用的引用日志过期时间:默认值很好。)
1由于ref被定义为*以stash
开头的东西,git gc
不能完全是一个引用。但它仍然有一个 reflog,这意味着它是一个 ref。我们可以将其与 pseudorefs 进行比较,例如 refs/
、HEAD
、ORIG_HEAD
等,它们不会获得 reflog。 Git 文档在 CHERRY_PICK_HEAD
中有点软性,呃,关于 MERGE_HEAD
是否算作参考,这里是模糊的。
事实上,HEAD
——像这样全部大写——是特别的。有一种象征性的方式来引用它,使用字符 HEAD
,这可能有助于强调它的特殊性。不过,将 HEAD
用于 @
首次出现在 Git 1.8.5 中,并且随着时间的推移修复了各种故障。这种特殊性体现在其他方面:例如,@
永远不会打包,如果保存它的文件消失了,Git 就不再认为存储库是存储库:文件的存在是内部“这是一个 Git 存储库”测试中的三个标准之一。此外,HEAD
现在是每个工作树的引用,但这也适用于,例如,二等分引用。由于添加了 HEAD
,每个工作树引用的整个概念在 Git 2.5 中都是新的。有些事情在 Git 2.7 中得到了一些纠正,并且在 Git 2.14 和 2.15 之前没有修复一些影响 HEAD
的讨厌的每个工作树项目。出于这个原因,如果您的 Git 不是至少 2.15,我建议您注意 git worktree
。
请注意,分支、标签、远程跟踪名称等都是一般形式的子集。名称以 git gc
开头的引用是分支名称。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。