Редактировать по обновлениям: здесь определенно что-то странное, хотя трудно точно сказать, что именно (захваченный вывод - изображения, поэтому невозможно проверить, к примеру, странные проблемы с 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 перезапишет файл рабочего дерева, если вы успешно выполните git checkout
этого другого коммита. 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: <number>
показывает биты предполагаемого неизменного и дерева пропусков, хотя и не в удобном для человека формате: пропустить дерево работ 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 свободным для clobber таких неотслеживаемых файлов в некоторых случаях, сложнее запомнить или объяснить в этом отношении.)