148

当我想取消暂存文件时,我所有的 Git 教程都显示如下内容:

$ git add *
$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    README.md -> README
    modified:   CONTRIBUTING.md

此提示告诉我们git reset用于取消暂存文件。

但相反,在我的终端中,我看到:

git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
    renamed:    cat.js -> catcat.js
    renamed:    tolendo.gogo -> tolendo.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    readme (copy).md
    tolendo (copy).txt
    zing (copy).html

我的终端告诉我使用git restore --staged,但教程以及Git 的网站告诉我使用git reset HEAD.

我对新restore命令一无所知。我试图谷歌找到和之间的区别git resetgit restore但似乎没有什么适合我的问题。

4

3 回答 3

118

我已经在“如何从工作目录而不是暂存区重置所有文件?git restore ”中介绍了(仍然标记为“实验性”),以及最近的 Git 2.23(2019 年 8 月)。

它有助于git checkout分成两个命令:

重置、恢复和还原文档状态:

有三个具有相似名称的命令git resetgit restoregit revert

  • git-revert是关于进行新的提交,以恢复其他提交所做的更改。
  • git-restore是关于从索引或另一个提交恢复工作树中的文件。
    此命令不会更新您的分支。
    该命令还可用于从另一个提交恢复索引中的文件。
  • git-reset是关于更新您的分支,移动提示以便在分支中添加或删除提交。此操作更改提交历史记录。
    git reset也可用于恢复索引,与git restore.

所以:

恢复索引中的文件以匹配 HEAD 中的版本(这与使用相同git-reset

git restore --staged hello.c

或者您可以同时恢复索引和工作树(这与 using 相同git-checkout

git restore --source=HEAD --staged --worktree hello.c

或者更实用但可读性较差的简短形式:

git restore -s@ -SW hello.c

在 Git 2.25.1(2020 年 2 月)中,“ git restore --staged”没有正确更新缓存树结构,导致之后要写入虚假树,已更正。

讨论

请参阅Jeff King ( ) 的commit e701bab(2020 年 1 月 8 日(由Junio C Hamano 合并 -- --提交 09e393d中,2020 年 1 月 22 日)peff
gitster

restore: 使用 --staged 删除条目时使缓存树无效

报告人:Torsten Krah
签字人:Jeff King

当“ git restore --staged”删除索引中的路径时,它会标记条目,CE_REMOVE,但我们不会做任何事情来使缓存树无效。
在非分阶段的情况下,我们最终会在checkout_worktree()中调用remove_marked_cache_entries()。这实际上会从索引中删除条目,并使缓存树和未跟踪缓存无效。

但是--staged,我们从不调用checkout_worktree(),并且CE_REMOVE条目仍然存在。有趣的是,当我们写出索引时它们会被删除,但这意味着结果索引是不一致的:它的缓存树与实际条目不匹配,然后git commit立即运行“”会创建错误的树。

remove_marked_cache_entries()我们可以通过在写出索引之前调用自己来解决这个问题。请注意,我们不能只是将其从checkout_worktree();中取出。该函数需要在删除条目之前迭代CE_REMOVE条目(以删除其匹配的工作树文件)。

对测试的一个好奇:如果没有这个补丁,它实际上会在运行 git-restore 时触发一个 BUG():

BUG: cache-tree.c:810: new1 with flags 0x4420000 should not be in cache-tree

但是在使用类似配方的原始问题报告中,git restore实际上创建了虚假索引(并且使用错误的树创建了提交)。我不确定为什么这里的测试与我的套房外复制的行为不同,但这里的内容应该可以捕捉到任何一种症状(并且修复可以纠正这两种情况)。


在 Git 2.27(2020 年第二季度)中," git restore --staged --worktree"现在默认从 "HEAD" 中取出内容,而不是出错。

请参阅Eric Sunshine ( ) 的提交 088018e(2020 年 5 月 5 日(由Junio C Hamano 合并 -- --提交 4c2941a中,2020 年 5 月 8 日)sunshineco
gitster

restore: 组合 --staged 和 --worktree 时默认为 HEAD

签字人:Eric Sunshine
审核人:Taylor Blau

默认情况下,文件从 索引恢复--worktree,从 HEAD恢复--staged.

--worktree--staged组合时,--source必须指定以消除还原源的歧义,从而使还原工作树和索引中的文件变得很麻烦。

(由于疏忽,该--source要求虽然记录在案,但实际上并未强制执行。)

--worktree但是,当与 结合使用时,HEAD 也是一个合理的默认值--staged,因此无论何时--staged使用(无论是否结合--worktree)都将其设为默认值。

所以现在,这有效:

git restore --staged --worktree
git restore -SW
于 2019-09-19T04:47:59.140 回答
45

为了添加VonC 的答案,并将所有相关命令按字母顺序放入图片中,我将介绍:

  • git checkout
  • git reset
  • git restore
  • git switch

我还要再扔一个,名字错git revert了。

从最终用户的角度

您只需要git checkoutgit resetgit revert。这些命令一直在 Git 中。

git checkout实际上有两种操作模式。一种模式是“安全”的:它不会意外破坏任何未保存的工作。另一种模式是“不安全的”:如果你使用它,它告诉 Git 清除一些未保存的文件,Git 假设 (a) 你知道它的意思,并且 (b) 你确实打算清除未保存的文件,所以 Git 会立即清除你未保存的文件。

这不是很友好,所以 Git 的人最终——经过多年的用户抱怨——分成git checkout了两个新的命令。这导致我们:

从历史的角度

git restore的,于 2019 年 8 月在 Git 2.23 中首次出现。 git reset很老,一直在Git,可以追溯到2005年之前。这两个命令都有能力破坏未保存的工作。

git switch命令也是新的,git restore在 Git 2.23 中引入。它实现了“安全的一半” git checkoutgit restore实现“不安全的一半”。

你什么时候使用哪个命令?

这是最复杂的部分,要真正理解它,我们需要知道以下几项:

  • Git 真的是关于提交。提交存储Git 存储库中。git pushgit fetch命令将提交——整个提交,作为一个孤注一掷的交易 1——转移另一个 Git。你要么拥有所有的承诺,要么你没有。其他命令,例如git mergeor git rebase,都适用于本地提交。该pull命令运行fetch(以获取提交),然后是第二个命令,以便在提交位于本地时对其进行处理。

  • 新提交添加到存储库。您几乎从不从存储库中删除提交。此处列出的五个命令中只有一个(checkout、reset、restore、revert 和 switch)能够删除提交。2

  • 每个提交都由其哈希 ID 编号,该 ID 对于该特定提交是唯一的。它实际上是根据提交的内容计算得出的,这就是 Git 如何使这些数字在所有 Git 中都能工作的方式。这意味着提交中的内容将永远冻结:如果您更改任何内容,您将得到一个带有新编号的新提交,而旧提交仍然存在,具有相同的旧编号。

  • 每个提交都存储两件事:快照和元数据。元数据包括一些先前提交的哈希 ID。这使得提交形成了向后看的链。

  • 分支名称包含一个提交的哈希 ID。这使得分支名称找到该提交,这又意味着两件事:

    • 该特定提交是该分支的提示提交;和
    • 导致并包括该提示提交的所有提交都该分支上。
  • 稍后我们还将讨论 Git 的索引以及您的工作树。它们与这些是分开的,但值得一提的是,特别是因为索引有三个名称:Git 有时称它为index,有时称它为staging area,有时——现在很少——称它为cache。这三个名字都指的是同一个东西。

我认为,通过分支名称的所有内容最好通过图片来理解(至少对于大多数人而言)。如果我们绘制一系列提交,较新的提交向右,使用o每个提交并省略一些提交以获取空间或其他内容,我们会得到如下内容:

        o--o---o   <-- feature-top
       /        \
o--o--o--o--...--o---o--o   <-- main
    \               /
     o--o--...--o--o   <-- feature-hull

如您所见,它是一个船库。有三个分支。主线分支包含每个 commit,包括顶行和底(外壳)行的所有提交。该feature-top分支包含前三个提交以及沿左侧主线的三个提交,但不包含底行中的任何提交。提交之间的所有连接器都是——嗯,应该是,但我没有足够好的字体——单向箭头,指向左,或向下和向左,或向上和向左。

这些“箭头”,或从提交到提交的单向连接,在技术上是有向图中的或单向边。这个有向图是一个没有循环的图,使其成为有向无环图或DAG,它具有许多对 Git 有用的属性。

如果您只是使用 Git 在提交中存储文件,那么您真正关心的是圆形o 节点顶点(同样是两个词),每个都用于存储您的文件,但您至少应该模糊知道它们是如何排列的。这很重要,尤其是因为merges。合并提交是那些具有两个输出弧的提交,向后指向 Git 所谓的两个父提交。子提交是“较晚”的提交:就像人类的父母总是比他们的孩子年长一样,Git 的父母提交也比他们的孩子年长。

不过,我们还需要一件事:新的提交从何而来? 我们注意到,提交中的内容——快照(保存所有文件)和元数据(保存 Git 保存的有关提交的其余信息)都是只读的。您的文件不仅被冻结,它们也被转换,然后转换后的数据被删除重复数据,因此即使每次提交都有每个文件的完整快照,存储库本身也保持相对较小。但这意味着提交中的文件只能被Git读取,而没有任何东西——甚至 Git 本身——可以写入给他们。它们被保存一次,并从那时起被删除重复数据。提交充当档案,几乎就像 tar 或 rar 或 winzip 或其他任何东西。

那么,要使用 Git 存储库,我们必须让 Git提取文件。这会将文件某些提交中取出,将那些特殊的存档格式的东西变成常规的、可用的文件。请注意,Git 很可能能够存储您的计算机实际上无法存储的文件:一个典型的例子是一个名为 的文件aux.h,对于某些 C 程序,在 Windows 机器上。我们不会详细介绍所有细节,但理论上仍然可以使用此存储库完成工作,该存储库可能构建在 Linux 系统上,即使您使用的是无法使用aux.h直接存档。

无论如何,假设没有像 那样令人讨厌的小惊喜aux.h,您只需运行git checkoutGitgit switch中获取一些提交。这将填充您的工作树,并从存储在某个分支的提示提交中的文件中填充它。提示提交再次是该分支上的最后一次提交,由分支名称找到。通过选择该分支名称作为当前分支,您的或选择该提交作为当前提交。您现在拥有来自该提交的所有文件,在您可以查看和处理它们的区域中:您的git checkoutgit switch工作树

请注意,工作树中的文件实际上并不在 Git 本身中。它们只是Git 中提取出来的。这很重要,因为当git checkoutGit 中提取文件时,它实际上将每个文件放在两个地方。这些地方之一是您看到和处理/使用的普通日常文件。Git 将每个文件放入的另一个位置是 Git 的index

正如我刚才提到的,索引有三个名称:索引、暂存区和缓存。所有都指的是同一件事:Git 粘贴每个文件的这些“副本”的地方。每一个实际上都是预先去重的,所以“复制”这个词有点错误,但是——不像它的其他大部分内容——Git实际上在隐藏去重方面做得很好。除非您开始使用 and 之类的内部命令git ls-files,否则您git update-index不需要了解这部分,并且可以将索引视为保存文件的副本,准备进入下一次提交

这对你作为一个只使用Git 的人来说意味着 index / staging-area 作为你提议的下一次提交。当您运行时,Git 会将文件的这些git commit副本打包为要存档在快照中的副本。您在工作树中的副本是您的;index / staging-area副本是Git 的,可以使用了。因此,如果您更改了副本并希望更改后的副本成为下一个快照中的内容,您必须告诉 Git:更新 Git 副本,在 Git 索引/暂存区域中。 你用. 3 _git addgit addcommand 意味着使建议的下一个提交副本与工作树副本匹配。它是add执行更新的命令:这是 Git 压缩和去重文件并使其准备好存档的时候,而不是在git commit时间上。4

然后,假设您有一系列以以下结尾的提交hash-N

[hash1] <-[hash2] ... <-[hashN]   <--branch

你运行git commit,给它任何它需要的元数据(一个提交日志消息),你得到一个 N+1'th 提交:

[hash1] <-[hash2] ... <-[hashN] <-[hashN+1]   <--branch

Git 自动更新分支名称以指向新的提交,因此它已被添加到分支中。

现在让我们看一下各种命令:

  • git checkout: 这是一个大而复杂的命令。

    我们已经看到了这个,或者至少看到了这个的一半。我们用它来挑选一个分支名称,因此是一个特定的提交。这种检查首先查看我们当前的提交、索引和工作树。它确保我们已经提交了所有修改过的文件,或者——这部分有点复杂——如果我们没有提交所有修改过的文件,切换到另一个分支是“安全的”。如果不安全git checkout则告诉您由于文件已修改而无法切换。如果安全,就会git checkout切换;如果您不是要切换,则可以切换回去。(另请参阅当当前分支上有未提交的更改时签出另一个分支

    但是git checkout有一个不安全的一半。假设您修改了工作树中的某个文件,例如README.mdoraux.h或其他。你现在回头看看你改变了什么,然后想:不,那是个坏主意。我应该摆脱这种变化。我希望文件恢复原样。

    要做到这一点——比如清除你的更改README.md——你可以运行:

    git checkout -- README.md
    

    这里的--部分是可选的。使用它是个好主意,因为它告诉 Git 后面的部分--文件名,而不是分支名

    假设您有一个名为的分支hello 一个名为hello. 有什么作用:

    git checkout hello
    

    意思是?我们是要求 Git 破坏文件 hello以删除我们所做的更改,还是要求 Git 检查分支 hello?为了明确这一点,您必须编写:

    git checkout -- hello        (clobber the file)
    

    或者:

    git checkout hello --        (get the branch)
    

    这种情况下,存在具有相同名称的分支和文件或目录,是一种特别阴险的情况。它已经吸引了真正的用户。这就是为什么 git switch现在存在。该git switch命令绝不意味着破坏我的文件。它只意味着做安全的那种git checkout

    (该git checkout命令也已智能化,因此如果您有新命令并且运行“坏”的 ambiguous 类型git checkout,Git 只会向您抱怨并且什么也不做。要么使用更智能的拆分命令,要么在正确的位置添加--以选择您想要的操作。)

    更准确地说,这种git checkout理想拼写是Git 将文件从 Git 的索引复制到您的工作树的请求。这意味着破坏我的文件。您也可以运行,在其中将提交哈希 ID 5添加到命令中。这告诉 Git 从该提交中复制文件,首先复制到 Git 的索引,然后复制到您的工作树。这也意味着破坏我的文件:不同之处在于 Git 从哪里获取它正在提取的文件的副本。git checkout -- pathsgit checkout tree-ish -- paths

    如果您git add在某个文件上运行并因此将其复制到 Git 的索引中,您需要从当前提交中取回它。Git索引中的副本是您编辑的副本。所以这两种形式的,带有提交哈希 ID(或 name )、可选的 和文件名,是我的文件形式的不安全破坏者。git checkout HEAD -- filegit addgit checkoutHEAD--

  • git reset: 这也是一个大而复杂的命令。

    根据您的计算方式,最多有五到六种不同形式的git reset. 我们将在这里集中讨论一个较小的子集。

    • git reset [ --hard | --mixed | --soft ] [ commit ]

      在这里,我们要求 Git 做几件事。首先,如果我们给出一个commit参数,例如HEADorHEAD~3或一些这样的,我们选择了Git 应该重置为的特定提交。这是一种通过将提交从分支末端弹出来删除提交的命令。在此处列出的所有命令中,这是唯一删除任何提交的命令。另一个命令 - <code>git commit --amend - 具有在放置新替换时弹出最后一个提交的效果,但该命令仅限于弹出一个提交。

      让我们用一张图来展示它。假设我们有:

      ...--E--F--G--H   <-- branch
      

      也就是说,这个名为 的分支branch以四个提交结束,我们将按顺序调用其哈希 ID EFGH。该名称branch当前存储这些提交中最后一个的哈希 ID H,. 如果我们使用git reset --hard HEAD~3,我们是在告诉 Git 弹出最后三个提交。结果是:

             F--G--H   ???
            /
      ...--E   <-- branch
      

      该名称branch现在选择 commit E,而不是 commit H。如果我们没有写下(在纸上、在白板上、在文件中)最后三个提交的哈希 ID,它们就会变得有点难以找到。Git 确实提供了一种再次找到它们的方法,有一段时间,但大多数情况下它们似乎已经消失了

      HEAD~3命令的一部分是我们如何选择删除最后三个提交。它是 Git 中整个子主题的一部分,记录在gitrevisions 手册中,关于命名特定提交的方法。重置命令只需要实际提交的哈希 ID 或任何等效项,HEAD~3这意味着返回三个第一父步骤,在这种情况下,这使我们从提交H返回到提交E

      --hard部分是我们git reset如何告诉 Git 如何处理 (a) 它的索引和 (b) 我们的工作树文件。我们在这里有三个选择:

      • --soft告诉 Git:别管了。Git 将移动分支名称而不触及索引或我们的工作树。如果您git commit现在运行,索引中(仍然)的任何内容都会进入提交。如果索引与 commit 中的快照匹配H,这将为您提供一个新的提交,其快照H,但其父级E,就好像所有提交FH被折叠成一个新提交一样。人们通常称之为挤压

      • --mixed告诉 Git:重置你的索引,但不要管我的工作树。Git 将移动分支名称,然后将索引中的每个文件替换为新选择的 commit 中的文件。但是Git 会留下你所有的工作树文件。这意味着就 Git 而言,您可以开始git adding 文件以进行新的提交。您的新提交将不匹配H,除非您使用git add 所有内容,因此这意味着您可以,例如,E+F如果您愿意,可以构建一个新的中间提交,类似或类似的东西。

      • --hard告诉 Git:重置你的索引我的工作树。 Git 将移动分支名称,替换其索引中的所有文件,并替换工作树中的所有文件,所有这些都是一件大事。现在就好像您根本没有进行这三个提交。您不再拥有来自F、 或G、 或H的文件:您拥有来自 commit 的文件E

      请注意,如果您省略了commit这种 (hard/soft/mixed)的部分reset,Git 将使用HEAD. 由于HEAD命名当前提交(由当前分支名称选择),因此分支名称本身保持不变:它仍然选择与以前相同的提交。所以这只对--mixedor有用--hard,因为git reset --soft没有提交哈希 ID,意味着不要移动分支名称,不要更改 Git 的索引,也不要触摸我的工作树。这就是这种方法git reset可以做的三件事——移动分支名称、更改 Git 索引中的内容以及更改工作树中的内容——而你只是排除了所有这三件事。Git 什么都不做也没关系,但何必呢?

    • git reset [ tree-ish ] -- path

      这是git reset我们将在这里关心的另一种类型。这有点像混合重置,因为它意味着破坏文件的一些索引副本,但在这里您指定要破坏的文件。这也有点不像混合重置,因为这种git reset永远不会移动分支名称。

      相反,您选择要从某处复制的文件。某处tree-ish你给的;如果您不提供,则某处HEAD,即当前提交。这只能将建议的下一次提交中的文件恢复为它们在某些现有提交中的形式。通过默认为当前现有的提交,这种具有撤消. 6git reset -- pathgit add -- path

      还有其他几种形式git reset。要了解它们的含义,请查阅文档

  • git restore: 这从git checkout.

    基本上,这与各种形式的git checkout破坏git reset文件(在您的工作树和/或 Git 的索引中)的作用相同。它比旧的-and-clobber-my-work 变体更智能,因为您可以在一个命令行中选择文件的来源去向git checkout

    做你过去常做的事,你只需要跑。(在大多数情况下,您可以省略该部分,与.git checkout -- filegit restore --staged --worktree -- file--git checkoutgit add-whatever

    做你过去常做的事,你只需要跑。也就是说,您告诉从暂存区/索引复制,这是如何操作的。git reset -- filegit restore --staged -- filegit restoreHEADgit reset

    请注意,您可以将某个现有提交中的文件复制到 Git 的索引,而无需触及该文件的工作树副本:这样做。你不能对 old这样做,但你可以对 old 这样做, as 。而且,您可以将某个现有提交中的文件复制到您的工作树,而无需触及暂存副本:这样做。重叠部分(恢复和重置)因为是新的而存在,这种恢复是有道理的;也许,理想情况下,我们应该总是在这里使用,而不是使用旧的做事方式,但 Git 试图保持向后兼容性。git restore --source commit --staged -- filegit checkoutgit resetgit reset commit -- filegit restore --source commit --worktree -- file git restoregit restoregit reset

    新的能力——从任意源复制到你的工作树,而不接触 Git 的索引/暂存区副本——就是这样:新的。你以前做不到。(您之前可以运行,但这超出了我们要检查的五个命令。)git show commit:path > path

  • git switch:这只是做“安全的一半” git checkout。这就是你需要知道的一切。使用git switch, without --force, Git 不会覆盖你未保存的工作,即使你打错了或其他什么。旧git checkout命令可能会覆盖未保存的工作:如果您的拼写错误将分支名称转换为文件名,例如,哎呀。

  • git revert(为了完整起见,我添加了这个):这会产生一个新的 commit。新提交的重点是撤销某人在某些现有提交中所做的事情因此,您需要命名恢复应该退出的现有提交。这个命令可能应该被命名为git backout.

    如果您退出最近的提交,这会恢复到最近的第二个快照:

      ...--G--H   <-- branch
    

    变成:

      ...--G--H--Ħ   <-- branch
    

    其中 commit Ħ(H-bar)“撤消”提交H,因此给我们留下了与commit相同的文件G。但是我们不必撤消最近的提交。我们可以采取:

      ...--E--F--G--H   <-- branch
    

    并添加一个Ǝ撤消的提交E以获取:

      ...--E--F--G--H--Ǝ   <-- branch
    

    这可能与任何先前提交的源快照不匹配!


1 Git 正在慢慢地发展一种“部分获取”提交的工具,这样您就可以处理具有大量提交的大型存储库,而不必一次等待整个提交。现在这不是普通用户会看到的,而当它涉及到普通用户时,它意味着作为提交的基本“全有或全无”模式的附加组件。它将把这从“你要么有一个提交,要么没有”变成“你有一个提交——要么全部,要么部分承诺很快交付其余部分——或者没有;如果你有一部分提交,您可以使用该部件,但仅此而已”。

2即使那样,“已删除”的提交还没有消失:您可以将其取回。不过,这个答案不会涵盖如何做到这一点。另外,git commit --amend是一个特殊情况,我们会提到,但在这里并没有真正涵盖。

3要从工作树Git 的索引中删除文件,您可以使用git rm. 如果您从工作树中删除文件,然后git add在该文件名上运行,Git 将“添加”删除,因此也可以。

4如果您使用git commit -a,届时 Git 将git add在所有文件上运行。这是以一种棘手的方式完成的,可能会破坏一些编写不佳的预提交钩子。我建议学习两步过程,部分原因是那些写得很糟糕的钩子——尽管我会尽量避免或修复它们——部分原因是如果试图避免像那些作者那样学习 Git 的索引写得不好的钩子确实如此,Git 以后会给你带来更多麻烦。

5这是一个而不是提交的原因是你可以使用任何指定一些现有的内部 Git对象的东西。但是,每个提交都有一个保存的快照,它适用于此处,并且是您通常放在这里的内容。

6与所有其他 Git 命令一样,您可以--在命令和路径之间使用add添加。这实际上是一个好习惯,因为这意味着你可以添加一个名为的路径-u,如果你有这样的路径:git add -- -u意味着添加名为的文件-u,但git add -u根本不意味着。当然,与名称与分支名称匹配的文件相比,名称与选项序列匹配的文件不太常见,也不令人惊讶:拥有一个dev分支一组名为dev/whatever. 由于文件路径将使用目录匹配,对于添加、签出、重置和恢复,这些可能会混淆。该add命令不采用分支名称不过,因此在这方面更安全。

于 2021-02-22T01:45:53.040 回答
28

对于您的第一个问题“什么是 git-restore?”:

git-restore 是一个恢复未提交更改的工具。未提交的更改是:a) 工作副本中的更改,或 b) 索引(也称为暂存区)中的内容。

该命令是在 git 2.23 中引入的(与 git-switch 一起),用于分离以前在 git-checkout 中合并的多个关注点。

git-restore 可以在三种不同的模式下使用,具体取决于您是希望在工作副本、索引中恢复工作,还是两者兼而有之。

git restore [--worktree] <file>用索引 (*) 中的内容覆盖工作副本中的 <file>。换句话说,它会还原您在工作副本中所做的更改。你是否指定--worktree并不重要,因为如果你不另外说,它是暗示的。

git restore --staged <file>用本地存储库中的当前 HEAD 覆盖索引中的 <file>。换句话说,它取消了以前上演的内容。到目前为止,它确实相当于旧的git reset HEAD <file>

要使用当前 HEAD 覆盖工作副本和索引,请使用git restore --staged --worktree --source HEAD <file>. 此版本同时执行以下操作:将您的工作副本恢复为 HEAD 并取消暂存以前暂存的工作。

对于您的第二个问题“git-restore 和 git-reset 有什么区别?”:

这两个命令之间存在重叠和差异。

两者都可用于修改您的工作副本和/或暂存区域。但是,只有 git-reset 可以修改您的存储库。从这个意义上说,如果您只想恢复本地工作, git-restore 似乎是更安全的选择。

还有更多的区别,我在这里无法一一列举。

(*) 未add编入索引的文件仍被视为在索引中,但在当前 HEAD 修订版中处于“干净”状态。

于 2020-01-31T14:16:36.810 回答