TL; DR: ошибка индекса, в некотором роде / в основном, с чертой .gitignore
Пока todo.txt
равно в текущем коммите и вindex, и вы переключаетесь на коммит, где todo.txt
равен , а не в коммите, Git удалит todo.txt
из индекса и, следовательно, также удалит его из рабочего дерева.Если это приведет к засорению ценных файловых данных, Git иногда будет жаловаться, но если todo.txt
имеет .gitignore
-ед, Git все равно сможет удалить файл.
Длинный
Одна проблемаздесь трактуется «диск» как своего рода индивидуальное существительное в одной точке и собирательное существительное в другой.Нам нужны специальные слова для описания ситуаций и действий, потому что Git является системой контроля версий и поэтому имеет несколько копий некоторого файла, имя которого - в тот момент, когда мы говорим о файлах с помощьюимя в любом случае - это todo.txt
. 1
Поскольку Git предназначен для хранения файла content , но к файлу необходимо получить доступ по name, мы можем описать некоторые версии файлов, используя двоеточие, которое обеспечивает Git: git show a123456:todo.txt
показывает версию todo.txt
, которая была сохранена с коммитом, сокращенный хэш которого равен a123456
.Это содержимое хранится внутри Git как объекты BLOB-объектов .Эти версии являются постоянными - ну, в любом случае, такими же постоянными, как и коммиты, в которых они содержатся.-дерево или рабочее дерево , когда мы запускаем less todo.txt
или vim todo.txt
или что-то еще, что мы используем для доступа к нему и его изменения.Эта версия рабочего дерева не вообще хранится в Git.Это не постоянно;он может быть изменяемым и, конечно, также съемным.
Наконец, есть еще одна версия todo.txt
- или, скорее, иногда.Эта версия называется в том, что Git называет, по-разному, index , staging area или иногда cache .Этот индекс имеет несколько функций, но самое простое его описание: Индекс - это место, где вы (и Git) строите следующий сделанный вами коммит. Все это имеет значение, поскольку оно влияет на то, как Git обрабатывает файлы врабочее дерево.
Мы можем вызвать совершенный todo.txt
по его хеш-имени и имени коммита: a123456:todo.txt
.Мы можем вызвать индексную версию todo.txt
, если она существует, по имени :todo.txt
. 2 Мы можем использовать эти имена с git show
, чтобы просмотреть сохраненное содержимое этих версий файла.Зафиксированное содержимое замораживается - не подлежит изменению - в то время как содержимое индекса может изменяться до тех пор, пока мы не зафиксируем его в новом коммите.Git не может git show
версия рабочего дерева, но в этом нет необходимости: это просто todo.txt
, и мы можем увидеть и изменить его, используя имеющиеся у нас не-Gitty компьютерные программы.
1 Вопрос с именами файлов сложен.Git имеет другой ответ от других систем контроля версий: многие другие используют какой-то скрытый идентификатор файла , в то время как Git имеет только имя файла.Вместо этого Git выполняет динамическое обнаружение переименования в случаях, которые здесь не важны, поэтому мы можем игнорировать сложные вопросы об именах и просто беспокоиться об имени todo.txt
.Например, в ClearCase файл с именем todo.txt
может иметь другое имя в какой-то другой ревизии.
2 Это также 0:todo.txt
, поскольку индекс фактически имеет четыре пронумерованных слота.,Слоты 1-3 используются для слияния, поэтому мы игнорируем их здесь.
Не отслеживаемые файлы и игнорируемые файлы
Мы простоотметил, что Git делает коммиты из того, что находится в index / staging-area.Это означает, что файл, который не в настоящее время в индексе Git, не будет в следующем коммите, который вы сделаете. Вот почему git rm --cached
сработал: он удалил файл из index , не касаясь одного в рабочем дереве. следующий коммит, который вы сделали, не имел todo.txt
в качестве файла фиксации, поэтому, если следующий коммит получил хэш-идентификатор fedcba9
, fedcba9:todo.txt
.
не существует.
Предположим, теперь, что вы находитесь на этом новом коммите fedcba9
, у которого нет todo.txt
.Файл todo.txt
также отсутствует в индексе (который, поскольку вы только что сделали fedcba9
из индекса, имеет те же файлы в той же форме, что и в коммите - просто версии коммитазаморожены навсегда, в то время как версии индекса могут быть изменены или удалены).Файл todo.txt
, однако, в рабочем дереве.
Когда в рабочем дереве есть файл с некоторым именем, например todo.txt
, которого нет виндекс, этот файл неотслеживаемый .(Если файл не находится в рабочем дереве и не находится в индексе, это просто не файл вообще: это как человек на лестнице, которого там нет .)
Git обычно довольно осторожен с неотслеживаемыми файлами.Если мы попросим Git проверить какой-то более старый коммит, например a123456
, в котором есть todo.txt
, Git будет вынужден заглушить наше рабочее дерево todo.txt
, в то время как он создаст индекс :todo.txt
из a123456:todo.txt
, так что рабочее дерево todo.txt
соответствует индексу :todo.txt
, который соответствует a123456:todo.txt
.Так что Git будет жаловаться на это: он может уничтожить нашего тщательно созданного todo.txt
, переписав его с * из 1135 *.
Но, увы, Git скулит о неотслеживаемых файлах, постоянно вынуждая вас git add
их или git add
-из них при использовании любого из , добавьте все файлы, теперь операции.Помните, что git add
просто означает * скопировать этот файл в индекс, создать его или перезаписать любую версию, которая была там ранее.Если мы не хотим этого, мы можем создать файл с именем .gitignore
и поместить имя файла в файл .gitignore
.Это заставляет Git молчать о файле.
Увы, также позволяет Git не стесняться обрезать содержимое файла в различных случаях.Поэтому, если мы теперь попросим Git проверить какой-нибудь другой коммит, такой как a123456
, в котором есть todo.txt
, Git проконсультируется с нашим текущим .gitignore
и увидит, что todo.txt
игнорируется и, следовательно, способен к клобберу.Таким образом, Git извлекает a123456:todo.txt
в :todo.txt
(индекс) и todo.txt
(рабочее дерево), делая коммит a123456
текущим коммитом.
Обратите внимание, что это побочный эффект замены.gitignore
с тем же из a123456
или удалением .gitignore
, если его нет в a123456
.Это не имеет большого значения - не совсем.Важно то, что когда мы выбираем проверить a123456
, мы получаем :todo.txt
в нашем индексе из a123456:todo.txt
, и эта версия также входит в рабочее дерево.
Обратите внимание, что после проверкииз комита мы обычно имеем случай, когда все три активные версии любого файла совпадают. То есть, HEAD
коммит - независимо от его хеш-идентификатора - содержит замороженный todo.txt
, индекс :todo.txt
соответствует этой подтвержденной версии, а версия рабочего дерева соответствует версии индекса.Это иногда важно, потому что это означает, что git status
говорит nothing to commit, working tree clean
: нет изменений "в процессе" рабочего дерева, к которым следует быть осторожным.
Теперь мы просим Git вернуться к фиксации fedcba9
Например, запустив git checkout master
(предполагая, что master
в настоящее время именует fedcba9
).Git смотрит на этот коммит, сравнивая его с текущим коммитом a123456
.Есть a123456:todo.txt
, но нет fedcba9:todo.txt
.Таким образом, Git удаляет :todo.txt
из индекса - а также удаляет todo.txt
из рабочего дерева.
Поскольку он был чистым, это не проблема,Если вы хотите вернуть его, даже если он .gitignore
-d в fedcba9
, просто git show a123456:todo.txt > todo.txt
, и теперь у вас есть файл рабочего дерева.
настоящая проблема возникает, когда todo.txt
является не чистым.То есть, поскольку todo.txt
является файлом рабочего дерева, мы можем запустить наш редактор и изменить , чтобы todo.txt
не совпадал с несуществующим :todo.txt
, который совпадает с несуществующим fedcba9::todo.txt
,Если мы теперь попросим Git переключиться на a123456
, Git обычно жаловался бы здесь, что версия рабочего дерева не чистая и переключать коммиты небезопасно.Но если todo.txt
игнорируется - что это такое - Git говорит себе: О, так что все в порядке, чтобы забить его с помощью из a123456
! Git делает это, итеперь мы потеряли наш отредактированный todo.txt
, замененный на a123456:todo.txt
, который удаляется, когда мы снова переключаемся на todo.txt
.
Это реальная проблема, и нет реального решения
Самое близкое к решению в дереве работы, кроме переписывания истории, - избегать перечисления файла в .gitignore
.Это рискует случайно повторно добавить файл в какой-то момент.Другой трюк заключается в том, чтобы вообще перестать хранить файл в рабочем дереве.Это не очень приятно, но работает.