git rebase:以下未跟踪的工作树文件将被检出覆盖(尽管文件被跟踪)
git rebase: The following untracked working tree files would be overwritten by checkout (though files are tracked)
尝试交互式变基时,git 说:
error: The following untracked working tree files would be overwritten by checkout:
# some files here
Please move or remove them before you switch branches.
Aborting
error: could not detach HEAD
有问题的文件集似乎因我标记为 (e)dit 的提交而异。它们都是跟踪文件,在分支中的提交中被修改,然后最终在后续提交中被删除。
为什么 git 这样做?我该如何解决?
谢谢!
Git 正在抱怨,因为文件未被跟踪。
我知道你说:
[the] files are tracked
但他们不是:你还说:
They are ... finally removed in a subsequent commit.
当您处于故障发生点时,文件已从索引中删除,但未从工作树中删除。这是 未跟踪文件的定义: 它是工作树中的文件,但不在索引中。
具体来说,这个:
error: could not detach HEAD
告诉你(或者我,无论如何)当你输入命令时,你现在签出的提示提交中的文件当前未被跟踪:
git rebase -i <start-point>
但此处由 标识的提交中包含它们。 Git 需要 git checkout
该提交,但在签出该提交时,它将用跟踪文件覆盖未跟踪文件。
如果最后一点给您带来了 record-scratch / WTF 噪音,请不要担心:它确实 令人困惑。
Git 中的索引是一个关键的小动物,经常没有得到正确的解释。要理解它,您必须意识到 Git 在任何时候都有 三 — 好吧, 最多 3 — 的副本您正在处理的每个文件 on/with。一个副本在提交中一直冻结:您现在签出的提交。第二个副本1 位于index。这就是使文件 被跟踪 的原因。第三个副本是唯一一个你可以看到 ...但Git甚至不使用它因为它是你的副本,在你的工作树中。
索引,Git 也称为 暂存区 ,包含将进入 [=79] 的每个文件的副本(再次参见脚注 1) =]你的下一次提交。因此,当您选择要处理的提交时,它最初是从您 git checkout
的提交中复制 out 的。 Git 然后复制索引副本,这是一种特殊的 Git 格式,出你的 work-tree,这是 Git 放置所有快照文件供您查看和使用。
一旦 Git 将这些复制出来,大部分就完成了。您对工作树副本进行更改,然后 运行 git add
告诉 Git: 将工作树副本复制回索引副本的顶部/上方。 (同样,参见脚注 1。)这样做的原因有点复杂,但如果你愿意,可以这样想:
- 提交中的副本是只读的,永远冻结。为了节省大量 space,Git 将它们保存为一种特殊的 Git-only 压缩格式,只有 Git 可以使用。
- 这对存档有意义,但对完成 工作 毫无用处,因此 Git 必须将文件扩展为正常格式。那是 work-tree 副本。
- 为了加快速度并方便 Git,Git 在索引中保留冻结副本(但不再冻结)。您可以通过
git add
-ing 从工作树中批量替换索引副本:Git 会将其放入冻干 格式 ,准备好已提交,在索引中(但它仍然可以在那里替换)。
您可以对工作树副本做任何您想做的事情,因为它是一个常规文件。只要工作树文件存在,它就作为文件存在。
您可以随时删除或替换索引副本。要替换它,请使用 git add
将工作树文件复制回索引中该名称下的任何内容。要完全删除它,请使用 git rm <em>file</em>
.
当您 运行 git commit
、Git 打包 索引中的所有文件 以制作新提交的快照。索引中的任何内容(以索引中的任何形式)都会进入新提交。 Git 根本不会为此使用您的工作树。这就是为什么你必须一直重新 git-add
文件的原因:Git 没有查看工作树副本。2
如果工作树文件存在并且索引副本消失,则文件未被跟踪。但是,如果文件在某个较早的 commit 中并且您检查了该提交,则较早提交的副本将 返回到 索引中,并且文件再次被跟踪。而且,当来自较早提交的副本进入索引时,Git 也会覆盖工作树中的副本——除非因为它没有保存在任何地方而将其丢弃,在这种情况下,Git说挡路了。
(如果它被跟踪,Git 知道它是否安全地保存在提交中。如果是这样,Git 可以覆盖它。)
那么,现在该怎么做应该很明显了:如果数据很重要,请将文件移到其他地方。做你的变基。完成后,文件将 消失 因为最后一次提交删除了它们。现在您可以将文件移回原处,它们将再次取消跟踪。之前有文件的提交现在仍然有文件;如果你的 rebase 将这些提交复制到新的和改进的提交中,那些新的和改进的提交仍然有文件。
关键概念:一个文件是tracked还是untracked取决于是否有索引中的副本。索引副本大部分是不可见的,3但它非常重要! 索引是一个临时区域。它主要包含您将要进行的下一个提交;当您 git checkout
提交时,Git 必须从提交中 填写 。
因此,某些文件的 tracked/untracked 状态可能会突然改变。当 Git 从 提交中填充索引 时,如果需要,它将在索引中创建一个文件 and 在工作中创建它-当时的树;或者,如果需要,它将 从工作树中删除 一个文件——如果它不在你要移动到的提交中 —— 届时,它将 删除 文件 来自 工作树。
1从技术上讲,对于每个要提交的文件,索引包含一个 对 blob 对象的引用。 blob 对象一直被冻结并且提交引用它,就像索引一样。当您 git add
一个文件时,Git 会对内容进行压缩和哈希处理,如果已经有包含该数据的 blob,Git 可以重新使用现有的 blob。否则 Git 生成一个新的并且那个 blob 的哈希值进入索引。
有时这会创建一个永远不会被使用的 blob,因为您写入一个版本,然后意识到其中有错误并写入另一个。没关系:Git 一直创建垃圾对象,在其自己的。 Git 有一个 垃圾收集器 ,它 运行 会在合理的时候清理未使用的对象。
2如果你使用git commit -a
,那会创建一个second,但是临时索引,git add
- s 所有文件到临时索引,并从临时索引提交。如果一切正常,临时索引将成为新索引;如果失败,Git 简单地删除临时索引,一切都会恢复到您 运行 git commit -a
之前的状态。 TL;DR 是 Git 仍在从索引中提交。这只是第二个额外的索引。
3git status
命令比较索引与HEAD
提交并告诉你什么是不同。因此,如果一个文件有 "gone missing",您将看到一个删除,因为 暂存提交 。然后,它分别比较索引和工作树,并告诉您不同。因此,如果一个文件在索引中但在工作树中丢失,您将看到删除为 not staged for commit.
如果文件存在于 HEAD
,但从索引中 消失 ,然后在工作中 返回 -树,您会看到一个分阶段删除 和 一个未跟踪的文件,两者具有相同的名称。
假设你有:
- 修改文件
foo.txt
然后删除的提交历史;
- 目前,在您的跟踪文件旁边,您的磁盘上还有一个
foo.txt
的未跟踪版本。
在这种情况下:git rebase
会抱怨,因为它不知道如何处理当前(未跟踪的)文件 foo.txt
而不删除其内容。
尝试交互式变基时,git 说:
error: The following untracked working tree files would be overwritten by checkout:
# some files here
Please move or remove them before you switch branches.
Aborting
error: could not detach HEAD
有问题的文件集似乎因我标记为 (e)dit 的提交而异。它们都是跟踪文件,在分支中的提交中被修改,然后最终在后续提交中被删除。
为什么 git 这样做?我该如何解决?
谢谢!
Git 正在抱怨,因为文件未被跟踪。
我知道你说:
[the] files are tracked
但他们不是:你还说:
They are ... finally removed in a subsequent commit.
当您处于故障发生点时,文件已从索引中删除,但未从工作树中删除。这是 未跟踪文件的定义: 它是工作树中的文件,但不在索引中。
具体来说,这个:
error: could not detach HEAD
告诉你(或者我,无论如何)当你输入命令时,你现在签出的提示提交中的文件当前未被跟踪:
git rebase -i <start-point>
但此处由 git checkout
该提交,但在签出该提交时,它将用跟踪文件覆盖未跟踪文件。
如果最后一点给您带来了 record-scratch / WTF 噪音,请不要担心:它确实 令人困惑。
Git 中的索引是一个关键的小动物,经常没有得到正确的解释。要理解它,您必须意识到 Git 在任何时候都有 三 — 好吧, 最多 3 — 的副本您正在处理的每个文件 on/with。一个副本在提交中一直冻结:您现在签出的提交。第二个副本1 位于index。这就是使文件 被跟踪 的原因。第三个副本是唯一一个你可以看到 ...但Git甚至不使用它因为它是你的副本,在你的工作树中。
索引,Git 也称为 暂存区 ,包含将进入 [=79] 的每个文件的副本(再次参见脚注 1) =]你的下一次提交。因此,当您选择要处理的提交时,它最初是从您 git checkout
的提交中复制 out 的。 Git 然后复制索引副本,这是一种特殊的 Git 格式,出你的 work-tree,这是 Git 放置所有快照文件供您查看和使用。
一旦 Git 将这些复制出来,大部分就完成了。您对工作树副本进行更改,然后 运行 git add
告诉 Git: 将工作树副本复制回索引副本的顶部/上方。 (同样,参见脚注 1。)这样做的原因有点复杂,但如果你愿意,可以这样想:
- 提交中的副本是只读的,永远冻结。为了节省大量 space,Git 将它们保存为一种特殊的 Git-only 压缩格式,只有 Git 可以使用。
- 这对存档有意义,但对完成 工作 毫无用处,因此 Git 必须将文件扩展为正常格式。那是 work-tree 副本。
- 为了加快速度并方便 Git,Git 在索引中保留冻结副本(但不再冻结)。您可以通过
git add
-ing 从工作树中批量替换索引副本:Git 会将其放入冻干 格式 ,准备好已提交,在索引中(但它仍然可以在那里替换)。
您可以对工作树副本做任何您想做的事情,因为它是一个常规文件。只要工作树文件存在,它就作为文件存在。
您可以随时删除或替换索引副本。要替换它,请使用 git add
将工作树文件复制回索引中该名称下的任何内容。要完全删除它,请使用 git rm <em>file</em>
.
当您 运行 git commit
、Git 打包 索引中的所有文件 以制作新提交的快照。索引中的任何内容(以索引中的任何形式)都会进入新提交。 Git 根本不会为此使用您的工作树。这就是为什么你必须一直重新 git-add
文件的原因:Git 没有查看工作树副本。2
如果工作树文件存在并且索引副本消失,则文件未被跟踪。但是,如果文件在某个较早的 commit 中并且您检查了该提交,则较早提交的副本将 返回到 索引中,并且文件再次被跟踪。而且,当来自较早提交的副本进入索引时,Git 也会覆盖工作树中的副本——除非因为它没有保存在任何地方而将其丢弃,在这种情况下,Git说挡路了。
(如果它被跟踪,Git 知道它是否安全地保存在提交中。如果是这样,Git 可以覆盖它。)
那么,现在该怎么做应该很明显了:如果数据很重要,请将文件移到其他地方。做你的变基。完成后,文件将 消失 因为最后一次提交删除了它们。现在您可以将文件移回原处,它们将再次取消跟踪。之前有文件的提交现在仍然有文件;如果你的 rebase 将这些提交复制到新的和改进的提交中,那些新的和改进的提交仍然有文件。
关键概念:一个文件是tracked还是untracked取决于是否有索引中的副本。索引副本大部分是不可见的,3但它非常重要! 索引是一个临时区域。它主要包含您将要进行的下一个提交;当您 git checkout
提交时,Git 必须从提交中 填写 。
因此,某些文件的 tracked/untracked 状态可能会突然改变。当 Git 从 提交中填充索引 时,如果需要,它将在索引中创建一个文件 and 在工作中创建它-当时的树;或者,如果需要,它将 从工作树中删除 一个文件——如果它不在你要移动到的提交中 —— 届时,它将 删除 文件 来自 工作树。
1从技术上讲,对于每个要提交的文件,索引包含一个 对 blob 对象的引用。 blob 对象一直被冻结并且提交引用它,就像索引一样。当您 git add
一个文件时,Git 会对内容进行压缩和哈希处理,如果已经有包含该数据的 blob,Git 可以重新使用现有的 blob。否则 Git 生成一个新的并且那个 blob 的哈希值进入索引。
有时这会创建一个永远不会被使用的 blob,因为您写入一个版本,然后意识到其中有错误并写入另一个。没关系:Git 一直创建垃圾对象,在其自己的。 Git 有一个 垃圾收集器 ,它 运行 会在合理的时候清理未使用的对象。
2如果你使用git commit -a
,那会创建一个second,但是临时索引,git add
- s 所有文件到临时索引,并从临时索引提交。如果一切正常,临时索引将成为新索引;如果失败,Git 简单地删除临时索引,一切都会恢复到您 运行 git commit -a
之前的状态。 TL;DR 是 Git 仍在从索引中提交。这只是第二个额外的索引。
3git status
命令比较索引与HEAD
提交并告诉你什么是不同。因此,如果一个文件有 "gone missing",您将看到一个删除,因为 暂存提交 。然后,它分别比较索引和工作树,并告诉您不同。因此,如果一个文件在索引中但在工作树中丢失,您将看到删除为 not staged for commit.
如果文件存在于 HEAD
,但从索引中 消失 ,然后在工作中 返回 -树,您会看到一个分阶段删除 和 一个未跟踪的文件,两者具有相同的名称。
假设你有:
- 修改文件
foo.txt
然后删除的提交历史; - 目前,在您的跟踪文件旁边,您的磁盘上还有一个
foo.txt
的未跟踪版本。
在这种情况下:git rebase
会抱怨,因为它不知道如何处理当前(未跟踪的)文件 foo.txt
而不删除其内容。