Git Checkout без удаления неотслеживаемых файлов - PullRequest
0 голосов
/ 16 ноября 2018

repro

mkdir gittest && cd gittest
git init
touch file1 file2
git add ./file1
git add ./file2
git commit -a -m "master init"
git update-index --skip-worktree ./file2
git rm --cached ./file2
git commit -a -m "file2 removed from master"
git checkout -b branch2
git add ./file2
git commit -a -m "file2 added to branch2 instead."
git checkout master
ls file2

-

ls: cannot access 'file2': No such file or directory

Почему кровавый git удаляет file2, когда master извлекается, когда яуже сказал это не отслеживать file2 в master.Это ошибка?

Ответы [ 2 ]

0 голосов
/ 16 ноября 2018

Почему кровавый 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 по запросу удалит пустые каталоги из рабочего дерева.

0 голосов
/ 16 ноября 2018

Вы удалили file2 на master и добавили его в branch2.Так как он отслеживается в branch2 и отсутствует на master, файл удаляется.Это нормальное поведение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...