Почему кровавый git
удаляет file2
, когда извлекается master
, когда я уже сказал ему не отслеживать file2
в master
.Это ошибка?
Действует в соответствии с дизайном.Вы можете назвать дизайн ошибкой, но я думаю, что дизайн не тот, о котором вы думаете, как показывает ваша собственная фраза , которая не отслеживает file2
в master
.Состояние «отслеживания» для любого данного файла: , а не функция имени ветви, например master
или branch2
.Для каждого файла есть только отслеженные или неотслеживаемые.Файл отслеживается тогда и только тогда, когда он находится в индексе прямо сейчас.
(Из-за способа, которым работает git checkout
, можно расширить,фраза для обозначения определенных коммитов, а имена веток всегда ссылаются на какой-то конкретный коммит. Так что хочет говорить о том, что файл отслеживается в какой-то ветви, не исключено, но если вы это сделаете, вы будете склонныввести себя в заблуждение.)
Что такое индекс и что он делает
Чтобы по-настоящему все это зафиксировать, мы должны сначала поговорить об индексе: что именно является индексом?Чтобы получить правильную мысленную картину, начните с того, что все коммиты доступны только для чтения.Каждый коммит содержит каждый файл, который вы сохранили в Git с этим коммитом.Эти файлы заморожены (только для чтения) и хранятся в специальной сжатой форме, предназначенной только для Git.
Хотя хранение файлов всегда хорошо, нам нужен какой-то способ использования и работают с файлами.Для этого Git предоставляет рабочее дерево .Файлы в рабочем дереве доступны для чтения / записи (ну, как правило, вы можете изменить определенные файлы, если хотите).У них есть обычная повседневная форма использования компьютера, какая бы она ни была на вашем компьютере.Вы можете работать с ними, чтобы делать все, что вам нужно.Но сам Git уделяет им относительно мало внимания.
Большинство систем контроля версий на этом останавливаются: в них зафиксированы замороженные файлы и файлы рабочего дерева, с которыми вы можете работать.Git, однако, вставляет эту специальную вещь index между коммитом и рабочим деревом.Для почему вам действительно нужно спросить Линуса Торвальдса, но мы можем заметить, что это делает кучу вещей, в том числе делает git commit
и git checkout
действительно быстрыми по сравнению с этимидругие системы контроля версий.Но это также доставляет нам огромную головную боль, с которой вы только что столкнулись: оно дает представление о отслеживаемом файле .
То, что находится в индексе, обычно и изначально просто не заморожено- но все еще сжатый и Git-only format - версия файла, который вышел из коммита.Это означает, что файл идеально подходит для замораживания Git в new commit.Таким образом, новый коммит вообще не требует повторного сжатия файла: он уже есть в индексе.Следовательно, для большинства файлов существует три активных копии:
commit index work-tree
---------------- ------------------ ------------------
frozen, Git-only unfrozen, Git-only unfrozen, ordinary
Вы можете скопировать любую копию в любую другую копию, за исключением, конечно, фиксации, потому что эта копия заморожена.Копирование из коммита в индекса является простым, но в основном невидимым, потому что git checkout
делает это, но затем также копирует из индекса(после записи в индекс) в рабочее дерево, так что вы видите «скопировать файл из некоторого коммита в рабочее дерево», не понимая, что есть «индекс»"шаг в середине.
Копирование из рабочего дерева в индекс также прост: это то, что делает git add
.Шаг git add
сжимает и запускает Git-файл во время git add
, так что, как только он будет в индексе, он будет готов к замораживанию.
Возможно, самая большая вещь, которую делает индекс, это то, что индекс всегда является источником для нового коммита. 1 Когда вы запускаете git commit
, Git просто замораживает файлы, которые находятся в индекс, , не смотря на рабочее дерево вообще , и использует их для создания нового коммита. Новый коммит затем становится текущим коммитом, так что копия индекса и копия фиксации совпадают так же, как копии индекса и фиксации каждого файла совпадают сразу после вашего первого git checkout
какого-то коммита.
Это дает нам однострочную сводку индекса: это набор файлов , который вы предлагаете добавить в следующий коммит. Если вы удалите файл из индекса, используя git rm
, вы предлагаете, чтобы ваш следующий коммит не включал этот файл.
1 Такие команды, как git commit -a
, которые, кажется, фиксируются из рабочего дерева, действительно работают, сначала добавляя файлы в индекс, а затем фиксируя. При необходимости они создают специальный временный индекс, добавляют файлы во временный индекс и фиксируют из временного индекса. Это создает впечатление, что Git каким-то образом выполняет коммит из рабочего дерева, но это не так: он коммитит из и индекса где-то, даже если это специальный временный индекс.
git checkout <em>branch-or-commit</em>
заполняет индекс и рабочее дерево
Всякий раз, когда вы git checkout
делаете коммит, Git должен извлекать файлы этого коммита как в индекс , так и рабочее дерево. Ему нужны файлы, чтобы войти в индекс, чтобы индекс совпадал с фиксацией. Нужно, чтобы файлы вошли в рабочее дерево, чтобы вы могли их видеть. Как только все они будут созданы, git checkout
обновит HEAD
- где Git отслеживает текущий коммит и / или текущую ветвь - в зависимости от ситуации, так что текущий коммит - это коммит, который вы только что извлекли, и вы Вы находитесь на ветке или в режиме «отсоединенная ГОЛОВА» в зависимости от ситуации.
Но обратите внимание, что только что произошло:
git checkout
заполнил индекс из коммита.
- Содержимое индекса определяет, какие файлы отслеживаются.
Это означает, что набор отслеживаемых файлов изменится . Если вы используете master
, а file2
отсутствует в индексе, то либо file2
вообще не существует (так что в этом нет сомнений), либо он существует в рабочем дереве и поэтому не отслеживается. Но как только вы наберете git checkout branch2
, в коммите на кончике branch2
появится file2
, так что file2
войдет в индекс, а Git перезапишет копию рабочего дерева. Теперь файл отслеживается . Если вы затем git checkout master
, Git видит, что file2
в настоящее время отслеживается, но не является в коммите, к которому вы хотите добраться, поэтому Git удаляет file2
из обоих индекс и дерево работы.
Это страшная опасность удаления файла с git rm --cached
: он оставляет копию в рабочем дереве, в то же время убирая ее из индекса прямо сейчас . Но если сейчас это в индексе, есть большая вероятность, что также в некоторых других коммитах. Если вы когда-нибудь проверите эти коммиты, файл возвращается в индекс; если затем вы удалите из этого коммита, файл будет удален из и индекса и рабочего дерева, и теперь он исчез.
git update-index --skip-worktree
не помогает
Для этого задается один из двух специальных управляющих битов, другой - --assume-unchanged
- для записи индекса для некоторого файла. Запись индекса существует только , если файл на самом деле в индекс: удаление файла из индекса удаляет запись и, следовательно, удаляет управляющие биты.
WhЕсли биты управления установлены, различные команды Git, которые будут сравнивать индексную копию файла с копией файла рабочего дерева, пропускают их сравнение . Это означает, что Git не будет предлагать вам использовать git add
для копирования обновления рабочего дерева обратно в индекс, и не будет предлагать, чтобы файл, который находится в индексе, но по какой-то причине отсутствовал в рабочем дерево, отсутствует.
Ничто из этого не влияет на то, что на самом деле в указателе. Индексная копия файла остается неизменной, и каждый новый коммит продолжает снимать индексную копию файла. Просто git add -a
не не обновляет копию индекса (если вы изменили копию рабочего дерева), а git status
не не жалуется , что копия индекса устарел (если вы изменили копию рабочего дерева).
Примечание о каталогах
Git никогда не отслеживает каталог. В частности, вы не можете добавить каталог в индекс. Вместо этого Git делает то, что если в индексе есть какой-то отслеживаемый файл , который Git хочет создать в рабочем дереве , а имя файла включает в себя каталог, который в данный момент не существует, Git создаст каталог самостоятельно, чтобы он мог поместить в него файл.
В основном это все, за исключением того, что Git также иногда удаляет каталог после удаления последнего файла из этого каталога. Кажется, здесь есть несколько странных угловых случаев (особенно в очень старых версиях Git, предшествующих 1.8), так как я заставлял Git оставлять пустые каталоги, когда не было очевидной причины для этого. Команда git clean
по запросу удалит пустые каталоги из рабочего дерева.