git checkout 错误,即使 git status 报告工作树是干净的

Posted

技术标签:

【中文标题】git checkout 错误,即使 git status 报告工作树是干净的【英文标题】:git checkout errors even though git status reports that working tree is clean 【发布时间】:2019-04-12 08:04:52 【问题描述】:

我在本地my-feature 分店

git status 举报nothing to commit, working tree clean

我想切换到开发分支并在那里做git fetchgit merge(我更喜欢它而不是git pull

但是,这样做会产生以下错误

这里我首先检查状态,它表明一切都很干净

mymbp:MyProj username$ git status
On branch my-feature
nothing to commit, working tree clean

接下来我尝试检查我的开发分支,它是一个现有的本地分支

On branch my-feature
nothing to commit, working tree clean
mymbp:MyProj username$ git checkout develop
error: Your local changes to the following files would be overwritten by checkout:
    MyProj.sln
Please commit your changes or stash them before you switch branches.
Aborting

它抱怨myProj.sln 已更改,即使git status 表示没有任何更改。

再次发出git status,确认没有任何变化

mymbp:MyProj username$ git status
On branch my-feature
nothing to commit, working tree clean

更新 1

执行git ls-files --stage --debug MyProj.sln 如下所示,我没有看到任何 4000 或 8000(--skip-worktree--assume-unchanged 标志):

mymbp:MyProj username$ git ls-files --stage --debug MyProj.sln
100644 40c3593ed572beb2139c189455274f8900a1340c 0   MyProj.sln
  ctime: 1541703970:521058155
  mtime: 1541637062:121492660
  dev: 16777220 ino: 8470003
  uid: 501  gid: 20
  size: 55684   flags: 0
mymbp:MyProj username$ 

发布git show develop:MyProj.sln 会显示解决方案中的项目文件数量及其 GUID,用于前后解决方案的全局部分,但输出很长,仅显示发布、调试配置和一些 GUID。还不知道该怎么处理。

更新 2

因此,似乎 MyProj.sln 文件在工作树中,但不在索引和提交 (HEAD) 中。根据@torek 的解释,发出 git add MyProj.sln 应该将此文件添加到索引中,但事实并非如此,因为没有添加任何内容,并且 git status 在我执行 git add 之前和之后都没有返回任何内容。同时 git checkout 仍然抱怨 MyProj.sln 已更改。 git diff 也不返回任何内容

更新 3

我还发现有人建议发出这 2 个命令来获取提交 HEAD 的哈希值,然后查看其中发生了什么变化。我看到很多文件重复,而有些则没有。那些似乎不是我在当前功能分支中添加的文件。那些重复的似乎是来自远程的文件

mymbp:MyProj username$ git rev-parse HEAD
1ca8d8a7c5eff0f2a03eb185f1b25aff27c1d2fd
mymbp:MyProj username$ git ls-tree -r 1ca8d8a7c5eff0f2a03eb185f1b25aff27c1d2fd

这是它的输出

更新 4

我的配置是:

mymbp:MyProj username$ git config --list
credential.helper=osxkeychain
core.excludesfile=/Users/username/.gitignore_global
core.autocrlf=input
difftool.sourcetree.cmd=opendiff "$LOCAL" "$REMOTE"
difftool.sourcetree.path=
mergetool.sourcetree.cmd=/Applications/Sourcetree.app/Contents/Resources/opendiff-w.sh "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"
mergetool.sourcetree.trustexitcode=true
user.name=User Name
user.email=username@somesystems.com
color.ui=true
color.status.changed=blue normal
color.status.untracked=red normal
color.status.added=magenta normal
color.status.updated=green normal
color.status.branch=yellow normal bold
color.status.header=white normal bold
commit.template=/Users/username/.stCommitMsg
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
remote.origin.url=https://github.com/SomeSystems/MyProj.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.develop.remote=origin
branch.develop.merge=refs/heads/develop
branch.feat-1.remote=origin
branch.feat-1.merge=refs/heads/feat/feat-1
branch.1234-refactoring.remote=origin
branch.1234-refactoring.merge=refs/heads/1234-refactoring
mymbp:MyProj username$ 

【问题讨论】:

文件MyProj.sln真的被添加到当前分支了吗? 另一个 git status 怎么说 它已经存在了几个月,但无论如何,即使添加了它,git status 也应该将其视为未跟踪。另一个 git 状态?每次我在这个分支中执行 git status 时,它说工作树是干净的,我重新启动并尝试了几次,每次结果都和上面一样 如果您有未跟踪的文件并且您收到该消息,可能是因为它已被添加到分支(由其他人?)。 不,这是我的本地功能分支,没有人碰过它。另外,该文件未显示为未跟踪。 Git status 显示没有任何变化, git commit 显示它已经如上文所述。我更新为 clerify 【参考方案1】:

每次更新编辑:这里肯定有些奇怪,尽管很难确定是什么(捕获的输出是图像,因此无法检查奇怪的 Unicode 问题,例如)。任何文件都不应该在提交树的git ls-tree -r 输出中列出两次。我从未在 Git 中看到过这种行为。 存在大写和小写的问题,尤其是在 Windows 和 MacOS 上,可能会导致这种行为,但这与您在此处显示的内容不符。

原答案如下

首先,快速说明:您的标签提到了 MacOS。 MacOS 文件系统默认不区分大小写,因此如果您有一个名为README.TXT 的文件并要求系统查看名为readme.txt 的文件,它会显示README.TXT。如果您要求系统添加一个名为 readme.txt 的新文件,它将改为 覆盖 现有 README.TXT 并保留大写名称。因此,请注意名称不同的文件,以防万一:Git 提交可能有一个 myproj.sln 文件,该文件将覆盖您的 MyProj.sln 文件。


至少有两种可能性,但让我们先看最有可能的:

这意味着有一个文件不在当前索引和提交中,但在当前工作树中,名为MyProj.sln。在您向 Git 提交给git checkout 的提交中。因此,如果您成功完成git checkout 其他提交,Git 将覆盖工作树文件。 Git 警告您,您将丢失该文件的 当前 内容。

或者,当前索引和工作树中有一个的文件,名为MyProj.sln。工作树副本与索引副本不匹配。通常,git status 会告诉您文件已被修改,但您设置了告诉 Git 的两个索引标志位之一:不要查找文件的更改,和/或不要告诉如果你不小心发现了一些更改,请保持索引副本保持原样。这两个标志位是--assume-unchanged--skip-worktree

在这两种情况下,如果您确实成功签出了您要求 Git 签出的提交,那将覆盖 MyProj.sln 的工作树副本。如果没问题,请立即删除文件,git checkout 将继续。


看看是什么情况:

git ls-files --stage --debug MyProj.sln

如果没有输出,则该文件不在索引中(因此也不在当前提交中,基于git status 输出或缺少输出)。这反过来意味着它目前只是一个未跟踪的工作树文件。

如果你确实得到了输出,它应该类似于我在这里为不同的文件得到的输出:

$ git ls-files --stage --debug Makefile
100644 b08d5ea258c69a78745dfa73fe698c11d021858a 0       Makefile
  ctime: <number>:<number>
  mtime: <number>:<number>
  dev: <number>  ino: <number>
  uid: <number>  gid: <number>
  size: <number> flags: <number>

flags: &lt;number&gt; 显示了假设不变和跳过工作树位,尽管不是人类友好的格式:跳过工作树是4000,另一个是8000(如果两者都设置了,你会得到c000)。设置skip-worktree位,我实际上得到:

size: 96311   flags: 40004000

虽然设置假设不变位给了我:

size: 96311   flags: 8000

您要求切换到的提交(develop 的提示)具有该文件的已提交版本,您可以通过以下方式查看(不会覆盖当前副本):

git show develop:MyProj.sln

请注意,一旦您git checkout develop,该文件将位于所有三个活动位置:当前提交、索引和工作树。如果my-feature 的提示提交确实没有 有该文件,则从develop 切换回my-feature 将从工作树中删除该文件。记住大部分内容的方法是:

从索引进行提交。 因此首先将提交提取到索引中(然后再提取到工作树中)。 索引跟踪工作树中应提交的内容。索引副本是进入提交的副本。 git status 将索引与工作树进行比较,以告诉您应该将什么复制到索引中。 切换提交时,不在索引中但需要基于新提交的工作树文件,创建;文件>是在索引中,但需要基于新的提交,得到删除。 工作树中不在索引中的文件未被跟踪,并且被单独留下。

在那之后,假设不变和跳过工作树位(如果您设置它们)只是对原本基本上自洽的规则的微小调整。 .gitignore 规则也开始变得有意义:.gitignore 中列出的文件是 git status不会抱怨未被跟踪的文件。 (然而,一个重要的副作用是,.gitignore 让 Git 在某些情况下可以随意破坏这些未跟踪的文件,这更难记住或解释。)

【讨论】:

如果文件不在在索引中,git add会将它复制到索引中,现在它在索引中并且将在您进行的下一次提交中。如果您希望它被提交(永久保存,或者至少只要提交本身继续存在),就这样做。 没有看到更新,但它们很奇特。任何文件都不应以相同的名称列出两次;这里很多都列出了两次(也有相同的 blob 哈希 ID)。这些名称似乎并不复杂:例如,没有明显的大写与小写问题。可能存在 UTF-8 编码的名称,但通常会在名为 schön 的文件中看到此问题,而且我在这里也看不到重音符号。 行尾不影响 Git 内部数据结构。您可以使用 git ls-tree -r HEAD | vis -ctl 将制表符和换行符显示为 \t\$,但我怀疑有人一直在使用损坏的 Git 版本并生成坏树对象。 嗯,是的,\$ 的重点是让任何 尾随 空格可见,以防一个文件名为 X 而另一个名为 X , 例如。 vis 命令还将对各种控制和转义序列进行编码,以便它们变得可见; -t 编码标签(如\011-c,如\t)。 没什么明显的,不过我现在想知道您是否有任何清洁和/或涂抹过滤器正在做一些不明显的事情。但这不会与 tree 对象混淆,并且您列出的配置没有定义过滤器。

以上是关于git checkout 错误,即使 git status 报告工作树是干净的的主要内容,如果未能解决你的问题,请参考以下文章

git checkout 将未暂存的文件带到新分支

Git

从 git checkout 构建 yeoman 项目

Git 中 git checkout -- <file>的真正用法

Git checkout:更新路径与切换分支不兼容

如何理解git checkout