“git rm --cached”、“git restore --staged”和“git reset”有啥区别
Posted
技术标签:
【中文标题】“git rm --cached”、“git restore --staged”和“git reset”有啥区别【英文标题】:What's the difference between 'git rm --cached', 'git restore --staged', and 'git reset'“git rm --cached”、“git restore --staged”和“git reset”有什么区别 【发布时间】:2021-04-02 16:23:02 【问题描述】:我遇到了以下三种方法来取消暂存由命令“git add”暂存的文件
git rm --cached <file>
git restore --staged <file>
git reset <file>
当我一一运行这些命令时,它们的行为看起来完全一样。 它们之间究竟有什么区别?
【问题讨论】:
【参考方案1】:两个是一样的;一个不是,除非在特殊情况下。
要理解这一点,请记住:
提交包含 Git 知道的所有文件的快照,以您说提交时的形式; 快照是从 Git 索引中的文件制作的,又名暂存区,又名缓存(同一事物的三个术语);和git add
表示 使 index/staging-area/cache 中的副本与我的工作树中的副本匹配(如果工作树副本已更新,则从工作树中复制,或者从如果工作树副本被删除,则为索引)。
因此,索引/暂存区域始终包含您的提议的下一次提交,并且最初是在您执行git checkout
时从您的当前提交播种的或git switch
以获得该提交。1 因此,您的工作树包含每个文件的第三个副本2,前两个副本为当前提交 aka HEAD
中的一个,以及索引中的一个。
考虑到这一点,您的每个命令的作用如下:
git rm --cached <em>file</em>
:从索引/暂存区中删除文件的副本,而不触及工作树副本。提议的下一次提交现在缺少该文件。如果当前提交有该文件,而实际上此时您确实进行了下一次提交,则上一次提交和新提交之间的区别在于该文件已消失。
git restore --staged <em>file</em>
:Git 将文件从HEAD
提交复制到索引中,而不涉及工作树副本。索引副本和 HEAD
副本现在匹配,无论它们之前是否匹配。现在进行的新提交将具有与当前提交相同的文件副本。
如果当前提交缺少该文件,这具有从索引中删除该文件的效果。所以在这种情况下它和git rm --cached
做同样的事情。
git reset <em>file</em>
:这会将文件的HEAD
版本复制到索引中,就像git restore --staged <em>file</em>
。
(请注意git restore
,与git reset
的这种特殊形式不同,可以覆盖某些文件的工作树副本,如果您要求它这样做。--staged
选项,如果没有 --worktree
选项,则指示它只写入索引。)
旁注:很多人最初认为索引/暂存区只包含更改,或者只包含更改的文件。情况并非如此,但如果您这样想,git rm --cached
似乎与其他两个相同。由于索引不是这样工作的,所以不是。
1当你执行某件事时,会有一些古怪的边缘情况,然后执行一个新的git checkout
。本质上,如果可以保留不同的分阶段副本,Git 会这样做。有关血腥细节,请参阅Checkout another branch when there are uncommitted changes on the current branch。
2提交的副本和任何暂存的副本实际上都以内部 Git blob 对象 的形式保存,该对象会删除重复的内容。因此,如果这两个匹配,它们实际上只是共享一个底层副本。如果暂存副本与HEAD
副本不同,但匹配任何(甚至许多)其他现有已提交副本或副本,则暂存副本与所有其他提交共享底层存储。所以称每个人为“副本”是矫枉过正的。但作为一种心智模型,它运行得很好:没有一个可以被覆盖;如果需要,一个新的git add
将创建一个新的 blob 对象,如果最后没有人使用某个 blob 对象,Git 最终会丢弃它。
【讨论】:
考虑git restore
和git reset --hard
,虽然它们都改变了工作树,但git restore
(没有选项)不会触及索引。那么我可以假设git restore --staged --worktree
与git reset --hard
相同吗?
@GordonBai:对(尽管git restore
专注于提交/the-index/your-worktree 中的单个文件,而git reset --hard
拒绝接受任何路径规范:它始终是提交范围的) .以上是关于“git rm --cached”、“git restore --staged”和“git reset”有啥区别的主要内容,如果未能解决你的问题,请参考以下文章
“git rm --cached”、“git restore --staged”和“git reset”有啥区别