Чтобы расширить ответ Бена Джексона , и это нормально, давайте внимательно рассмотрим исходный вопрос. (Смотрите его ответ, чтобы узнать, зачем беспокоить типовые вопросы; это больше о том, что происходит .)
Я новичок в управлении версиями и понимаю, что «фиксация» - это, по сути, создание резервной копии при обновлении новой «текущей» версии того, над чем вы работаете.
Это не совсем так . Резервное копирование и контроль версий, безусловно, связаны - насколько сильно зависит от некоторых вещей, которые в какой-то степени являются предметом мнения, - но, безусловно, есть некоторые различия, хотя бы по назначению: резервные копии обычно предназначены для аварийного восстановления (сбой машины, пожар уничтожает все здание, включая все носители информации и т. д.). Контроль версий обычно предназначен для более детального взаимодействия и предлагает функции, которых нет в резервных копиях. Резервные копии обычно хранятся в течение некоторого времени, а затем отбрасываются как «слишком старые»: все, что имеет значение, - это более свежие резервные копии. Контроль версий обычно сохраняет каждую подтвержденную версию навсегда.
Я не понимаю, для чего постановка с практической точки зрения. Постановка чего-то, что существует только по названию, или служит определенной цели? Когда вы фиксируете, он все равно все равно фиксирует, верно?
Да и нет. Дизайн Git здесь несколько своеобразный. Существуют системы контроля версий, которые не требуют отдельного этапа подготовки. Например, Mercurial, который в остальном очень похож на Git с точки зрения использования, не требует отдельного hg addшага, кроме самого первого, который вводит полностью новый файл. В Mercurial вы используете hgкоманду, которая выбирает какую-то фиксацию, затем вы делаете свою работу, затем запускаете hg commit, и все готово. В Git вы используете git checkout, 1, затем выполняете свою работу, затем запускаете git add, а затем git commit. Почему лишняя git addступенька?
Секрет здесь в том, что Git называет, по-разному, индексом или промежуточной областью , а иногда - редко в наши дни - кешем . Все это названия одного и того же.
Изменить: я думаю, что могу запутать терминологию. «Поэтапный» файл - это то же самое, что «отслеживаемый» файл?
Нет, но они связаны. Отслеживаются файл один , который существует в индексе Git и . Чтобы правильно понять индекс, хорошо начать с понимания коммитов.
Начиная с версии Git 2.23, вы можете использовать git switchвместо git checkout. В данном конкретном случае эти две команды делают одно и то же. Новая команда существует потому, что git checkoutона перегружена множеством вещей; они были разделены на две отдельные команды git switchи git restore, чтобы было проще и безопаснее использовать Git.
Совершает
В Git коммит сохраняет полный снимок каждого файла, о котором знает Git. (О каких файлах знает Git? Мы увидим это в следующем разделе.) Эти снимки хранятся в специальной, доступной только для чтения, только Git, сжатой и дедуплицированной форме, которую, как правило, может читать только сам Git. . (В каждом коммите есть больше вещей, чем просто этот снимок, но это все, что мы здесь рассмотрим.)
Дедупликация помогает с пространством: обычно мы изменяем только несколько файлов, а затем делаем новую фиксацию. Таким образом, большинство файлов в коммите в основном такие же, как файлы в предыдущем коммите. Просто повторно используя эти файлы напрямую, Git экономит много места: если мы коснулись только одного файла, новая фиксация займет место только для одной новой копии. Даже в этом случае он сжимается - иногда очень сжат, хотя на самом деле это происходит позже, - так что .gitкаталог может быть меньше, чем файлы, которые он содержит, после того, как они расширены до обычных повседневных файлов. Дедупликация безопасна, поскольку зафиксированные файлы замораживаются навсегда. Никто не может изменить один, поэтому фиксации могут зависеть от копий друг друга.
Однако, поскольку хранящиеся файлы находятся в этом специальном, замороженном на все время формате, предназначенном только для Git, Git должен расширять каждый файл в обычную повседневную копию. Эта обычная копия не является копией Git : это ваша копия, которую вы можете использовать. Git просто напишет им, когда вы ему скажете, чтобы у вас были копии для работы. Эти используемые копии находятся в вашем рабочем дереве или рабочем дереве .
Это означает, что когда вы проверяете конкретный коммит, автоматически создаются две копии каждого файла:
В текущем коммите Git имеет замороженную на все время копию Git-ified . Вы не можете изменить эту копию (хотя вы, конечно, можете выбрать другую фиксацию или сделать новую фиксацию).
В вашем рабочем дереве есть копия в нормальном формате. Вы можете делать с этим все, что захотите, используя любую из команд на вашем компьютере.
Другие системы контроля версий (включая Mercurial, упомянутые выше) останавливаются на этих двух копиях. Вы просто изменяете свою копию рабочего дерева, а затем фиксируете. Гит ... нет.
Индекс
Между этими двумя копиями Git хранит третью копию 2 каждого файла. Эта третья копия находится в замороженном формате , но, в отличие от замороженной копии в фиксации, вы можете изменить ее. Чтобы изменить это, вы используете git add.
Команда git addозначает, что индексная копия файла должна соответствовать копии рабочего дерева . То есть вы говорите Git: замените замороженный формат, дедуплицированную копию, которая сейчас находится в индексе, сжав мою обновленную копию рабочего дерева, дедуплицируя ее и подготовив ее к замораживанию в новом коммите. Если вы не используете git add, индекс по-прежнему содержит копию в замороженном формате из текущего коммита.
Когда вы запускаете git commit, Git упаковывает все, что находится в индексе, сразу для использования в качестве нового снимка. Поскольку он уже находится в замороженном формате и предварительно дедуплицирован, Git не нужно выполнять много дополнительной работы.
Это также объясняет, что такое неотслеживаемые файлы . Неотслеживаемый файл представляет собой файл , который находится в рабочем дереве , но не в индексе Git и сейчас . Неважно, как файл оказался в таком состоянии. Может быть, вы скопировали его из другого места на своем компьютере в свое рабочее дерево. Может быть, вы создали его здесь свежим. Возможно, в индексе Git была копия, но вы удалили эту копию с помощью git rm --cached. Так или иначе, в вашем рабочем дереве есть копия, но ее нет в индексе Git. Если вы сделаете новую фиксацию сейчас, этого файла не будет в новой фиксации.
Обратите внимание, что git checkoutизначально индекс Git заполняется из проверенной фиксации. Итак, индекс начинает соответствовать фиксации. Git также заполняет ваше рабочее дерево из того же источника. Итак, изначально все три совпадают. Когда вы меняете файлы в своем рабочем дереве и git addих, ну, теперь индекс и ваше рабочее дерево совпадают. Затем вы запускаете, git commitи Git делает новую фиксацию из индекса, и теперь все три снова совпадают.
Поскольку Git делает новые коммиты из индекса, мы можем сказать это так: индекс Git содержит следующую фиксацию, которую вы планируете сделать. Это игнорирует расширенную роль, которую индекс Git берет на себя во время конфликтного слияния, но мы все равно хотели бы игнорировать это сейчас. :-)
Вот и все, но это все еще довольно сложно! Это особенно сложно, потому что нет простого способа точно увидеть, что находится в индексе Git. 3 Но это команда Git , которая говорит вам , что происходит, таким образом , что это очень полезно, и что команда git status.
2 Технически это вообще не копия . Вместо этого это ссылка на файл Git-ified, предварительно дедуплицированный и все такое. Здесь также есть больше вещей, таких как режим, имя файла, номер стадии и некоторые данные кеша, чтобы Git работал быстрее. Но если вы не начнете работать с некоторыми низкоуровневыми командами Git - git ls-files --stageи git update-indexв частности - вы можете просто думать об этом как о копии.
3 Команда git ls-files --stageпокажет вам имена и промежуточные номера каждого файла в индексе Git, но обычно это все равно не очень полезно.
git status
На git statusсамом деле команда работает, выполняя git diffза вас две отдельные команды (а также делая некоторые другие полезные вещи, например, сообщая вам, в какой ветке вы находитесь).
Первый git diffсравнивает текущую фиксацию - которая, помните, заморожена на все время - с тем, что есть в индексе Git. Для одинаковых файлов Git вообще ничего не скажет. Для разных файлов Git сообщит вам, что этот файл подготовлен для фиксации . Это включает в себя все новые файлы, если обязательство не sub.pyв нем, но индекс действительно есть sub.pyв нем, то добавляется, и этот файл любые удаленные файлы, которые были (и есть) в фиксации , но не в индекса уже нет ( git rm, возможно).
Второй git diffсравнивает все файлы в индексе Git с файлами в вашем рабочем дереве. Для одинаковых файлов Git вообще ничего не говорит. Для разных файлов Git сообщит вам, что этот файл не предназначен для фиксации . В отличие от первого сравнения, этот конкретный список не включает файлы, которые являются полностью новыми: если файл untrackedсуществует в вашем рабочем дереве, но не в индексе Git, Git просто добавляет его в список неотслеживаемых файлов . 4
В конце, собрав эти неотслеживаемые файлы в списке, также git statusбудут объявлены имена этих файлов, но есть специальное исключение: если имя файла указано в .gitignoreфайле, этот последний список будет подавлен. Обратите внимание, что перечисление отслеживаемого файла - того, что находится в индексе Git - .gitignoreздесь не имеет никакого эффекта : файл находится в индексе, поэтому он сравнивается и фиксируется, даже если он указан в .gitignore. Файл игнорирования подавляет только жалобы на "неотслеживаемый файл". 5
4 При использовании краткой версии git status- git status -s- неотслеживаемые файлы не отделены друг от друга, но принцип тот же. git statusПодобное накопление файлов также позволяет суммировать имена нескольких неотслеживаемых файлов, иногда просто печатая имя каталога. Чтобы получить полный список, используйте git status -uallили git status -u.
5 Листинг файла также заставляет массово добавлять множество файловых операций, таких как неотслеживаемый файл git add .или git add *пропускать его. Эта часть становится немного сложнее, так как вы можете использовать git add --forceдля добавления файла, который обычно пропускается. Есть несколько других обычно незначительных особых случаев, все из которых складываются в это: файл .gitignoreможет называться более правильно .git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-themили что-то столь же громоздкое. Но это слишком смешно, так .gitignoreоно и есть.
git add -u, git commit -aи т. д.
Здесь есть несколько удобных ярлыков:
git add .добавит все обновленные файлы в текущий каталог и любой подкаталог. Это соблюдается .gitignore, поэтому, если файл, который в настоящее время не отслеживается, не получил жалобу git status, он не будет добавлен автоматически.
git add -uавтоматически добавит все обновленные файлы в любое место вашего рабочего дерева . 6 Это влияет только на отслеживаемые файлы. Обратите внимание, что если вы удалили копию рабочего дерева, это также удалит копию индекса ( git addэто как часть его соответствия индексу с деревом работы ).
git add -Aэто похоже на бег git add .с верхнего уровня вашего рабочего дерева (но см. сноску 6).
Помимо этого, вы можете бегать git commit -a, что примерно эквивалентно 7 бегу git add -uи затем git commit. То есть это дает вам то же поведение, которое удобно в Mercurial.
Я обычно не рекомендую использовать этот git commit -aшаблон: я считаю, что его лучше использовать git statusпочаще, внимательно посмотрите на вывод и, если статус не соответствует вашим ожиданиям, выясните, почему это так. При git commit -aиспользовании слишком легко случайно изменить файл и зафиксировать изменение, которое вы не собирались фиксировать. Но это в основном вопрос вкуса / мнения.
6 Если ваша версия Git предшествует Git 2.0, будьте осторожны: git add -uработает только с текущим каталогом и подкаталогами, поэтому вы должны сначала подняться на верхний уровень своего рабочего дерева. У git add -Aварианта есть аналогичная проблема.
7 Я говорю примерно эквивалентно, потому что на git commit -aсамом деле работает путем создания дополнительного индекса и использования этого другого индекса для фиксации. Если фиксация работает , вы получите тот же эффект, что и выполнение git add -u && git commit. Если фиксация не работает - если вы заставляете Git пропускать фиксацию любым из множества способов, которыми вы можете это сделать, - после этого никакие файлы не обрабатываются git add, потому что Git выбрасывает временный дополнительный индекс и возвращается к использованию основного индекса .
Если вы воспользуетесь git commit --onlyздесь , возникнут дополнительные сложности . В этом случае Git создает третий индекс, и все становится очень сложно, особенно если вы используете перехватчики перед фиксацией. Это еще одна причина использовать отдельные git addоперации.