在尝试了解撤消各种 git 操作的方法时,我想出了一个我不确定如何处理的场景。免责声明:我在“生产中”实际使用 git 时没有遇到这种情况,但我仍然认为这不仅仅是一个学术问题。
echo "some content" >> example.txt
git add example.txt
git checkout @ -- example.txt
"some content"
恢复您的更改 ( )每次git add
在.git/objects/下创建带有blob 对象的暂存更改时,索引文件 ( .git/index ) 都会更新。如果我多次更改和添加内容,则会出现多个 blob。旧的不会立即被垃圾收集。
当从上面运行 checkout 命令时,索引会立即更新(我也假设内容只会在我的工作目录中但未暂存)。这样参考就消失了,我不能使用诸如git checkout-index
还原它们之类的东西。
除非垃圾收集开始,内容在技术上仍然存在。但我不知道如何将其取回,然后手动尝试以某种方式查找哈希并使用git cat-file
. 例如,对于git add
多次运行也是如此,尽管这里想要恢复先前阶段的更改可能并不是真正的用例。(或者也许当从 stash 中弹出更改时?...)
所以所有这一切都归结为以下问题:
git reflog
索引有类似的东西吗?git checkout @ -- file
认为是像 git 这样的危险命令reset --hard
,你可能会失去你的工作?如果答案是“否”/“是”(我目前的假设):
奖励:是否有其他方法可以在不立即暂存文件的情况下检出单个文件?
你的幕后描述大多是正确的。唯一不是 100% 的事情与这部分有关:
每次
git add
在.git/objects/下创建带有blob 对象的暂存更改时
在内部,git add
散列工作树文件中的数据内容,一个 la git hash-object -w -t blob
。这不一定会创建一个新对象:如果散列内容已经在存储库中,它只是重新使用现有对象。现有的对象可能被打包,即,在.git/objects/pack
,而不是作为一个单独的 blob松散。
此外,写入一个blob对象的内容可能是任意从工作树的内容不同,由于一个干净的过滤器。由于行尾设置,CR-LF 行尾与工作树中的内容更常见。清洁过滤器和行尾设置部分(或大部分,取决于您对 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
. 如果斑对象是不可达,它会被列为这些可达或悬挂斑点中的一个,或者具有其内容恢复成.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 @ --
file是否被认为是一个危险的命令,比如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 操作,您可能希望先对索引进行另一个备份。
是否有另一种方法可以在不立即暂存文件的情况下签出单个文件?
不,只是因为这里的动词checkout。要查看位于索引或任何提交中的文件的内容(以便内容具有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
目录的路径,它会重置索引中已经存在并驻留在该目录中的所有文件。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句