当试图了解撤消各种git操作的方法时,我想到了一个不确定如何处理它的场景。免责声明:在实际使用git“生产”时,我没有这种情况,但我仍然认为这不仅是一个学术问题。
echo "some content" >> example.txt
git add example.txt
git checkout @ -- example.txt
"some content"
)每次使用git add
更改阶段时,都会在 .git / objects / 下创建一个blob对象,并获得索引文件( .git / index )更新。如果我多次更改和添加东西,将有多个斑点。不是立即收集旧的垃圾。
从索引上方运行checkout命令时,索引会立即更新(同样,我也假定内容仅位于我的工作目录中,但未暂存)。这样,引用就消失了,我无法使用git checkout-index
之类的东西来还原它们。
除非技术上仍然存在垃圾回收,否则内容仍然存在。但是我不知道如何将其取回,然后手动尝试以某种方式查找哈希并使用git cat-file
读取内容。相同的例如多次运行git add
时确实如此,尽管这里想找回先前已进行的更改可能并不是真正的用例。 (或者也许是从隐藏弹出更改时??)
所有这些归结为以下问题:
git reflog
之类的内容?git checkout @ -- file
是否被视为像git reset --hard
这样的危险命令,可能会导致您的工作松动?如果答案是“否” /“是”(到目前为止,我假设是这样):
奖金:是否有另一种方法可以在不立即登台的情况下检出单个文件?
答案 0 :(得分:2)
您对引擎盖的描述大部分是正确的。唯一不是100%的事情与这部分有关:
每次使用
下创建一个blob对象。git add
更改阶段时,都会在 .git / objects /
在内部,git add
对工作树文件la git hash-object -w -t blob
中的数据内容进行哈希处理。这不必不必要创建一个 new 对象:如果散列内容已经在存储库中 ,它只会重新使用现有对象。现有对象可能是打包的,即在.git/objects/pack
中,而不是 loose 作为单独的blob。
此外,由于干净过滤器,写入blob对象 的内容可能任意与工作树中的内容不同。通常,由于行尾设置,CR-LF-行尾与工作树中的内容不同。干净的过滤器和行尾设置的一部分(或大部分,取决于您对Git的使用)通过.gitattributes
文件进行控制,部分(或大部分)通过配置中的设置进行控制。
无论如何,重要的是您获得了Blob对象的哈希ID。 blob对象肯定存在于某个地方,在.git/objects
目录中作为松散对象,或在pack文件中。现在git add
可以写入.git/index
(或任何其他文件GIT_INDEX_FILE
表示):它将在暂存插槽零的索引中存储给定 {{1 }} ,使用计算后的blob哈希和模式path
或100644
,具体取决于是否稍后将工作树文件标记为可执行。
[场景已被删除,但以100755
破坏了索引条目而结束,其 git checkout HEAD -- path
代表了$path
和模式$blobhash
信息, 和破坏 $mode
中文件的工作树副本。)
除非技术上仍然存在垃圾回收,否则内容仍然存在。但是我不知道如何将其取回,然后手动尝试以某种方式查找哈希并使用
path
读取内容。
实际上,您不能:哈希ID计算是trapdoor function,只有做有哈希,您才能让Git散布内容,但是您需要如果没有哈希,则具有内容。那就是你的Catch-22 situation。
If -这是一个非常重要的“ if”-内容 是唯一的,因此git cat-file
确实创建了一个新 blob对象,和您刚刚覆盖了索引中的blob引用,该blob对象确实不再在任何地方引用。另一方面,如果git add
最终重用了一些现有的Blob,则该Blob对象仍会被以前引用的对象引用。因此,现在有两种有趣的情况:blob 曾经是并且现在可以进行垃圾回收,或者blob是不是唯一并且不是。
使用git hash-object -w
或git fsck --lost-found
或git fsck --unreachable
(默认设置),您可以让Git遍历整个对象数据库,确定哪些对象可达以及哪些对象并告诉您一些或所有不可达的信息,和/或将信息或从中复制信息到git fsck --dangling
中。如果blob对象 无法访问,则 将被列为这些不可访问或悬挂的blob之一,或者将其内容恢复到.git/lost-found
中。
这里的缺点是可能有数十个甚至数百个悬挂的斑点对象。现在,您的任务已从“猜测哈希”(实际上是不可能的)切换为“在大海捞针中找到针”(不是那么困难,但很乏味,并且您很可能会发现错误针—不是)真的是干草堆,毕竟是一堆针)。而且,当然,这仅适用于“斑点是唯一的”情况。
(顺便说一句,这实际上不是问题{em> 的重复Can git undo a checkout of unstaged files。但是这个问题仍然有用,所以也可以查看。)
索引是否有
.git/lost-found
之类的东西?
不。您可以制作自己的备份副本:只需git reflog
。但是,Git不能自己做到这一点。您可能会在cp .git/index
操作之前通过用来执行这种危险操作的别名或shell函数进行操作。
请注意,Git无法识别这些备份副本,因此git checkout HEAD -- path
不会将引用的对象视为受保护的对象。要将备份与git gc
之类的管道命令一起使用,请在该命令期间将路径名放入git ls-files
中。
GIT_INDEX_FILE
文件是否被视为像git checkout @ --
这样的危险命令,您有可能会丢失工作?
答案取决于谁在考虑。我建议自己考虑这样做很危险,因为您根本在问这个问题。 :-)
是否有管道命令手动更改/重写索引? (请参见上面的示例,其中对象仍在其中)
是:git reset --hard
是一次一次输入的更新程序(使用git update-index
或--cacheinfo
提供原始索引输入数据,而不是让它们重复很多--stdin
个工作)。许多其他命令也可以部分更新索引或进行索引更新。
如果您有一个在进行git add
操作之前备份索引的过程,则可以从备份索引中读取条目(例如,使用git checkout HEAD -- ...
),然后使用{{1 }},没有设置了GIT_INDEX_FILE=... git ls-files
,可将信息放入常规索引。当然,这是一个索引覆盖Y操作,您可能希望首先对索引进行另一个备份。
是否有另一种方法可以在不立即登台的情况下检出单个文件?
否,但这仅是因为动词 checkout 在这里。要查看索引或任何提交中的文件的内容,以使内容具有git update-index
可以理解的名称,请使用GIT_INDEX_FILE
:< / p>
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
提供目录路径,它将重置索引中已经存在并驻留在目录中的所有文件。