什么是“git restore”命令?“git restore”和“git reset”有什么区别?

What is the `git restore` command and what is the difference between `git restore` and `git reset`?

当我想取消暂存文件时,我的所有 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’s website 告诉我使用 git reset HEAD.

我不知道新的 restore 命令。我尝试 Google 找出 git resetgit restore 之间的区别,但似乎没有什么符合我的问题。

我在“”中展示了git restore(仍然标记为"experimental"),最近Git 2.23(2019年8月)。

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

  • 一个文件(git restore),可以覆盖git reset个案例。
  • 一个用于分支 (git switch, as seen in "Confused by git checkout"),它只处理分支,不处理文件。

reset, restore and revert 文档所述:

There are three commands with similar names: git reset, git restore and git revert.

  • git-revert is about making a new commit that reverts the changes made by other commits.
  • git-restore is about restoring files in the working tree from either the index or another commit.
    This command does not update your branch.
    The command can also be used to restore files in the index from another commit.
  • git-reset is about updating your branch, moving the tip in order to add or remove commits from the branch. This operation changes the commit history.
    git reset can also be used to restore the index, overlapping with git restore.

所以:

To restore a file in the index to match the version in HEAD (this is the same as using git-reset)

git restore --staged hello.c

or you can restore both the index and the working tree (this the same as using git-checkout)

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

or the short form which is more practical but less readable:

git restore -s@ -SW hello.c

使用 Git 2.25.1(2020 年 2 月),“git restore --staged”未正确更新缓存树结构,导致之后写入虚假树,已更正.

参见 discussion

参见 commit e701bab (08 Jan 2020) by Jeff King (peff)
(由 Junio C Hamano -- gitster -- in commit 09e393d 合并,2020 年 1 月 22 日)

restore: invalidate cache-tree when removing entries with --staged

Reported-by: Torsten Krah
Signed-off-by: Jeff King

When "git restore --staged " removes a path that's in the index, it marks the entry with CE_REMOVE, but we don't do anything to invalidate the cache-tree.
In the non-staged case, we end up in checkout_worktree(), which calls remove_marked_cache_entries(). That actually drops the entries from the index, as well as invalidating the cache-tree and untracked-cache.

But with --staged, we never call checkout_worktree(), and the CE_REMOVE entries remain. Interestingly, they are dropped when we write out the index, but that means the resulting index is inconsistent: its cache-tree will not match the actual entries, and running "git commit" immediately after will create the wrong tree.

We can solve this by calling remove_marked_cache_entries() ourselves before writing out the index. Note that we can't just hoist it out of checkout_worktree(); that function needs to iterate over the CE_REMOVE entries (to drop their matching worktree files) before removing them.

One curiosity about the test: without this patch, it actually triggers a BUG() when running git-restore:

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

But in the original problem report, which used a similar recipe, git restore actually creates the bogus index (and the commit is created with the wrong tree). I'm not sure why the test here behaves differently than my out-of-suite reproduction, but what's here should catch either symptom (and the fix corrects both cases).


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

参见 commit 088018e (05 May 2020) by Eric Sunshine (sunshineco)
(由 Junio C Hamano -- gitster -- in commit 4c2941a 合并,2020 年 5 月 8 日)

restore: default to HEAD when combining --staged and --worktree

Signed-off-by: Eric Sunshine
Reviewed-by: Taylor Blau

By default, files are restored from the index for --worktree, and from HEAD for --staged.

When --worktree and --staged are combined, --source must be specified to disambiguate the restore source, thus making it cumbersome to restore a file in both the worktree and the index.

(Due to an oversight, the --source requirement, though documented, is not actually enforced.)

However, HEAD is also a reasonable default for --worktree when combined with --staged, so make it the default anytime --staged is used (whether combined with --worktree or not).

所以现在,这有效:

git restore --staged --worktree
git restore -SW

第一个问题"What is git-restore?":

git-restore 是一种还原未提交更改的工具。未提交的更改是:a) 工作副本中的更改,或 b) 索引中的内容(a.k.a。暂存区)。

此命令在 git 2.23 中引入(与 git-switch 一起)以分离以前在 git-checkout 中联合的多个问题。

git-restore 可以在三种不同的模式下使用,具体取决于您是否喜欢在工作副本、索引或两者中还原工作。

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

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

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

你的第二个问题"What's the difference between git-restore and git-reset?":

这两个命令之间有重叠,也有区别。

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

差异比较多,这里就不一一列举了

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

要添加到 ,并将所有相关命令纳入图片,按字母顺序排列,我将介绍:

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

我再放一个,名字不对的 git revert

从end-user的角度来看

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

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

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

从历史的角度来看

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

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

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

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

  • Git 实际上就是 提交 。提交存储在 Git 存储库中。 git pushgit fetch 命令 t运行sfer commits——整个提交,作为 all-or-nothing deal1——给另一个Git。您要么拥有所有提交,要么没有。其他命令,例如 git mergegit rebase,都适用于 local 提交。 pull 命令 运行s fetch (获取提交)后跟第二个命令,一旦它们是本地的就处理提交。

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

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

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

  • A b运行ch name 保存了一次提交的哈希 ID。这使得 b运行ch name find 提交,这反过来意味着两件事:

    • 该特定提交是该 b运行ch 的 提示提交;和
    • 导致并包括该提示提交的所有提交都在 on that b运行ch.
  • 稍后我们还将讨论 Git 的 index,以及您的 working tree。它们与这些不同,但值得提早提及,尤其是因为索引具有三个名称:Git 有时称其为 index,有时称其为 staging area,有时(现在很少见)将其称为 cache。这三个名字指的是同一个东西。

b运行ch name 开始的所有内容,我认为最好通过图片来理解(至少对大多数人而言)。如果我们绘制一系列提交,较新的提交向右,每次提交使用 o 并省略 space 或其他任何内容的一些提交,我们会得到这样的结果:

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

如您所见,这是一个船库。一共有三个 b运行ches。主线 b运行ch 包含 每个提交 ,包括顶行和底行(外壳)上的所有提交。 feature-top b运行ch 包含前三个提交以及左侧主线上的三个提交,但不包含底行的任何提交。 between commits 的所有连接器都是——好吧,应该是 但我没有足够好的字体——one-way 箭头,指向左,或down-and-left,或up-and-left。

这些“箭头”,或者一种连接方式从提交到提交,在技术上是 arcs, or one-way edges, in a directed graph. This directed graph is one without cycles, making it a Directed Acyclic Graph or DAG,它有一堆对 Git.

有用的属性

如果您只是使用 Git 在提交中存储文件,那么您真正关心的是轮次 o nodes or vertices (again two words for the same thing),每个轮次都用于存储您的文件,但你至少应该隐约知道他们是如何被争论的运行。这很重要,尤其是因为 合并 。合并提交是那些有两个传出弧的提交,向后指向 Git 调用的两个 parent 提交 。 child 提交是“较晚”的提交:正如人类 parent 总是比他们的 children 更老一样,Git parent 提交也比他们的更早child仁.

不过,我们还需要一件事:新提交来自哪里?我们注意到提交中的内容——快照、保存所有文件和元数据, 保留有关提交的其余信息 Git — 全部 read-only。您的文件不仅被冻结,而且还 t运行sformed,然后 t运行sformed 数据 de-duplicated,因此即使每次提交都有 每个 文件的完整快照,存储库本身仍然相对较小。但这意味着 in 的文件只能被 Git 读取 read,而 nothing[=573] =]——甚至 Git 本身也不能 给他们。他们得救一次,从此就是de-duplicated。提交充当档案,几乎像 tar 或 rar 或 winzip 或其他任何东西。

要使用 Git 存储库,我们必须 Git 提取 文件。这会将一些提交的文件 out ,将那些特殊的 archive-formatted 东西变成常规的可用文件。请注意,Git 可能能够存储您的计算机字面上 不能 存储的文件:一个典型的例子是一个名为 aux.h 的文件,对于某些 C 程序, 在 Windows 机器上。我们不会详细介绍所有细节,但理论上仍然可以使用这个存储库完成工作,它可能是在 Linux 系统上构建的,即使你在 Windows 系统上也是如此你不能直接使用 aux.h 文件。

无论如何,假设没有像 aux.h 这样令人讨厌的小惊喜,你只需 运行 git checkoutgit switch 来获得一些提交 out 个 Git。这将填充您的工作树,从存储在某些b运行ch 的提示提交 中的文件填充它。 tip commit 再次是 last 提交 b运行ch,由 b 发现运行通道名称。您的 git checkoutgit switch 选择该提交作为 当前提交 ,方法是选择 b运行ch 名称作为 当前b运行ch。您现在拥有所有提交的 来自 的文件,在您可以看到并处理它们的区域:您的 工作树 .

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

刚才我也提到了,索引有三个名字:索引、暂存区、缓存。所有指的是同一件事:Git 位置粘贴每个文件的这些“副本”。每个实际上都是de-duplicated之前的,所以“复制”这个词有点不对,但是——不像它的大部分其他内部结构——Git实际上很好地隐藏了de-duplication方面。除非你tar t 进入像 git ls-filesgit update-index 这样的内部命令,否则你不需要知道这部分,并且可以将索引视为持有文件,准备进入下一次提交

这一切对你来说意味着 使用 Git 是索引 / staging-area 作为你的 提议的下一个提交。当你运行git commit时,Git将打包这些个文件副本作为要在快照中存档的副本。您在工作树中拥有的副本是 您的; index / staging-area 副本是 Git的,准备出发。因此,如果您更改您的副本并希望更改副本成为下一个快照中的副本,您必须告诉Git: 更新 Git 副本,在 Git 索引 / staging-area. 你用 git add.3 git add 命令意味着 使 proposed-next-commit 副本匹配 working-tree 副本 。这是执行更新的 add 命令:这是 Git 压缩和 de-duplicates 文件并使其准备好存档的时候,而不是在git commit次。4

然后,假设您有一系列以 hash-N:

结尾的提交
[hash1] <-[hash2] ... <-[hashN]   <--branch

你 运行 git commit,给它任何它需要的元数据(提交日志消息),然后你得到第 N+1 次提交:

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

Git 自动更新 b运行ch name 以指向 new commit,它有因此 添加到 b运行ch.

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

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

    我们已经看过这个,或者至少 一半。我们用它来挑选出一个 b运行ch 名称,并因此挑选出一个特定的提交。这种检查首先查看我们当前的提交、索引和工作树。它确保我们已经提交了所有修改过的文件,或者——这部分变得有点复杂——如果我们还没有提交所有修改过的文件,切换到另一个b运行ch 是“安全的”。如果 安全,git checkout 会告诉您由于修改了文件而无法切换。如果安全,git checkout会切换;如果你不想切换,你可以切换回来。 (另请参阅 Checkout another branch when there are uncommitted changes on the current branch

    但是 git checkout 有一个 不安全的 一半。假设您修改了工作树中的某些文件,例如 README.mdaux.h 或其他任何文件。您现在回顾一下您所做的更改并思考:不,那是个坏主意。我应该摆脱这种变化。我希望文件恢复原样。

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

    git checkout -- README.md
    

    这里的--部分是可选的。使用它是个好主意,因为它告诉 Git -- 之后的部分是 文件名 ,而不是 b 运行通道名称.

    假设您有一个 b运行ch 名为 hello 文件 名为 hello。什么:

    git checkout hello
    

    是什么意思?我们是要求 Git 破坏 文件 hello 以删除我们所做的更改,还是要求 Git 检查 [=350] =]b运行chhello?为了明确这一点,您必须这样写:

    git checkout -- hello        (clobber the file)
    

    或:

    git checkout hello --        (get the branch)
    

    这种情况下,有b运行ches和同名的文件或目录,是一个特别阴险的案例。它咬了真正的用户。 为什么 git switch 现在存在。 git switch 命令 永远不会破坏我的文件 。它只意味着做安全的git checkout

    (git checkout 命令也变得更聪明了,所以如果你有新命令并且你 运行 那种“坏”的模棱两可 git checkout,Git 只会抱怨你什么都不做。要么使用更聪明的 split-up 命令,要么在正确的地方添加 -- 来选择你想要的操作类型。)

    更准确地说,git checkout 这种 ,最好拼写为 git checkout -- <em>paths</em>,是请求 Git 将文件从 Git 的索引复制到您的工作树。这意味着 破坏了我的文件 。您还可以 运行 git checkout <em>tree-ish</em> -- <em>paths</em>,在其中向命令添加提交哈希 ID5。这告诉 Git 从那个提交复制文件,首先到 Git 的索引,然后到你的工作树。这也意味着 破坏我的文件: 区别在于 Git 获取正在提取的文件的副本。

    如果你 运行 git add 在某个文件上,然后将其复制到 Git 的索引中,你需要 git checkout HEAD -- <em>file</em> 从当前提交中取回。 Git 的 index 中的副本是您 git add 编辑的副本。所以这两种形式的 git checkout,带有提交哈希 ID(或名称 HEAD),可选的 --,和文件名,是不安全的 clobber my文件 表格。

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

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

    • git 重置 [ --hard | --混合 | --soft] [<em>提交</em>]

      在这里,我们要求 Git 做几件事。首先,如果我们给出一个 commit 参数,例如 HEADHEAD~3 或类似的参数,我们选择了一个特定的 承诺 Git 应该重置为。这是一种通过将提交从 b运行ch 末尾弹出来 删除提交 的命令。在此处列出的所有命令中,这是唯一一个删除任何提交的命令。另一个命令——git commit --amend——具有弹出 last 提交的效果,同时换上一个新的替代品,但那个替代品仅限于弹出 一个 提交。

      让我们将其显示为绘图。假设我们有:

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

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

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

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

      此命令的 HEAD~3 部分是我们选择删除最后三个提交的方式。它是 Git 中整个 sub-topic 的一部分,记录在 the gitrevisions manual 中,关于命名特定提交的方法。重置命令只需要实际提交的哈希 ID,或任何等效的东西,HEAD~3 意味着 返回三个 first-parent 步骤 ,在这种情况下让我们从提交 H 回到提交 E.

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

      • --soft 告诉 Git:两个都不要管。 Git 将移动 b运行ch name 而不会触及索引或我们的工作树。如果你现在 运行 git commit,索引中(仍然)的内容就是进入 new 提交的内容。如果索引与提交 H 中的快照匹配,这将为您提供一个新提交,其 snapshotH,但其 parentE,就好像 FH 的提交都已折叠成一个新的提交。人们通常称之为 squashing.

      • --mixed 告诉 Git:重置你的索引,但不要管我的工作树。 Git 将移动 b运行ch 名称,然后 将索引中的每个文件替换为新选择的提交 中的文件。但是 Git 将单独保留所有 working tree 文件。这意味着就 Git 而言,您可以 start git adding 文件来进行新的提交。你的新提交不会匹配 H 除非你 git add everything,所以这意味着你可以,例如,构建一个新的中间提交,有点像 E+F 或者什么的,如果你想要的话。

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

        的文件

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

    • git重置[<em>tree-ish</em>]--<em>路径</em>

      这是另一种 git reset 我们会在这里关心的。它有点像混合重置,因为它意味着破坏一些文件的索引副本,但在这里你指定破坏哪些文件。它也有点不像混合重置,因为这种git reset永远不会移动b运行ch名称。

      相反,您可以选择要从某处复制的文件。 某处就是你给的tree-ish;如果您不提供,则 某处 HEAD,即当前提交。这只能将 提议的下一次提交 中的文件恢复为它们在 某些现有提交 中的形式。默认为 current 现有提交,这种 git 重置 - <em>path</em>具有撤消 git 添加的效果 -- <em>path</em>.6

      git reset还有其他几种形式。要了解它们的含义,请咨询the documentation.

  • git restore:这是从 git checkout.

    中分离出来的

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

    做你以前用 git checkout 做的事 -- <em>file</em>,你只是 运行 git 恢复 --staged --worktree -- <em>文件</em>。 (在大多数情况下,您可以省略 -- 部分,就像 git checkout 一样,但是养成使用它的习惯通常是明智的。就像 git add,这个命令是专门设计的这样只有名为 -whatever 的文件实际上是有问题的。)

    要做你以前用 git 重置的操作 -- <em>file</em>,你只需 运行 git 恢复 --staged -- <em>文件</em>。也就是说,您告诉 git restoreHEAD 复制到暂存区/索引,这就是 git reset 的操作方式。

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

    新功能——从任意源复制到你的工作树,而不触及 Git 的索引/staging-area 副本——就是这样:新的。你以前做不到。 (你可以 运行 git show <em>commit</em>:<em>path</em> > <em>path</em>,之前,但这不属于我们要检查的五个命令。)

  • git switch:这只是 git checkout 的“安全一半”。这就是您真正需要知道的全部。使用 git switch,不使用 --force,Git 不会覆盖您未保存的工作,即使您输入错误或其他任何错误。旧的 git checkout 命令可能会覆盖未保存的工作:例如,如果您的拼写错误将 b运行ch 名称转换为文件名,那么糟糕。

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

    如果您取消最近的提交,这会恢复到 second-most-recent 快照:

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

    变为:

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

    where commit Ħ (H-bar) “undoes” commit H 因此给我们留下与 commit [=] 相同的 files 99=]。但我们不必撤消 最近的 提交。我们可以采取:

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

    并添加撤消 E 的提交 Ǝ 以获得:

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

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


1Git 正在慢慢地发展一种“部分获得”提交的工具,这样您就可以处理带有大量提交的大型存储库而无需等待例如,一次性完成整个提交。现在这不是普通用户会看到的东西,当它出现在普通用户身上时,它意味着 add-on 到提交的基本“全有或全无”模式。它将把它从“你要么有一个提交,要么没有”变成“你有一个提交——要么是全部,要么是其中的一部分,并承诺很快交付其余部分——或者不交付;如果你有一部分提交,您可以使用该部分,但仅此而已。

2即便如此,“已删除”的提交还没有消失:您可以取回它。不过,这个答案不会涵盖如何做到这一点。另外,git commit --amend 是一个特例,我们会提到,但这里没有真正涵盖。

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

4如果你使用git commit -a,Git会在那个时候,运行 git add文件。这是以一种棘手的方式完成的,可以破坏一些 poorly-written pre-commit 钩子。我建议学习两步过程,部分原因是那些 poorly-written 钩子——尽管我会尽可能避免或修复它们——部分原因只是因为如果 you尽量避免像那些 poorly-written 钩子的作者那样学习 Git 的索引,Git 以后会给你带来更多麻烦。

5这是 tree-ish 而不是 commit-ish[ 的原因=573=] 是你可以使用任何指定一些现有内部 Git tree object 的东西。不过,每个提交都有一个已保存的快照,适合放在这里,通常也是您放在这里的内容。

6与所有这些其他 Git 命令一样,您 可以 add 命令和要添加的路径。这实际上是一个很好的习惯,因为这意味着你可以添加一个名为 -u 的路径,如果你有这样一个路径: git add -- -u 意味着 添加名为 [=179] 的文件=]git add -u 根本不是这个意思。当然,名称匹配选项序列的文件比名称匹配 b运行ch 名称的文件更不常见,也不那么令人惊讶:拥有一个 dev b运行ch [=350 真的很容易=]和一组名为dev/whatever的文件。由于文件路径将使用目录匹配,对于添加、检出、重置和恢复,这些可能会混淆。 add 命令不使用 b运行ch name,所以在这方面它更安全。