致命:磁盘上存在路径“.eslintrc.json”,但 'master~10' 中不存在

fatal: Path '.eslintrc.json' exists on disk, but not in 'master~10'

从开发推送到主控时,测试作业(在 Gitlab 管道中)的阶段分布式任务出现错误。 本地或者开发都没有问题。

我想了解此错误消息的实际含义,这里的“磁盘”是什么以及这个 master~10 到底是什么意思(在谷歌搜索时,我理解它的意思是“将当前推送的分支与所有分支进行比较”父级在 master 中最多提交 10",是否正确,为什么要这样做?)

如何解决这个问题,如何在本地调试?

I'd like to understand what

fatal: Path '.eslintrc.json' exists on disk, but not in 'master~10'

actually mean[s], what is "disk" here and what does this master~10 mean exactly

准确意味着我们需要从一些关键背景开始。

背景

当使用Git时,我们运行:

git checkout somebranch

或:

git switch somebranch

或者有时——CI/CD系统特别喜欢这样做——我们可能会使用:

git checkout <hash-ID>

或:

git switch --detach <hash-ID>

这些命令指示 Git 从某个特定提交中提取源代码树,并使该特定提交成为 当前提交 。该提交的哈希 ID 是我们直接给 Git 的一个 - raw 哈希 ID,如 git 开关 --detach <em>hash-ID</em> 情况——或者当我们使用分支名称时,某些分支名称暗示的情况。

(当我们do使用分支名称时,checkout或switch命令的作用是使它成为当前分支,在使其 most recenttip 提交 current commit 时。当我们使用分离的 HEAD 模式时,我们最终得到 no 分支,但是我们仍然有给定的提交作为 当前提交 。所以我们总是有一个当前提交;我们有一个当前分支名称,当且仅当我们在结帐/切换命令中使用分支名称时。)

Git 以这种方式工作的原因 是 Git 不存储 文件 ,确切地说,也不Git 真的可以与 分支 一起使用吗? Git 商店是 提交 。所有提交都具有唯一的哈希 ID:可以说,这些是它们的“真名”。每个 commit 存储文件:事实上,每个提交存储一整套 every 文件,就像你当时的形式一样(或谁)做出了那个承诺。所以这就是我们如何获取文件 out of Git:它们在每个提交中,我们选择我们想要的提交并获取这些文件。

使用原始哈希 ID 的问题在于它们又大又丑,random-looking 人类通常无法正确处理的事情。人类不喜欢或使用它们。 计算机可以使用它们——计算机擅长这类事情——但人类不会。人们喜欢 names: 分支名称,例如 mastermaindevelopfeature/tall 等。这些对人类很有效。因此,除了 snapshots-in-commits——存储为 Git 内部对象的大数据库,由哈希 ID 索引——Git 提供了一个数据库,其中 Git 存储这些名称并将每个名称配对使用单个哈希 ID。

分支名称,特别是,总是表示 最新的 提交“在”该分支上。 Git 使用每个提交中存储的 元数据 将提交本身串在一起,向后。

换句话说,每个提交,根据其唯一的(和random-looking和又大又丑和impossible-for-humans)哈希ID,存储两个事情:

  • 一个提交有一个源快照,更像是一个 tarball 或 WinZip 存档或其他什么。 (然而,与某些存档格式不同,in 快照中的文件以特殊的 Git-only 格式存储,它们不仅被压缩,而且 de-duplicated 反对 所有 次提交,包括这次提交本身。)

  • 一个提交有元数据,或者关于提交本身的信息。例如,元数据包括提交人的姓名。它们包括提交者编写的日志消息。它们包括一些 date-and-time 标记以显示 提交者何时 提交。

为了使 Git 分支工作,Git 在每个提交的元数据中包含一个 previous 提交哈希 ID 的列表。此列表通常只有一个元素长:给出此提交的 parent 提交的哈希 ID。

这些“普通提交”父哈希 ID 的结果是一个简单的线性但 backwards-looking 链。让我们画一个,用简单的单个大写字母替换真正的哈希 ID。我们将把最近的提交——我们将其哈希 ID 称为 H——放在右边,这里:

... <-F <-G <-H

提交 H 存储快照和元数据,并且在 H 的元数据中,Git 存储了另一个较早提交的哈希 ID。那个散列 ID——在我们的图表中是 G,但在现实中是一些丑陋的大东西 random-looking——是 Git 对象的大数据库中提交 G 的真实名称,所以Git 可以使用该哈希 ID 提取提交 G

提交G当然是一个提交,所以它既有快照又有元数据,在G的元数据中,有一个早期提交F的哈希ID。所以 Git 可以读取 G 来找到 F 的哈希 ID,并使用它来找到 F 本身。

提交 F 当然是一个提交,所以它既有快照又有元数据,而且......好吧,你现在应该知道这是怎么回事了。通过反复阅读一个提交,包括它的元数据,Git 可以通过历史不断备份,一次提交:从 H 我们到达 G,从那里我们到达 F,从那里我们到达(大概)E,依此类推,随着时间倒退。最终我们将到达 有史以来第一次提交,这里将是提交 A:该提交有一个 empty 列表之前的提交,因为没有之前的提交,所以Git终于可以停止倒退了。

这是存储库中的历史记录:存储库中的提交历史。我们所要做的就是以某种方式找到 last 提交 H 的原始哈希 ID,然后 Git 可以向后工作。这就是分支名称的用武之地。

为了找到提交H——最后一次提交——Git存储哈希ID名称中的最后一次提交。当该名称是 branch 名称时,我们可以将其赋予 git checkoutgit switch 并“在分支上”。由于名称包含原始哈希 ID,我们将其绘制为箭头,指向提交:

...--G--H   <-- master

因为这里的分支名称是master,名称master包含提交H的原始哈希ID,从中Git可以找到更早的提交。

如果我们创建一个 new 分支,我们开始使用指向 一些现有提交 的新名称。我们可以选择任何提交,但通常在这种情况下我们会从 H 开始:

...--G--H   <-- develop, master

当我们使用 git checkoutgit switch 选择其中一个名称时,我们会得到一个 附加的 HEAD,因此 name 是当前的 name:

...--G--H   <-- develop (HEAD), master

这意味着我们最近的 checkout/switch 是 develop。我们有所有文件 来自 提交 H 以处理/使用。

Git 提交中的文件是 read-only. de-duplication / 共享技巧之所以有效,是因为每个提交的所有部分都是完全 read-only(并且只有 Git 实际上可以 读取 存储在提交中的文件),因此 Git 必须 复制在我们可以处理或使用它们之前,将文件从 commit 中移除。将它们复制出来后,这些文件现在可供使用。 Git 此时在您的错误消息中将它们称为“磁盘上”。

鉴于我们正在 on/with 通过 develop 提交 H,如果我们现在修改一些文件的“磁盘上”副本以及 git addgit commit,我们将得到一个新提交,我们可以将其称为I,而Git将使名称 develop 指向新提交 I:

          I   <-- develop (HEAD)
         /
...--G--H   <-- master

名称 develop 现在表示 develop 上的最新提交,即提交 I。名称 master 现在表示 master 上的最新提交,它仍然是提交 H。如果我们在 develop 上进行第二次新提交,我们将得到:

          I--J   <-- develop (HEAD)
         /
...--G--H   <-- master

也就是说,名称 develop 现在指向提交 J。提交 J 指向提交 I,后者指向提交 H,后者指向提交 G,依此类推。

注意:

  • 现在有 两个 最新提交:H 是最新的(在 master 上),J 是最新的(在 develop 上)。 根据定义, 某些分支名称指向的任何提交都是该分支“上”的最新提交。 Git 通常不会这样做,但我们可以强制名称 master 指向 G 而不是 H:

           H--I--J   <-- develop (HEAD)
          /
    ...--G   <-- master
    

    如果我们这样做(有理由不这样做!)那么 G 现在是 master 上的最新提交,并且 H-I-J 仅在 develop 上提交。请注意 所有 提交都在 develop 上。如果我们再次将 master 向前移动一跳到 H,我们将回到之前的设置,提交到 H,并且 I-J 仅在 develop ].

  • 重要的是提交;分支名称只是为了帮助我们(和 Git)找到 提交。 提交永远不会改变,但分支名称会发生​​很大变化。 “在”分支上的提交集通常会随着时间的推移而增加:我们添加新的提交,and/or 我们 Git 将分支名称“向前”移动。

如果我们切换回 master,Git 将从我们的“on-disk”工作区中删除提交 J 的所有文件并放入,相反,来自提交 H 的文件。我们现在可以创建第二个功能分支:

          I--J   <-- develop
         /
...--G--H   <-- feature (HEAD), master

并进行两次 更多 提交:

          I--J   <-- develop
         /
...--G--H   <-- master
         \
          K--L   <-- feature (HEAD)

我们只能添加 提交到存储库。无法更改现有提交。新提交向后指向现有提交,因此存在历史记录。历史不过是提交,正如从某个分支名称开始并向后工作所发现的那样。提交存储文件和元数据;元数据让历史发挥作用;并且分支名称找到 latest 提交,从中Git 向后工作。

回到你的问题

what is "disk" here and what does this master~10 mean exactly

我们已经了解了“在磁盘上”的含义:Git 已经提取了一个 当前提交 ,并且通过提取文件 from 那个提交,Git 已经在你的工作树中创建了文件。您可能还创建了 new 文件,因为检出或切换操作:在这种情况下,这些文件也在“磁盘上”。

Git 已在您的工作树中找到 .eslint.json

master~10 现在可以通过我们绘制的图表进行解释。假设我们有:

          I--J   <-- develop
         /
...--G--H   <-- master (HEAD)
         \
          K--L   <-- feature

这意味着我们正在“开启”master,使用提交 H。名称 master 的字面意思 意思是 “提交 H” 每当我们在 Git 需要 commit[ 的上下文中使用它时=334=],比如我们 运行:

git diff master feature

或:

git show master

在这些情况下,我们指示 Git 查看 git diff 命令的提交 HL,或提交 H 及其git show 命令的父级 G

添加波浪号 ~ 后缀,后跟可选数字,指示 Git 向后跳转给定次数。该数字默认为 1,但这里我们有 10,因此这意味着向后移动十次。对于我们的例子来说太多了,所以让我们考虑 master~3 代替:

  • 开始于 H
  • 后退一次:我们现在在 G
  • 再次向后移动:我们现在在 F,大概(虽然我没有画)
  • 第三次后退:我们现在在 E,大概是

所以 master~3 会 select 提交 E,假设有关我没有绘制的提交的内容。

同样,feature~3 表示:

  • 开始于 L
  • 后退一次 K
  • 第二次返回H
  • 第三次移回 G

因此这意味着 提交 Gdevelop~3 将从 J 开始,后退三次,并再次在提交 G 结束。

所以这些都是拼写提交哈希 ID 而不必拼写提交哈希 ID 的方法。此外,它们 relative 到分支中的 last 提交(根据定义,因为分支名称始终表示最后一次提交 分支)。当我们在 分支上进行新提交时,分支变得越来越长,如果我们总是向后移动 3 跳(或 10 跳),我们将始终得到 3(或10) 从 last.

跳回

所以说 .eslintrc.json 不在 master~10 中的错误消息只是意味着您通过 ~10 后缀选择的任何提交都应用于 master那个提交里面没有.eslint.json 提交中的文件缺少 .eslint.json 文件。

要获取此特定消息,您可能需要 运行:

git diff master~10

这意味着 将通过 master~10 找到的提交中存储的文件与工作树(磁盘上)中的文件进行比较 .