Что именно делает .gitignore - PullRequest
0 голосов
/ 07 апреля 2020

Я сделал несколько тестов, чтобы понять, что .gitignore делает с моим репозиторием, но я все еще не могу точно определить его поведение.

Вот шаги, которые я выполнил во время теста, я хотел бы понять, если я ' m что-то не так:

  • Инициализация git репо
  • Добавление файла .gitignore

enter image description here

  • Зафиксировать его
  • Добавить «игнорируемый» файл в репо
  • Добавить «не проигнорированный» файл в репо
  • git status

Вот полный git bash log:

~/Documents/gittest
$ git init
Initialized empty Git repository in C:/Users/DoktorUzi/Documents/gittest/.git/

~/Documents/gittest (master)
$ git add .gitignore.txt
warning: LF will be replaced by CRLF in .gitignore.txt.
The file will have its original line endings in your working directory

~/Documents/gittest (master)
$ git commit -s -m "_TESTGIT"
[master (root-commit) e37661f] _TESTGIT
 1 file changed, 2 insertions(+)
 create mode 100644 .gitignore.txt

~/Documents/gittest (master)
$ git status
On branch master
nothing to commit, working tree clean

~/Documents/gittest (master)
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        README.md
        example_gitignore.txt

nothing added to commit but untracked files present (use "git add" to track)

Теперь я думаю, что я делаю что-то не так, используя gitignore, иначе я не Понимание того, как это полезно, если все файлы, новые и игнорируемые, появляются в неотслеживаемых файлах.

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

1 Ответ

0 голосов
/ 07 апреля 2020

TL; DR

Как отметили комментаторы, файл должен иметь имя .gitignore, а не .gitignore.txt.

Реальный ключ для понимания .gitignore записей, однако, понять Git х индекс . Если вы не знаете об индексе и о том, как Git использует его, некоторые вещи будут очень загадочными.

Файл, который находится в вашем рабочем дереве, но отсутствует в индексе Git, является неотслеживаемый файл . Перечисление имени неотслеживаемого файла или шаблона, соответствующего его имени, в файле .gitignore сообщает git status to shutdown об этом - не вызывать его как неотслеживаемое, а также предотвращает git add от копирования файла в индекс Git. Если файл равен в индексе Git, то перечисление имени файла в .gitignore не влияет на сохранение файла Git.

Long

Первое, что нужно понять, это то, что Git не столько о файлах или ветвях: Git - это все о commits . Каждый коммит хранит полный и полный снимок всех ваших файлов - или, по крайней мере, всех тех, что вы указываете на снимок. Как только коммит сделан, он замораживается навсегда. Этот коммит, идентифицируемый его уникальным идентификатором ha sh - большой уродливой строкой букв и цифр, например, 9fadedd637b312089337d73c3ed8447e9f0aa775 - всегда будет содержать именно эти файлы плюс метаданные о том, кто сделал совершать, когда и так далее. Они хранятся в специальном, только для чтения, Git -только формате, который может понять только Git.

Второе, что нужно понять, так это то, что файлы, хранящиеся в коммитах Git, являются только для чтения и Git -только как это, Git было бы совершенно бесполезным, если бы у него не было способа извлечь этих файлов из коммита в другое место, где вы можете работать и с ними. Эта рабочая область, где Git извлекает файлы в пригодную для использования форму - обычные файлы, которые может использовать остальная часть вашего компьютера, - это ваше рабочее дерево или рабочее дерево .

Следовательно, после выбора определенного коммита, есть два набора файлов: замороженные в коммите и редактируемые в вашем рабочем дереве. Вы не можете видеть или делать что-либо с замороженными. Все, что вы можете сделать, это использовать копии рабочего дерева, но это все, что вам нужно.

Git может остановиться здесь. Mercurial, еще одна распределенная система управления версиями (DVCS), например Git, , останавливается на . С Mercurial вы извлекаете коммит в ваше рабочее дерево, затем вносите изменения, а затем фиксируете. Mercurial создает новый коммит из всего, что есть в вашем рабочем дереве. Но Git этого не делает. Вместо этого Git вставляет третью копию каждого файла, между замороженной, Git -только копией в коммите и используемой копией в вашем рабочем дереве. 1

Эти дополнительные копии каждого файла, которые находятся в замороженном формате - но на самом деле не заморожены - представляют собой большую часть индекса. Git также называет этот индекс промежуточной областью , поскольку Git использует индекс. Копии в замороженном формате каждого файла в индексе будут тем, что входит в следующий коммит, который вы делаете.

Когда вы запускаете git commit, что делает Git это:

  • сбор метаданных, таких как ваше имя и адрес электронной почты, а также сообщение в журнале
  • добавление соответствующих материалов, таких как текущая дата и время, и идентификатор ha sh для текущий коммит в качестве родителя нового коммита
  • заморозит все файлы в индексе

и запишет это как новый коммит. Он даже не смотрит на ваше рабочее дерево: вместо этого он строит новый коммит из того, что есть в index . 2

In другими словами, простой способ описать индекс состоит в том, что он содержит файлов, которые вы предлагаете поместить в следующий сделанный вами коммит . 3 Индекс изначально заполняется из коммита что вы выбрали с git checkout или Git 2.23 или более поздней git switch. Ваше рабочее дерево, которое иначе используется для вашего , также заполняется из этих файлов. Затем вы можете вносить любые изменения в свое рабочее дерево. Копии в индексе еще не изменены!

Когда вы запускаете git add для файла рабочего дерева, Git копирует копию рабочего дерева в индекс , сжимая его в специальный Git -только формат в процессе. Этот файл перезаписывает все, что было в индексе под этим именем.

Если файл является совершенно новым для хранилища, т. Е. Никогда не был замечен ранее, он еще не будет в индексе . Если файл находится в каком-то коммите в репозитории, но не в коммите, который вы выбрали с git checkout, файл также не будет в индексе. В этом случае git add записывает файл в индекс, и теперь он находится в индексе. Таким образом, индекс может содержать файлы, которые не вообще не выходят из коммита, или он может содержать обновленные копии файлов, которые * выходят из коммита.

Вы также можете удалить файл из индекса. Запуск git rm <em>path</em> удаляет как индексную копию именованного файла , так и копию рабочего дерева. Теперь, когда файл отсутствует в индексе, его нет в предлагаемом следующем коммите. Если вы делаете коммит сейчас, он не будет быть в коммите.

Когда вы запускаете git status, Git начинается со сравнения текущего коммита с содержимым его индекса. Для каждого файла, который здесь тот же , Git ничего не говорит. Для каждого файла, который отличается , Git говорит, что этот файл подготовлен для фиксации . Файл, который пропал без вести - который находится в текущем коммите, но не в индексе - готовится к удалению, а файл, новый в индексе, помещается в новый файл.

Git затем, однако, выполняется сравнение second . На этот раз он сравнивает файлы в индексе с файлами в вашем рабочем дереве. Опять же, каждый файл может либо совпадать, либо отличаться, либо отсутствовать, либо в вашем рабочем дереве могут быть файлы, которых вообще нет в вашем индексе. Для файла, который соответствует, Git вообще ничего не говорит. Для файла, который не соответствует, Git говорит, что не подготовлен для фиксации . Для файла, который отсутствует в вашем рабочем дереве, Git говорит, что удаление не подготовлено для фиксации. Особенность здесь в том, что если файл новый , Git не говорит "новый", он говорит вместо этого без отслеживания .

Примечание. : Git копии файлов в индексе Git просто имеют длинные имена - например, path/to/file.ext - это просто имя файла, а не папка path, содержащая подпапку с именем to, содержащая файл с именем file.ext. Вот почему вы не можете хранить пустой каталог в Git: Git вообще не хранит каталоги, так как нет индексной записи типа "directory", а Git создает новые коммиты из того, что находится в его индексе , Если бы вы могли вставить пустой каталог в индекс, Git мог бы хранить пустой каталог. Но вы можете получить в индекс только файлы, символические c ссылки и gitlinks (информация о субмодуле). 4 Всякий раз, когда Git отправляется для извлечения коммита, содержащего file с именем path/to/file.ext, Git знает, как создать необходимые каталоги / папки path и path/to, если необходимо.


1 Технически индекс содержит ссылки на Git объекты BLOB-объектов , а не на фактические копии данных файла. Однако если вы не начнете использовать команды низкого уровня git ls-files --stage и git update-index, вам не нужно заботиться об этом различии. Достаточно думать, что индекс содержит третью копию каждого файла.

2 Если вы используете git commit -a, Git, по сути, сначала запустит git add -u, до совершения. Здесь есть ряд скрытых проблем, о которых я не буду рассказывать, но при этом автоматически добавляются только те файлы, которые уже включены в индекс . Таким образом, только почти позволяет вам не заботиться об индексе: он падает в нескольких местах, включая случай совершенно нового файла.

3 Это несколько упрощенно. Индекс имеет дополнительные функции. Самая большая из них - та, с которой вы в конечном итоге столкнетесь и о которой нужно будет знать, - это то, что индекс играет расширенную роль во время конфликтующего слияния. Здесь мы не будем вдаваться в подробности.

4 Кто-то заметил, что это предоставляет хитрый способ Git хранить пустой каталог. Поскольку подмодуль сам по себе является каталогом, вы просто сохраняете в качестве подмодуля ссылку на полностью пустой дополнительный репозиторий. Без коммитов в этом хранилище ничто не может быть извлечено в этот подкаталог. Это дает вам пустой каталог. Это работает , но выглядит немного странно.


Игнорирование - то есть не жалоба и не добавление - файлы автоматически

Git часто используется сохранить исходные файлы для скомпилированных (или байтово скомпилированных, как в Python) языков. Эти компиляторы, как правило, записывают множество файлов, которые вы не хотите хранить с контролем версий. Удобно иметь возможность сказать: игнорировать все *.o файлы , например, или игнорировать все файлы, найденные в __pycache__ каталогах .

Git Файлы .gitignore являются механизмом для этого. Их формат немного сложен, потому что операционные системы обычно хранят файлы в виде записей в каталогах , или , папках , и Git должен вместить эти ОС. Итак:

  • Имя без специальных символов означает игнорировать любой файл с этим именем, даже если он появляется в каком-либо подкаталоге .
  • Имя с ведущим sla sh, как в /program-x, означает игнорировать файл с этим именем в этом каталоге, но не в подкаталогах .
  • Имя со встроенным sla sh, как в sub/program-x, означает игнорировать файл с именем sub/program-x, как видно из этого каталога . Это не будет игнорировать other/program-x и игнорировать sub/sub/program-x: здесь учитывается только sub/program-x.
  • Имя формы sub/ означает игнорировать каталог (папка aka) с этим именем, но не файл с этим именем . Это означает, что файлы в a/sub/ также игнорируются, но файл с именем b/sub не игнорируется. (Само это правило немного странно, но очень полезно для приведенных ниже правил отрицания.) Добавление ведущего sla sh «привязывает» имя к этому каталогу.
  • Last, начиная запись с ! говорит Git не игнорировать это в конце концов. Это особенно полезно с этими шаблонами каталогов.

Кроме того, вы можете добавить символ шаблона глобуса *, чтобы он соответствовал любому файлу, например, или другим символам стиля оболочки-глобуса, как перечислены в документации gitignore .

Предположим, например, что вы хотите, чтобы Git не жаловался на неотслеживаемые файлы где-либо внутри vendor/ , за исключением для любой файл, который вы положили в vendor/patches/. Для этого вы должны убедиться, что Git сканирует каталог с именем vendor для файлов и подкаталогов, что вы делаете, написав:

vendor/*
!vendor/patches/

Первая строка игнорирует все вещи - каталоги и файлы - внутри vendor, но все равно сканирует vendor сам. Вторая строка говорит, что когда Git встречается с vendor/patches и это каталог, делает сканирует его и не игнорирует его файлы. Более поздняя строка ! переопределяет более раннюю строку *. Следовательно, каталог с именем vendor/a/ и файл с именем vendor/description оба игнорируются, но vendor/patches/ ищется для файлов, на которые можно пожаловаться, что они не отслеживаются.

En-masse add операции

Когда вы запускаете git add, вы можете дать ему отдельные имена файлов: например,

git add main.py
git add README.md

. Но вы также можете выполнять массовые операции «добавить все»:

git add .

или:

git add *

Они немного отличаются, когда ваша оболочка выполняет расширение *, так как git add * не вызовет git add для файла с именем, например, .pylintrc, но git add . будет см. .pylintrc и попытается добавить его.

Эти массовые операции добавляют каждый файл, который Git может найти таким образом, за исключением , что если файл в настоящий момент не отслежен - находится в вашем рабочем дереве, но не в индексе - это git add будет пропускать файла, а не добавлять его.

Отсюда и название .gitignore немного al ie

Лучшее имя для этого файла будет .git-do-not-complain-about-untracked-files-and-do-not-auto-add-these-files-when-using-an-en-masse-add-operation. Но представьте, что вы пытаетесь создать это имя файла в новом хранилище.

Здесь есть еще одна тонкая особенность / ошибка: перечисление файла или шаблона в .gitignore дает Git разрешение на clobber файл в некоторых угловых случаях. Они появляются не очень часто, но когда они появляются, они могут раздражать. Было бы неплохо, если бы вы могли пометить определенные файлы как игнорируемыми, но ценными: никогда не перезаписывайте и не удаляйте этот файл . Подробнее см. В этой довольно длинной почтовой ветке.

Файлы Gitignore могут появляться в подкаталогах

Довольно часто встречается один файл .gitignore верхнего уровня, содержащий имена скомпилированные двоичные файлы и объектные файлы (*.o, *.a, *.so, et c.). Но вы также можете разрешить подкаталогу специально игнорировать некоторые файлы. Например, предположим, у вас есть большой проект с библиотекой и куча разных программ, которые используют библиотеку. Каждая скомпилированная программа может быть подкаталогом репозитория верхнего уровня, причем библиотека также является подкаталогом. В каталоге каждой программы имеет смысл создать .gitignore на этом уровне, который игнорирует исполняемый образ скомпилированной программы. Таким образом, верхний уровень .gitignore перечисляет только те вещи, которые являются общими для всех подкаталогов. Файл lib/.gitignore может содержать шаблон *.a, поскольку в каталогах программ нет встроенных архивов. Каталог документации может содержать список созданных программой (не созданных человеком) файлов перекрестных ссылок или других сгенерированных файлов, которые не отображаются в каталогах, не относящихся к документации, и т. Д.

empty .gitignore файл разрешен: он ничего не добавляет к набору вещей, которые следует игнорировать, но может занимать пустой каталог. Вы можете добавить этот файл .gitignore к набору файлов в индексе Git, чтобы Git создавал каталог позже, при проверке этого коммита.

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