为什么在压缩后错误切换分支?没有什么可承诺的

Why error switching branches after squashing? Nothing to commit

我只是压缩了我的远程提交,然后将它们强制推送到远程。 git status 表示没有变化。为什么我在尝试签出开发分支时会收到此错误?

error: Your local changes to the following files would be overwritten by checkout:
[file here]
Please commit your changes or stash them before you switch branches.
Aborting

这是完成的事情:

git rebase -i origin/feature/EX-1576~14 feature/EX-1576
git push --force origin feature/EX-1576

git status 什么也没显示:

$ git status
On branch feature/EX-1576
Your branch is up to date with 'origin/feature/EX-1576'.

nothing to commit, working tree clean

据我了解,当您进行本地更改时会出现此错误,但我没有进行本地更改。我只是想这样做:

git checkout develop

TL;DR

遵循 Git 的建议:对于它命名的每个文件,将该文件移动到其他地方(让开,可能完全从项目中移出),或提交它。然后进行结帐,查看替换了这些文件的文件,并决定是保留替换文件,还是使用结帐前保存的副本。

小心 git update-index --assume-unchangedgit update-index --skip-worktree:它们在某些情况下效果很好,但会让您落入这个特定的陷阱。

由于您在 Windows,默认情况下会将名为(例如)readme 的文件与其他名为 README 的不同文件合并 - Windows 无法同时存储两者;它只会破坏其中一个——小心区分大小写的文件名,通常由一些 Linux 程序员创建。 :-)

It's my understanding that the error occurs when you have local changes ...

这不太对。当操作(在本例中为 git checkout)将覆盖某些 state.

时,您会收到该错误

Git 根本不是 变化 。 Git 主要是关于 commits,并提交保存 state——你所有文件的快照,以及你的元数据:你的名字和电子邮件地址,你提交的时间,以及关于你为什么提交的日志消息,例如。 (此元数据中包含另一个关键项,即父提交哈希 ID,但对于这个特定问题,我们可以忽略它。)

状态和变化之间的区别就像谈论天气:说今天比昨天暖和告诉你关于温度的一件事,但不是全部。说昨天是 15˚C (59˚F),今天是 20 / 68,告诉你关于温度的一切。 (嗯,关于这个温度,无论如何。)请注意,需要两个 状态 才能得出 变化: 我们必须减去昨天的温度从今天开始,看看它可能会暖和或冷多少。

无论如何,提交存储状态:提交的每个文件的完整副本,截至提交时。此副本实际上来自 Git 的 index,但我们暂时忽略了这一细微差别。不过,它马上就会出现。所以每次提交都非常独立于其他所有提交。

你的 work-tree,另一方面,不是 Git 保存的东西(根本,真的,因为索引)。你用它来处理你的文件,因为提交的副本是一种特殊的、冻结的、压缩的(有时压缩得很厉害),仅 Git-only 格式。为了使这些有用,Git 需要将它们扩展为普通格式的文件,您可以使用,如果您愿意,可以 更改 。那些扩展的副本进入你的工作树。

现在,关于工作树的一个问题是它允许包含您不会提交的文件。这些是 Git 称为 未跟踪的 文件。通常,如果您的工作树中有一个文件未被跟踪——不会被提交——Git 将抱怨 那个文件。您可以通过在 .gitignore 中列出未跟踪的文件来让 Git 闭嘴,但这比看起来更棘手。这就是 Git 再次用索引的存在打你的脸。

索引是一个奇怪而美妙但也令人讨厌的东西,它几乎是 Git 所独有的。在以冻结 Git-only 压缩形式存储文件的提交和允许您处理文件的工作树之间,Git 放置了 third 每个文件的副本。每个文件的索引副本都是特殊的 Git-only 格式,但不是被冻结,而是 准备 冻结:如果你愿意的话,有点糊涂。关键是您可以 更改 这个副本,这就是 git add 所做的:它将文件从工作树复制到索引中。

实际上是索引副本的存在决定了文件是否被跟踪。如果文件在索引中,则它被跟踪;如果不是,则未跟踪。在 .gitignore 中列出文件意味着:如果它不在索引中,并且在工作树中,请不要抱怨。 但它有第二个副作用,即是:在某些情况下,它授予 Git 销毁 文件的权限。

文件名大小写问题

Linux 程序员愉快地编写并提交了两个不同的文件,一个名为 README,另一个名为 readmeReadme。或者他们对头文件做同样的事情:ip.hIP.h(在旧的 Linux 内核树中)。当使用 Mac 或 Windows 框的人尝试处理这些提交时,他们会被这些系统上的 work-tree 不能'将两个文件都放在适当的位置。 (Git的索引处理得很好,因为索引实际上是一个文件,.git/index。)

如果您正在从一个文件名为 README 的提交切换到一个文件名为 Readme 的提交,或者两者都有,Git 有时会因此而变得有点混乱,不知道该怎么办。 (Git 总有一天需要对此更加聪明。)

假设不变并跳过工作树

无论如何,假设索引中有一个文件。如果您更改工作树副本,Git 会告诉您您有一个修改过的跟踪文件。如果你不想Git一直提醒你这个,你可以用git update-index --assume-unchangedgit update-index --skip-worktree专门标记那个文件。

当你这样做时——我想你可能做过——Git 停止比较文件的索引副本与工作树副本,因为git status 命令,并且不会将文件的工作树副本复制到索引副本之上,用于 git add 命令。这意味着您可以获取一个配置文件,出于某种原因对其进行修改,但仍然有 new 提交——使用文件的 index 副本—存储文件的原始版本,即在设置假定不变或跳过工作树位之前从提交中进入工作树的版本。

但是git checkout必须,当它切换到其他提交时,用提交中的(不同的)提交副本替换该文件的索引副本你要切换到。发生这种情况时,Git 不仅会更新索引副本,还会覆盖工作树副本。因此,如果您有一个标有这两个位中的任何一个的文件,则在使用 git checkout.

时可能会出现该错误

这是个问题吗?也许是,也许不是。如果你强制 git checkout 检查另一个提交,Git 将用另一个提交的文件覆盖索引条目,并用另一个提交的文件替换该文件的工作树副本.由您决定这是否可行,如果不行,您是否需要先将文件移开,或者清除这些位并继续添加并提交文件。

也有文件被半忽略的极端情况

另一方面,假设您没有使用git update-index设置那些索引位。您仍然可以有一个普通的、未跟踪的文件,甚至可能在 .gitignore 中列出一个以保持 Git 安静。但是一些 other 提交可能有相同文件的(不同版本),如果你有 Git 切换到 that 提交, Git 将不得不 replace 您未跟踪的工作树文件,其中它是 tracked 文件的提交版本。

在这种情况下,git checkout 有时(但不总是)也会抱怨。通常它会说结帐会覆盖 untracked 文件。如果文件在 .gitignore 中列出,这将允许 Git 的某些部分破坏它。幸运的是 git checkout 通常对这些事情非常小心。