还原先前暂存的更改(或:撤消对 .git/index 的更改)

为什么

在尝试了解撤消各种 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 和模式100644100755取决于是否应标记工作树文件,在暂存槽零的索引中存储给定的条目稍后可执行。

如果你失去了它,你就很不走运

[场景被截断,但它以破坏索引条目、其代表和模式信息以及破坏文件的工作树副本结束。)git checkout HEAD -- path$path$blobhash$modepath

除非垃圾收集开始,内容在技术上仍然存在。但我不知道如何将其取回,然后手动尝试以某种方式查找哈希并使用git cat-file.

事实上,你不能:哈希ID计算是一个陷门函数,只有当你这样做有散,你可以让Git洒出的内容,但是你需要有内容,如果你不具备的哈希值。这就是你的Catch-22 情况

如果——这是一个非常重要的“如果”——内容唯一的,因此git add确实创建了一个新的blob 对象,并且您刚刚覆盖了索引中的 blob 引用,则该 blob 对象确实不再在任何地方被引用. 另一方面,如果git hash-object -w最终重用某些现有的 blob,则 blob 对象仍会被之前引用它的任何对象引用。所以现在有两种有趣的情况:blob唯一的,现在有资格进行垃圾收集,或者 blob不是唯一的,也不是。

使用git fsck --lost-foundorgit fsck --unreachablegit 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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

还原暂存的更改,保留未暂存的更改

来自分类Dev

如何还原/撤消对Activerecord对象的本地更改?

来自分类Dev

Git:如何撤消某个文件夹中的暂存更改

来自分类Dev

吉特 撤消工作目录中的暂存更改,使暂存区域保持不变

来自分类Dev

更改后如何还原/撤消桌面墙纸?

来自分类Dev

如何撤消tortoisesvn中的“从此修订版本还原更改”

来自分类Dev

git:合并分支中的更改,该分支还原了先前的合并

来自分类Dev

为什么“ git reset HEAD”不会撤消我未提交的,未暂存的更改?

来自分类Dev

为什么“ git reset HEAD”不会撤消我未提交的,未暂存的更改?

来自分类Dev

Git:如何撤消某个文件夹中未进行的暂存更改

来自分类Dev

在一个分支上提交更改会撤消另一个分支上未暂存的更改

来自分类Dev

Git-撤消更改

来自分类Dev

如何撤消更改?

来自分类Dev

撤消python中的更改

来自分类Dev

撤消SVN更改列表

来自分类Dev

撤消setfacl更改

来自分类Dev

撤消对宝石来源的更改

来自分类Dev

如何还原SharedPreferences更改?

来自分类Dev

如何合并还原的更改?

来自分类Dev

还原.filter更改

来自分类Dev

Solr RELOAD更改/还原架构更改

来自分类Dev

撤消合并/重置更改状态

来自分类Dev

撤消取消按钮上的更改

来自分类Dev

如何撤消QAbstractTableModel中的更改?

来自分类Dev

Qtreewidget-撤消选择更改

来自分类Dev

撤消合并/重置更改状态

来自分类Dev

如何撤消PATH更改?(CentOS的)

来自分类Dev

撤消导致崩溃的文件更改

来自分类Dev

如何撤消.profile中的更改?