社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Git

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

mvo • 5 年前 • 1164 次点击  

当我试图理解撤销各种git操作的方法时,我想到了一个不确定如何处理它的场景。免责声明:我在实际与git“生产中”合作时没有这种情况,但我仍然认为这不仅仅是一个学术问题。

让我们看看下面的场景

  • 操作先前提交的文件: echo "some content" >> example.txt
  • 准备更改: git add example.txt
  • 签出上次提交的更改: git checkout @ -- example.txt
  • 意识到您选择了错误的文件,并且希望撤消最后一个命令以获取更改( "some content" )回来

我想在引擎盖下发生的事

每次转移更改时 git add blob对象是在 .git/对象/ 以及索引文件( .git/索引 )得到更新。如果我多次更改和添加内容,将有多个blob。旧的没有立即被垃圾收集。

当从上面运行checkout命令时,索引会立即得到更新(我还假设内容只在我的工作目录中,但未分页)。这样参考就不见了,我不能用 git checkout-index 还原它们。

除非垃圾收集在技术上仍然存在。但我不知道怎样才能把它弄回来,然后手动地寻找散列,并用 git cat-file . 跑步也是如此 Git添加 很多次,尽管在这里想要返回先前的阶段性更改可能不是一个真正的用例。(或者当从储藏室弹出变化时?……)


所以所有这些归结为以下问题:

  • 有什么像 git reflog 为了索引?
  • git checkout @ -- file 被认为是像git这样危险的命令 reset --hard 你在哪里可能会放松工作?

如果答案是“不”或“是”(我目前的推测):

  • 有手动更改/重写索引的管道命令吗?(见上例,物体仍在那里)

奖励:有没有其他方法可以在不立即转移文件的情况下签出单个文件?

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/39958
 
1164 次点击  
文章 [ 1 ]  |  最新文章 5 年前
torek
Reply   •   1 楼
torek    6 年前

你的描述基本上是正确的。唯一不是百分之百与这部分有关的是:

每次转移更改时 git add blob对象是在 .git/对象/

在内部, Git添加 哈希工作树文件中数据的内容 git hash-object -w -t blob . 这不是 必要地 创建 新的 对象:如果哈希内容是 已经在存储库中 ,它只是重新使用现有对象。现有对象可能是 拥挤的 ,即,在 .git/objects/pack ,而不是 释放 作为一个独立的整体。

此外,写入blob对象的内容 可以 任意地 与工作树中的内容不同,因为 清洁过滤器 . 由于行结束设置的原因,更常见的情况是,CR LF行尾与工作树中的内容不同。通过您的 .gitattributes 文件,部分(或大部分)通过配置中的设置。

在任何情况下,重要的是获得blob对象的hash id。blob对象肯定存在于 .git/objects 作为松散对象或在包文件中的目录。现在 Git添加 可以写入 .git/index (或其他任何文件 GIT_INDEX_FILE 指示):它将在临时槽0的索引中存储给定 path ,使用计算的blob散列和模式 100644 100755 取决于以后是否应将工作树文件标记为可执行。

如果你失去了它,你多半是运气不好

[场景被截取,但以 git checkout HEAD -- path 用它的 $path 代表 $blobhash 和模式 $mode 信息, 在中删除文件的工作树副本 路径 )

除非垃圾收集在技术上仍然存在。但我不知道怎样才能把它弄回来,然后手动地寻找散列,并用 git cat-file

实际上,您不能:哈希ID计算是 trapdoor function ,只有当你 有散列可以让git溢出内容,但如果没有散列,就需要有内容。那是你的 Catch-22 situation

如果 这是一个相当重要的“如果”的内容 独一无二,所以 Git添加 真的创造了 新的 blob对象, 您刚刚覆盖了索引中的blob引用,该blob对象确实不再在任何地方被引用。另一方面,如果 git hash-object -w 在重用某些现有blob时,blob对象仍然被以前引用它的任何对象引用。所以现在有两个有趣的例子:blob 唯一的,现在可以进行垃圾收集,或者,blob是 独一无二的。

使用 git fsck --lost-found git fsck --unreachable git fsck --dangling (默认情况下),您可以让git遍历整个对象数据库,确定哪些对象是 可达成的 不是的,告诉你一些或所有无法到达的,和/或从他们那里复制或关于他们的信息到 .git/lost-found . 如果blob对象 遥不可及,它 被列为这些无法到达或悬挂的斑点之一,或将其内容还原为 .git/失物招领 .

这里的缺点是可能有几十个甚至几百个悬空的blob对象。现在你的任务已经从“猜杂烩”(实际上是不可能的)变成了“大海捞针”(不是那么困难,但是很乏味,你很可能会发现 错误的 针线不是大海捞针,毕竟是一堆针)。当然,这只适用于“blob是唯一的”情况。

特定问题的答案

(顺便说一下,这个问题 不是 真的是 Can git undo a checkout of unstaged files . 但那本书还是很有用的,所以你也可以看看。)

有什么像 git reflog 为了索引?

不,你 可以 自己制作备份:只是 cp .git/index 在某个地方。但Git不会自己这么做。你可以在 Git结账头-- 路径 操作,通过用于执行此类危险操作的别名或shell函数。

注意git不知道这些备份副本,所以 git gc 不考虑引用的对象受保护。将备份与管道命令一起使用的步骤 git ls-files ,将路径名放入 git_索引文件 在命令执行期间。

git checkout @ -- 被认为是危险命令的文件,如 git reset --hard 你可能会失去工作的地方?

答案取决于谁在考虑。我建议你自己也考虑一下,因为你问的是这个问题。-)

有手动更改/重写索引的管道命令吗?(见上例,物体仍在那里)

对: git update-index 是一次一项更新程序(使用 --cacheinfo --stdin 提供原始索引项数据,而不是让它重复很多 Git添加 工作)。许多其他命令也会部分或整体更新索引。

如果您有一个在 git checkout HEAD -- ... 操作,可以从备份索引中读取条目(使用 GIT_INDEX_FILE=... git ls-files 例如)然后使用 git更新索引 , 没有 git_索引文件 设置,将信息放入常规索引中。当然,这是一个index-overwrite-y操作,您可能希望首先对索引进行另一次备份。

有没有另一种方法来签出单个文件而不立即暂存它?

不,只是因为动词 结帐 在这里。到 查看内容 在索引中或在任何commitso中的文件,其内容的名称为 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重置 一个目录的路径,它重置所有已经在索引中并且位于目录中的文件。