从根本上说,这里的问题是您在当前提交中有几个未跟踪的文件,但如果 Git 让您的交互式变基继续进行,则不会未跟踪。
这种情况——至少,直接的问题,甚至在应用任何变基魔法之前——相当于你有未跟踪的文件但想要跟踪git checkout
这些相同文件的其他分支的情况。有关这方面的更多信息,请参阅Unable to switch branch in git, shows error: The following untracked working tree files will be overwritten by checkout
并且git 在切换分支时说“The following untracked working tree files will be overwritten by checkout”。但是,这些问题的许多解决方案是故意跟踪这些文件,并立即提交它们,以便安全地删除它们。似乎.vscode/settings.json
,至少,真的不应该被跟踪(因此不被提交)。
这导致了此答案中概述的两难境地:如果您确实以某种方式使变基发生,即使您不希望跟踪它们,您也可能会跟踪这些文件。如果您git add
现在提交并提交,您将能够完成变基——可能在这些不应该提交的文件上存在冲突——然后您可以删除它们,但这只会将问题转移到下次有人需要提交的时候变基或以其他方式利用所有具有文件的提交。
对于这个答案的其余部分,我假设这些文件应该未被跟踪。请注意,当您签出包含文件的提交时,文件就会被跟踪!(有关未跟踪文件的精确定义,以及这变得复杂的方式、时间和原因,请参见下文。)
这个问题没有完美的解决方案。 不过,我个人的偏好是遵循 Git 本身刚刚打印的建议:
Please move or remove them before you switch branches
取出三个未跟踪的文件——它们现在存在于你的工作树中,但现在不存在于你的索引中——并将它们移出你的工作树。现在它们根本不存在,就 Git 而言。您可以完成变基。然后,完成变基后,这可能导致这三个文件现在作为跟踪文件存在,您可以删除这三个文件(如果它们在那里)并提交删除(如果您必须删除它们),以便他们没有被追踪。然后,您只需将保存的文件(您在开始时通过将它们移动到 Git 看不到它们的地方保存的文件)作为未跟踪的文件移回原位,然后您就回到了该文件开始变基时的情况,但是变基现在已经完成。
Long:什么是未跟踪的文件,为什么会发生这种情况?
未跟踪文件的定义非常简单,但其中包含 Git 术语。 未跟踪的文件是存在于您的工作树中但不在您的索引中的文件。 这意味着您需要知道您的索引和工作树是什么。(如果你已经知道,你可以在这里停止阅读。)
Git主要是关于提交。每个提交都由其自己的唯一、大、丑陋的哈希 ID 标识,存储所有文件的快照,以及一些元数据,例如提交的人、提交的时间和原因(日志消息)。所有这些东西都以压缩的、只读的、冻结的形式存储:任何提交的任何部分都不能更改。这对于存档非常有用:您不仅不能更改永久存储的文件,Git也不能,而且 Git 会自动检测在某些内容以某种方式更改或修改时发生的任何错误:磁盘故障或破坏的流氓程序您的数据,或其他任何可能出错的地方。
(如果你确实发现了一个糟糕的存储库,Git 并不擅长修复它,但请注意,Git 鼓励你保留数十个克隆。例如,如果你将 Git 存储库发送到 GitHub,GitHub 有一个副本,你有一个副本。如果有人分叉了你的 GitHub 存储库,他们至少有一个,可能还有两个,更多的副本,等等。有数百个副本,很可能其中大多数都很好。)
Git 有很多充分的理由将文件以冻结、压缩、只读、仅 Git 的形式——我喜欢称之为冻干——在所有保存的提交中。我们不需要在这里讨论它们;我们只需要知道您的所有文件以及其余的提交元数据都被永久冻结。它们无法更改,而且因为只有 Git 理解这种冻干形式,所以它们甚至对任何非 Git 程序都没有用。所以 Git必须提供一种从提交中提取所有文件的方法。
您已经对此很熟悉了,因为您使用过git checkout
. 除其他外,要做git checkout
的是获取某些提交中文件的冻干副本,并将它们重新水化:将它们转换为其他计算机程序可以使用的普通文件。这些文件是读/写的,适用于除Git 之外的所有文件。
提交是您所有文件的快照——当然,是提交中的所有文件。Git 获取快照中的所有文件并将它们恢复为有用的形式,Git 称之为你的工作树或工作树,或者类似的东西。在这里,您的文件再次有用——好吧,无论如何对您来说;他们并没有为 Git 做太多事情。
但是,为了将这些文件放入您的工作树中,Git 首先将所有冻干副本加载到位于当前冻结提交(HEAD
提交)和工作树之间的位置。这个地方就是 Git 所称的,不同地,索引,或暂存区,或(很少)缓存。这三个名字都指同一个东西:冻干文件的存放区。
索引或暂存区实际上有很多角色,但它的第一个角色是它保存您提议的下一次提交。也就是说,它有你所有的文件,都是冻干的。然后你编辑一些工作树文件来改变它。该编辑不会出现在下一次提交中——还没有!——直到你提交git add
文件。所做的是获取您git add
刚刚编辑的工作树副本,然后将其重新压缩为冻干格式。新的、更新的冻干文件进入您的索引/暂存区域,覆盖旧文件。现在新的和更新的文件将在下一次提交中:您已更改建议的提交以使用新副本(已经冻干)而不是旧副本。
git commit
(相对于其他较旧的版本控制系统,这是速度如此之快的一个原因。所有git commit
要做的就是打包预冻干文件。其他 VCS 在提交时没有准备就绪的索引去提交。他们必须从你的工作树中收集每个文件,将它们转换成内部使用的任何格式,然后提交;这个过程很慢,需要几秒钟甚至几分钟。Git 甚至不必看您的工作树:索引始终准备就绪。)
所有这一切的结果是,对于大多数文件,同时存在三个活动副本。当前提交 中有一个永久冻结的副本,您签出该副本以获取您现在所在的位置。在您的索引中有第二个副本,预冻干但实际上不是只读的。而且,当然,您的工作树中有一个有用的副本:这是您唯一可以直接查看和使用的副本。
请注意,当您运行 时git status
,Git 会运行两个单独的比较:
当您进行新提交时,Git 会将索引中的所有内容(所有冻干文件)打包并添加元数据,您将拥有一个新提交,它将永久保存这些文件(或者只要新提交仍然存在)。但是 - 未跟踪的文件来了! - 如果您的工作树中有一个文件,例如.vscode/settings.json
,但它不在索引中,会发生什么?
好吧,如果一个文件不在索引中,它就不会在下一次提交中。因此,如果您git commit
现在,新的提交将不会包含.vscode/settings.json
在其中。该文件将继续在您的工作树中,而不是在您的索引中。
这就是未跟踪的文件。 未跟踪的文件是工作树中的文件,而不是索引中的文件。
我们可以将以前不存在的文件放入索引中:我们所要做的就是使用git add
. 如果你git add
是一个文件,那么现在它在索引中。如果它以前存在,则您已将其替换为新版本。如果以前不存在,则您已将该文件放入索引中。现在该文件被跟踪。
我们也可以随时从索引中取出文件,使用git rm
. 如果您git rm .vscode/settings.json
(并且该文件在索引中),Git 将从索引和工作树中删除该文件。如果是,Git 将从索引中删除该文件,但保留工作树版本。git rm --cached .vscode/settings.json
到目前为止一切顺利:git add
放入文件,git rm --cached
取出文件,没有人弄乱工作树副本(只要你记得--cached
)。 但是,问题在于: git checkout
还将文件放入索引...和工作树中并从中取出文件。
假设.vscode/settings.json
现在未跟踪。这意味着它在工作树中,而不是在索引中。假设 commita123456
在某个特定的分支上,有一份.vscode/settings.json
. 这可能与您的工作树中的内容相同,或者可能是不同的版本,但关键是,它在那个 commit 中。如果您git checkout
提交,Git 会将那个版本的.vscode/settings.json
放入您的索引并将该版本.vscode/settings.json
放入您的工作树中。现在该文件已被跟踪!
您是否希望该文件不被跟踪?你还希望它不被追踪吗?嗯,它现在被跟踪了,因为你确实检查了a123456
。您唯一的其他选择就是不退房a123456
。
解决这个问题的唯一方法是提交当前版本.vscode/settings.json
——现在它一直被冻结,只要你的新提交继续存在,你就可以取回它——或者把它移开,这样当Git 提取提交a123456
并覆盖它,好吧,它没有碍事,实际上也没有被覆盖。
唯一能做的,有点痛苦的,就是“改写历史”。如果文件.vscode/settings.json
一开始就不应该提交,您可以在存储库中找到包含该文件的每个提交,提取该提交,删除该文件,然后进行新的和改进的提交,该提交与原始提交类似,但没有没有文件。然后,改进了所有这些提交——在 Git 中这也需要改进所有子提交,所以这是一件大事——你必须停止使用旧的提交来支持新的和改进的提交。
这并不难——像这样重写整个存储库很痛苦,但只有一次——但是这个存储库的所有其他克隆呢?他们中没有一个人的历史被改写。新历史与旧历史并不真正兼容。您必须让拥有存储库副本的其他所有人也切换到新的重写历史记录。
这就是为什么这里没有好的解决方案。一旦应该始终不跟踪的文件进入某些提交,您必须:
- 对这些提交要非常小心,或者
- 重写历史并摆脱那些提交。
第一个需要永远照顾;第二个要求对存储库的每个克隆进行修复。