Я вижу из комментариев, что вы уже вернули свои файлы, что хорошо и означает, что мне не нужно писать конкретные инструкции для этого (хотя у меня будет раздел об этом).Но: важно понимать, что делает .gitignore
.На самом деле это не приводит к игнорированию файлов!(Это означает, что .gitignore
не очень хорошее имя, но хорошее имя будет очень длинным. Возможно, оно должно было называться .gitxyz
или .gitconfuse
или что-то в этом роде, так что оно короткое изапоминающимся, но вы не думаете, что это означает «игнорировать».)
Итак, что же на самом деле делает 1010 * .gitignore
?Что ж, для этого требуется быстрое погружение в другую часть Git, которую затушевывают многие учебники или введения, что вызывает много путаницы, и это Git index .
Background
Во-первых, отметим, что Git в основном хранит коммитов .Коммит содержит полный снимок всех ваших файлов - ну, все те, которые содержатся в коммите, но это немного избыточно: «Коммит имеет то, что имеет коммит».Важной частью здесь является то, что есть полная копия каждого файла, замороженного и неизменного, сохраненного навсегда (или, по крайней мере, до тех пор, пока сам коммит продолжает существовать).
(фиксациятакже содержит некоторые метаданные - некоторую информацию, например, ваше имя и адрес электронной почты в качестве автора / коммиттера, и ваше сообщение журнала. Важно, что каждый коммит также содержит истинное имя - идентификатор хеша - предыдущего или parent commit. Но мы не будем вдаваться в подробности.)
Эти замороженные файлы, сохраняемые постоянно и постоянно при каждом коммите, занимали бы много места, если бы они не были сжаты, поэтому они находятся вспециальная сжатая форма только для Git.И действительно, поскольку они заморожены, если две разные фиксации используют одни и те же данные для файла, эти две фиксации фактически передают замороженную копию.Это означает, что тот факт, что Git продолжает помещать одну и ту же копию файла в каждый новый коммит, вообще не занимает места, потому что на самом деле он просто повторно использует старый файл.Но в любом случае эти файлы бесполезны ни для чего , кроме Git: никакая другая система не может читать их напрямую, и ничто - даже Git - не может писать на них: они заморожены.
Итак, чтобы вы могли видеть и работать с вашими файлами, Git должен разморозить все файлы, которые были сохранены в некотором коммите, в какую-то рабочую область.Git называет эту область рабочим деревом или рабочим деревом .Это хорошее имя, потому что именно там вы выполняете свою работу.
Другие системы управления версиями, не относящиеся к Git, обычно останавливаются здесь: у них есть зафиксированные файлы (возможно, хранящиеся в виде дельт вместо полных файлов), иДерево работы, и это все.Когда вы используете одну из этих систем и делаете коммит new , это занимает много времени, а иногда и много времени.Иногда вы могли бы пойти и пообедать, пока вы ждете.Однако с Git вы запускаете git commit -m message
и - zip - все готово.
Git получает всю эту скорость из index .Но index - ужасное название для этой вещи, поэтому Git также называет ее областью подготовки , или иногда кеш , в зависимости от того, кто / что делаетпризвание.Индекс хранится - в специальной форме только для Git, но на этот раз не заморожен - все файлы, которые будут находиться в next commit.
Изначально индекс заполняется в зависимости от того, какой коммит вы проверяли.То есть git checkout <some-commit-specifier>
находит фиксацию, которая содержит полный набор замороженных файлов.Git копирует замороженные файлы (ну, в общем, ссылку на их содержимое) в индекс, вычисляя полные имена файлов по пути, чтобы в индексе был список всех файлов, которые Git должен поместить в рабочее дерево,Теперь они в специальном формате Git-only, но unrozen .Затем Git также помещает файлы в рабочее дерево, расширяя их до полезного формата.
Конечный результатявляется то, что индекс соответствует фиксации, но индекс не заморожен.Рабочее дерево соответствует и коммиту и индекса, и, конечно, не заморожено, и файлы имеют свою полезную форму.Теперь вы делаете свою работу как обычно - и это объясняет , почему вы должны git add
ваши файлы все время!
Что git add
делает, это копирует файл рабочего дерева в индекс.Это перезаписывает предыдущую копию, если файл уже был в индексе.Новая копия теперь в формате Git-only (но еще не заморожена).Если файл ранее не был в индексе, то теперь он есть.В любом случае, индекс все еще готов к работе.Все, что git commit
должен сделать, кроме сбора метаданных, таких как ваше имя, электронная почта и сообщение журнала, - заморозить индекс.
Следовательно, лучшее краткое описание, которое я знаю для индекса, это: Индекс содержит ваш предложенный следующий коммит. Он содержит все файлы в специальной форме Git, но еще не заморожен.Вот почему он также называется промежуточной областью: содержит все файлы, подготовленные и готовые к работе.
Постановка, без постановки, без отслеживания, игнорируется
Сейчасчто вы знаете, что есть три копии каждого файла для беспокойства, все это начнет обретать смысл.Например, рассмотрим файл README.txt
.Вы запускаете git checkout master
для запуска, и Git находит коммит для master
и проверяет его, делая этот коммит текущим или HEAD
коммит:
HEAD:README.txt
замороженв текущем коммите.Он никогда не изменится - это часть этого коммита.
:README.txt
копируется в индекс и размораживается в процессе.Он может измениться, но в настоящее время он соответствует HEAD:README.txt
.
:README.txt
, скопирован из индекса в рабочее дерево и развернут в полезную форму.Он может измениться, но в настоящее время он соответствует :README.txt
.
Все три копии совпадают, поэтому Git ничего не говорит о файле.
Если вы сейчас изменитекопирование и запуск рабочего дерева git status
, команда Git status
сравнивает HEAD
и индексные копии.Они одинаковые, поэтому об этом ничего не сказано.Он сравнивает индекс и копии рабочего дерева, и они различаются, поэтому git status
говорит, что файл не подготовлен для фиксации .
После запуска git add README.txt
эти копии (и сжимает) версию рабочего дерева в :README.txt
.Теперь эти два совпадают, но HEAD:README.txt
и :README.txt
различны.Поэтому git status
сравнивает HEAD
с индексом и говорит, что файл подготовлен для фиксации .
Обратите внимание, что вы можете еще раз изменить копию рабочего дерева.Теперь файл отличается в во всех трех версиях, и git status
говорит вам, что он подготовлен для фиксации (HEAD и index не совпадают) и не подготовлен для фиксации (index и work-tree don 'тоже не совпадают).Все это основано на результате двух git diff
с: один от HEAD
до индекса и один от индекса до рабочего дерева.
Но что произойдет, если у вас есть файл, который находится в * 1149?* зафиксировать, что вы удалили из индекса и рабочего дерева?Что ж, теперь удалено при сравнении HEAD
с индексом.Так что Git говорит, что удаление проводится.Индекс и дерево работы совпадают, поэтому Git ничего не говорит об этом.В любом случае, ваш следующий коммит не будет иметь файл.
Что произойдет, если у вас есть файл, который находится в рабочем дереве, но отсутствует в индексе?Если он находится в коммите HEAD
, это все еще поэтапное удаление: файл не будет в следующем коммите.Но он также отличается по индексу и рабочему дереву, поэтому он не отслежен .
Если файл не находится в HEAD
и отсутствует в индексе, но находится в работе-дерево, оно не отслежено .
Это говорит нам, что значит иметь неотслеживаемый файл: файл не отслеживается тогда и только тогда, когда его нет в индексе прямо сейчас .Поскольку вы можете манипулировать индексом, добавляя или удаляя файлы в любое время, вы можете изменить отслеживаемость какого-либо файла в любое время, просто добавив его в индекс или исключив из индекса.
Если файл отсутствует в индексе , а отсутствует в рабочем дереве, он просто не существует.Только файлы, которые являются в рабочем дереве, но не являются в индексе, не отслеживаются.Вы можете git add
файл, и Git скулит на вас, ворча вас о файле.Перечисление файла в .gitignore
(или в .git/info/exclude
) в основном закрывает Git up .Фактически это не приводит к тому, что файл не отслеживается - вопрос в том, находится ли файл в индексе.Как только файл находится в индексе, он отслеживается, и .gitignore
не имеет никакого эффекта.Это просто удерживает git status
от ворчания.Так что вместо .gitignore
, может быть, это должно быть .git-dont-complain-about-these-files-if-they-are-untracked
.
Это также имеет еще один важный эффект.Вы можете запустить git add .
или git add somedir
или git add --all
, чтобы добавить целую кучу файлов на основе поиска Git по всему списку файлов в каталоге / папке.Если вы перечислите некоторые файлы как проигнорированные, git add
пропустит их, если они еще не отслежены.То есть отслеживаемый файл определенно находится в Git, поэтому git add
скопирует его в индекс, если он будет изменен.Но неотслеживаемого файла еще нет в Git, поэтому добавление в массовом порядке добавит его, если оно не будет проигнорировано.Здесь «игнорировать» - правильное слово.Так что, возможно, файл должен называться .git-dont-complain-about-these-files-if-they-are-untracked-and-dont-auto-add-them-with-an-en-masse-add-operation
.
К сожалению, есть еще один побочный эффект перечисления файла в .gitignore
, и это то, что вы говорите Git, что можно удалить или закрыть *.1210 * файл в некоторых случаях.Таким образом, полное собственное имя для .gitignore
может быть .git-dont-complain-about-these-files-if-they-are-untracked-and-dont-auto-add-them-with-an-en-masse-add-operation-but-do-feel-free-to-clobber-these-files-sometimes
.Представьте, если бы это было имя файла!По крайней мере, это было бы менее запутанным.
Недостатки удаления файла с git rm --cached
Как мы видели выше, если вы хотите, чтобы какой-то файл не отслеживался, вы должны извлечь его изиндекс.Если вы используете git rm --cached <em>filename</em>
, Git удалит файл из индекса (так что теперь он не отслежен), но не удалит файл из рабочего дерева (так что вы все равно можете его использовать).Ваш следующий коммит не будет иметь файл, который вам нужен.
Но все старые коммиты, фиксированные навсегда, фиксируют, что сделать файл ... все эти старые коммиты все еще существуют.Если вы когда-либо проверяете один из этих коммитов, Git должен будет скопировать замороженный файл в индекс, а затем скопировать копию индекса в рабочее дерево.Это загубит вашу рабочую версию.Это нормально?Git ответит на это проверкой файла в .gitignore
!
Если файл не , указанный в .gitignore
, Git не стесняется засорять его.Но вы получите жалобы на то, что это не отслеживается.Чтобы решить эти жалобы, вы, вероятно, перечислите файл в .gitignore
.Затем при проверке старого коммита ваши файлы будут забиты извлеченными, незамерзшими, несжатыми файлами, а затем при возврате к новому коммиту удалит файлы, потому что они теперь такие же, как и у замороженных., так что это "безопасно".
Git нужен, но не имеет, способ вывести файл рабочего дерева, когда заткнётся об этом, но никогда не обманывают его .Если и когда это когда-либо будет добавлено, это позволит вам справиться с вашей ситуацией.Но лучше, по возможности, вообще не попадать в ситуацию.
Восстановление того, что вы можете
Между тем, если вы делаете потерять свои файлы, помните, что по крайней мере некоторые версии (ы) из них в старых замороженных коммитах.Вы можете извлечь эти старые замороженные версии.Есть несколько способов сделать это:
git show
: запустить git show <em>commit</em>:<em>path</em>
, например, git show v1.0:README.txt
или git show a123456:path/to/file.ext
.Это расширяет замороженный сохраненный файл до стандартного вывода, поэтому вы можете сохранить его с перенаправлением ввода / вывода: git show v1.0:README.txt > README.txt.old
, например.
git checkout
имеет режим, в котором вместо проверки всего коммита он заполняет часть вашего индекса и рабочего дерева из некоторого существующего коммита.(Эта команда, вероятно, никогда не должна была называться git checkout
, так как она довольно разрушительна, если у вас есть несохраненные изменения, поэтому будьте осторожны с ней.) Запуск git checkout v1.0 -- README.txt
или git checkout a123456 -- path/to/file.ext
извлечет указанный файл из именованного коммита, копируя (разморозить) его в ваш индекс - так что теперь ваш следующий коммит будет иметь эту версию этого файла - и затем включится в ваше рабочее дерево.
Этоболее полезно, если у вас есть целый каталог файлов для восстановления или шаблон глобуса , например *.jar
, потому что вы можете git checkout
каталог или шаблон:
git checkout HEAD~2 -- '*.jar'
Здесь HEAD~2
- это фиксация для использования (два шага назад от текущего коммита по цепочкам первого родителя), и *.jar
, которая требует цитирования для защиты от оболочки, если в текущем каталоге есть файлы *.jar
, pathspec , который должен соответствовать Git.(Я думаю, это должно быть эквивалентно **/*.jar
, но если нет, то это также допустимая спецификация пути.) Поскольку это заполняет ваш индекс, вам придется отменить его позже, например, git rm --cached
снова или git reset
(чтотакже принимает pathspecs, поэтому вы можете git reset -- '*.jar'
).
Достаточно ли этих замороженных файлов для вашей текущей ситуации, конечно, зависит от ситуации.