您的幕后描述大多是正确的。唯一不是 100% 的事情与这部分有关:
每次在.git/objects/git add
下创建带有 blob 对象的暂存更改时
在内部,git add
对工作树文件 a la 中的数据内容进行哈希处理git hash-object -w -t blob
。这并不一定会创建一个新对象:如果散列内容已经在存储库中,它只是重新使用现有对象。现有对象可能被打包,即在 中.git/objects/pack
,而不是作为单独的 blob松散。
此外,由于干净的过滤器,写入 blob 对象的内容可能与工作树中的内容任意不同。由于行尾设置,CR-LF-line-ending-与工作树中的内容更常见的是不同。清洁过滤器和行尾设置部分(或大部分,取决于您对 Git 的使用)通过您的文件进行控制,部分(或大部分)通过您的配置中的设置进行控制。.gitattributes
在任何情况下,重要的是您获得一个 blob 对象的哈希 ID。blob 对象肯定存在于某个地方——.git/objects
作为松散对象存在于目录中,或者存在于包文件中。现在git add
可以写入.git/index
(或任何其他文件GIT_INDEX_FILE
指示):它将path
使用计算的 blob-hash 和模式100644
或100755
取决于是否应标记工作树文件,在暂存槽零处的索引中存储给定的条目以后可执行。
如果你失去了它,你大多不走运
[场景被剪断,但它以破坏索引条目、其表示和模式信息以及破坏文件的工作树副本结束。)git checkout HEAD -- path
$path
$blobhash
$mode
path
除非垃圾收集在技术上仍然存在。但是我不知道如何取回它,然后手动尝试以某种方式找到哈希并使用git cat-file
.
事实上,你不能:哈希 ID 计算是一个陷门函数,只有你有哈希才能让 Git 溢出内容,但如果你没有哈希,你需要有内容。这就是你的Catch-22 情况。
如果——这是一个非常重要的“如果”——内容是唯一的,所以git add
确实创建了一个新的blob 对象,而您刚刚覆盖了索引中的 blob 引用,那么该 blob 对象确实不再在任何地方引用. 另一方面,如果git hash-object -w
最终重用某个现有的 blob,则该 blob 对象仍会被之前引用它的任何对象引用。所以现在有两个有趣的情况:blob是唯一的,现在可以进行垃圾回收,或者,blob不是唯一的,现在不是。
使用git fsck --lost-found
orgit fsck --unreachable
或git fsck --dangling
(默认值),您可以让 Git 遍历整个对象数据库,确定哪些对象是可访问的,哪些是不可访问的,并告诉您一些或所有不可访问的对象和/或从或关于它们的信息复制到.git/lost-found
. 如果 blob 对象不可访问,它将被列为这些不可访问或悬空 blob 之一,或者将其内容恢复到..git/lost-found
这里的缺点是可能有数十个甚至数百个悬垂的 blob 对象。你的任务现在已经从“猜测哈希”(几乎不可能)切换到“大海捞针”(没那么难,但很乏味,而且你很可能会找错针——这不是真正的大海捞针,而是一堆毕竟是针)。而且,当然,这只适用于“blob is unique”的情况。
具体问题的答案
(顺便说一下,这个问题并不是真的与Can git undo a checkout of unstaged files重复的地方。但是那个问题仍然有用,所以也看看它。)
git reflog
索引有类似的东西吗?
不,您可以制作自己的备份副本:就在cp .git/index
某个地方。但Git 不会自己做这件事。您可以在操作之前通过一些别名或 shell 函数在操作之前创建一个,用于执行这种危险操作。git checkout HEAD -- path
请注意,Git 不知道这些备份副本,因此git gc
不会认为引用的对象是受保护的。要将备份与管道命令(如)一起使用git ls-files
,请将路径名放入GIT_INDEX_FILE
该命令的持续时间内。
文件是否git checkout @ --
被认为是一个危险的命令git reset --hard
,比如你可能会丢失你的工作?
答案取决于谁在考虑。我建议自己考虑一下它是危险的,因为您根本就在问这个问题。:-)
是否有手动更改/重写索引的管道命令?(见上面物体仍然存在的情况)
是:git update-index
是一次一个条目的更新程序(使用--cacheinfo
或--stdin
提供原始索引条目数据,而不是让它重复大量git add
工作)。许多其他命令也部分或整体更新索引。
如果您有一个在git checkout HEAD -- ...
操作之前备份索引的过程,您可以从备份索引中读取条目(GIT_INDEX_FILE=... git ls-files
例如使用),然后使用git update-index
,无需设置GIT_INDEX_FILE
,将信息放入常规索引。当然,这是一个 index-overwrite-y 操作,您可能希望先对索引进行另一个备份。
是否有另一种方法可以在不立即暂存的情况下签出单个文件?
不,只是因为这里的动词结帐。要查看位于索引或任何提交中的文件的内容——以便内容具有git rev-parse
可以理解的名称——使用git show
:
git show :file # file in index at stage zero
git show :3:file # file in index at stage three, during merge conflict
git show HEAD:file # file in current commit
git show master~7:file # file in commit 7 first-parent hops back from master
另请注意,git reset
可以覆盖索引中的一个或多个文件,而无需触及工作树中的文件:
git reset HEAD -- file # copy HEAD:file to :file leaving work-tree file undisturbed
如果您提供git reset
目录的路径,它将重置已在索引中并驻留在目录中的所有文件。