Если я сделаю git rm - кэшированный файл, затем добавлю его в .gitignore, а затем извлечу коммит из файла до его создания. Будет ли файл удален с диска? - PullRequest
0 голосов
/ 26 сентября 2018

У меня был git-репозиторий, и в нем был файл с именем todo.txt, который представлял собой список личных вещей, которые нужно сделать / узнать о них.
Я решил, что не хочу этого в моем Github репозиторий, или мой локальный git-репозиторий, так как это был личный материал, не связанный с проектом.То, что я должен был сделать, это поместить его в .gitignore с самого начала.Тем не менее, я этого не делал, и я знаю, что если вы поместите в .gitignore файл, который в настоящее время отслеживается, git НЕ отследит его.Поэтому мне нужна была команда, чтобы удалить файл из системы управления версиями, не удаляя его.Я думал, что git rm --cached todo.txt сделает это.Я так и сделал.Затем я добавил todo.txt to .gitignore.Тогда я committed .gitignore в мой локальный репозиторий.Затем, позже, я сделал git checkout для гораздо более раннего коммита, до того как я создал todo.txt.

Когда я это сделал, todo.txt был удален с моего диска, и он не вернулся, когда я вернулся к коммиту, на котором был ранее.У меня сложилось впечатление, что todo.txt не будет удалено - что оно не изменится при переходе между коммитами.Я что-то не так понял по поводу .gitignore или git rm --cached?

Почему это произошло?

Ответы [ 2 ]

0 голосов
/ 26 сентября 2018

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.Это рискует случайно повторно добавить файл в какой-то момент.Другой трюк заключается в том, чтобы вообще перестать хранить файл в рабочем дереве.Это не очень приятно, но работает.

0 голосов
/ 26 сентября 2018

После обновления вы описываете другую проблему.Чтобы git не удалил файл, я думаю, что лучше получить копию одной из ревизий, в которой он был, сохранить ее где-то еще, а затем переписать историю ветви, чтобы ее никогда не было в истории (дажевключая его в .gitignore), а затем вы можете снова безопасно поместить его в проект, зная, что git больше с ним не связывается.

Скажем, вы добавили файл на master ~ 10.Вы можете сделать это:

git checkout master~10 cp my-file.txt ../ # save it outside of the project, just in case git rm --cached my-file.txt # remove it from the commit, keep it in the project echo my-file.txt >> .gitignore # add the file to .gitignore git add .gitignore git commit --amend --no-edit # now the file is outside of this new brach history git cherry-pick master~10..master # replay history of the master branch git branch -f master # put master pointer on new branch git checkout master

На этом этапе файл должен быть на ФС, git не должен заботиться об этом, если вы перемещаетесь по истории мастера

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