Что такое область подготовленных файлов или индекс в git

Обновлено: 04.07.2024

Что именно содержит индекс Git и какую команду я могу использовать для просмотра содержимого индекса?

Обновление

Спасибо за все ваши ответы. Я знаю, что индекс действует как промежуточная область, а то, что зафиксировано, находится в индексе, а не в рабочем дереве. Мне просто интересно, из чего состоит объект индекса. Я думаю, это может быть список имени файла/каталога, пары SHA-1, своего рода виртуальное дерево, возможно?

Есть ли в терминологии Git любая команда сантехники, которую я могу использовать для отображения содержимого индекса?

Индекс представляет собой двоичный файл (обычно хранящийся в .git/index ), содержащий отсортированный список имен путей, каждый с разрешениями и SHA1 объекта BLOB-объекта; git ls-files может показать вам содержимое индекса:

Проблема Racy git дает более подробную информацию об этой структуре:

Индекс является одной из самых важных структур данных в git.
Он представляет состояние виртуального рабочего дерева путем записи списка путей и имен их объектов и служит промежуточной областью для записи следующего объекта дерева, который будет зафиксирован.
Состояние является "виртуальным" в том смысле, что оно не обязательно должно и часто не совпадает с файлами в рабочем дереве.

Индексный файл Git имеет следующий формат

  • 12-байтовый заголовок, состоящий из:
    • 4-байтовая подпись:
      Подпись: (расшифровывается как " dircache ")
    • 4-байтовый номер версии:
      В настоящее время поддерживаются версии 2, 3 и 4.
    • 32-битное количество записей индекса.
    • 4-байтная подпись расширения. Если первый байт ' A '.. ' Z ' расширение является необязательным и может быть проигнорировано.
    • 32-битный размер расширения
    • Данные расширения

    Если индекс - это место, где готовится следующий коммит, почему " git ls-files -s " ничего не возвращает после коммита?

    Поскольку индекс представляет то, что отслеживается, и сразу после коммита то, что отслеживается, совпадает с последним коммитом ( git diff --cached ничего не возвращает).

    Итак, git ls-files -s перечисляет все отслеживаемые файлы (имя объекта, биты режима и номер этапа в выходных данных).

    Этот список (отслеживаемых элементов) инициализируется содержимым коммита.
    Когда вы переключаете ветку, содержимое индекса сбрасывается до коммита, на который ссылается ветка, на которую вы только что переключились.

    Git 2.20 (Q4 2018) добавляет таблицу смещения записи индекса (IEOT):

    ieot: добавить расширение таблицы смещения записи индекса (IEOT)

    Этот патч позволяет определить стоимость загрузки индекса процессором, добавив к нему дополнительные данные, что позволит нам multi- эффективно обрабатывать загрузку и преобразование записей кэша.

    Это достигается путем добавления (необязательного) расширения индекса, которое представляет собой таблицу смещений, к блокам записей кэша в файле индекса.

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

    В результате (с использованием IEOT) совершить 7bd9631 очистить функцию read-cache.c load_cache_entries_threaded() .

    read-cache: удалить неиспользуемый параметр из многопоточной загрузки

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

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

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

    Во-вторых, что вероятно даже важнее, откат коммита по любым причинам приведет к тому, что откатятся правки, которые всё равно нужно будет делать.

    Именно здесь помогает индекс. Его наличие позволяет меньше переживать на тему того, как сформируется коммит.

    Стандартный способ работы с индексом — это добавление или изменение файлов и последующий коммит:

    Если речь идет про один-два файла, которые нужно закоммитить прямо сейчас, то можно сделать проще. Команда git commit принимает на вход аргументы — пути до файлов. Она автоматически добавляет эти файлы в индекс и затем в коммит. Данный подход работает только с уже отслеживаемыми файлами.

    Иногда бывает наоборот — мы исправили много файлов и хотим добавить их в коммит сразу все. Тогда поможет точка:

    Команда выше очень опасна. С ее помощью крайне легко закоммитить много лишнего, особенно если не помнить про необходимость перед коммитом смотреть git diff --staged .

    Ну и совсем страшная, но полезная команда — это коммит с одновременным добавлением всего в индекс:

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

    Самостоятельная работа

    Попрактикуйтесь в использовании команд из урока, меняя содержимое репозитория

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

    Если вы выполните git add с опцией -i или --interactive , Git перейдёт в интерактивный консольный режим, отобразив что-то подобное:

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

    Затем следует раздел со списком команд. С их помощью вы можете выполнить множество вещей — добавить или исключить файлы из индекса, добавить в индекс части файлов, добавить в индекс неотслеживаемые файлы и просмотреть проиндексированные изменения.

    Добавление и удаление файлов из индекса

    Если вы введете 2 или u в поле ввода What now> , скрипт спросит у вас какие файлы вы хотите добавить в индекс:

    Для добавления в индекс файлов TODO и index.html, вы можете ввести их номера:

    Символ * у каждого из этих файлов означает, что файл выбран для индексирования. Если вы нажмёте Enter, не вводя ничего в поле ввода Update>> , Git добавит в индекс всё, чтобы было выбрано ранее:

    Как вы можете заметить, сейчас файлы TODO и index.html добавлены в индекс, а файл simplegit.rb всё ещё нет. Если вы в этот момент хотите исключить файл TODO из индекса, вы можете использовать опции 3 или r (для выполнения revert):

    Посмотрев снова на состояние вашего рабочего каталога Git, вы увидите, что файл TODO исключён из индекса:

    Для того, чтобы посмотреть изменения, которые вы добавили в индекс, вы можете использовать команду 6 и d (для выполнения diff). Она покажет вам список добавленных в индекс файлов, и вы можете выбрать один из них, для которого вы хотите увидеть добавленные в индекс изменения. Это очень похоже на вывод команды git diff --cached :

    Использование этих основных команд в интерактивном режиме команды add, может упростить вам работу с вашей областью подготовленных изменений.

    Индексирование по частям

    В Git существует возможность индексировать не только файлы целиком, но и некоторые их части. Например, если вы сделали в файле simplegit.rb два изменения и хотите добавить в индекс только одно из них, добиться этого в Git очень легко. В поле ввода в режиме интерактивного индексирования введите 5 или p (для выполнения patch). Git спросит у вас какие файлы вы хотите добавить в индекс частично; а затем для каждой части выбранных файлов он будет показывать изменения в ней и спрашивать хотите ли вы добавить в индекс эту часть:

    В этой точке у вас есть множество вариантов дальнейших действий. Если вы введёте ? , Git отобразит, что именно вы можете сделать:

    Обычно вы будете вводить y или n , если вы хотите индексировать каждую часть по отдельности, но индексация всех частей в некоторых файлах или откладывание решения по индексацию части также может быть полезным. Если вы добавили в индекс одну часть файла, но не добавили другую, состояние вашего рабочего каталога будет подобно приведённому далее:

    Обратите внимание на состояние файла simplegit.rb. Оно говорит вам, что часть строк файла добавлена в индекс, а часть нет. Таким образом, вы частично проиндексировали этот файл. В данный момент вы можете выйти из интерактивного режима команды git add и выполнить git commit , чтобы зафиксировать частично проиндексированные файлы.

    Также вам не обязательно находиться в интерактивном режиме индексирования файлов для выполнения частичной индексации файлов — вы также можете запустить её, используя команды git add -p или git add --patch .

    Более того, вы можете использовать работу с отдельными частями файлов для частичного восстановления файлов с помощью команды reset --patch , для переключения частей файлов с помощью команды checkout --patch и для припрятывания частей файлов с помощью stash save --patch . Мы рассмотрим каждую из этих команд более подробно, когда будем изучать более продвинутые варианты их использования.

    В последние годы популярность git демонстрирует взрывной рост. Эта система контроля версий используется различными проектами с открытым исходным кодом.

    Новичков часто пугает большое количество замысловатых команд и сложных аргументов. Но для начала все они и не нужны. Можно начать с изучения наиболее часто используемых команд, и после этого постепенно расширять свои знания. Именно так мы и поступим в этой статье. Поехали!

    Git — это набор консольных утилит, которые отслеживают и фиксируют изменения в файлах (чаще всего речь идет об исходном коде программ, но вы можете использовать его для любых файлов на ваш вкус). С его помощью вы можете откатиться на более старую версию вашего проекта, сравнивать, анализировать, сливать изменения и многое другое. Этот процесс называется контролем версий. Существуют различные системы для контроля версий. Вы, возможно, о них слышали: SVN, Mercurial, Perforce, CVS, Bitkeeper и другие.

    Git является распределенным, то есть не зависит от одного центрального сервера, на котором хранятся файлы. Вместо этого он работает полностью локально, сохраняя данные в папках на жестком диске, которые называются репозиторием. Тем не менее, вы можете хранить копию репозитория онлайн, это сильно облегчает работу над одним проектом для нескольких людей. Для этого используются сайты вроде github и bitbucket.

    Установить git на свою машину очень просто:

    • Linux — нужно просто открыть терминал и установить приложение при помощи пакетного менеджера вашего дистрибутива. Для Ubuntu команда будет выглядеть следующим образом:
    • Windows — мы рекомендуем git for windows, так как он содержит и клиент с графическим интерфейсом, и эмулятор bash.
    • OS X — проще всего воспользоваться homebrew. После его установки запустите в терминале:

    Если вы новичок, клиент с графическим интерфейсом(например GitHub Desktop и Sourcetree) будет полезен, но, тем не менее, знать команды очень важно.

    Итак, мы установили git, теперь нужно добавить немного настроек. Есть довольно много опций, с которыми можно играть, но мы настроим самые важные: наше имя пользователя и адрес электронной почты. Откройте терминал и запустите команды:

    Теперь каждое наше действие будет отмечено именем и почтой. Таким образом, пользователи всегда будут в курсе, кто отвечает за какие изменения — это вносит порядок.

    Как мы отметили ранее, git хранит свои файлы и историю прямо в папке проекта. Чтобы создать новый репозиторий, нам нужно открыть терминал, зайти в папку нашего проекта и выполнить команду init. Это включит приложение в этой конкретной папке и создаст скрытую директорию .git, где будет храниться история репозитория и настройки.
    Создайте на рабочем столе папку под названием git_exercise. Для этого в окне терминала введите:

    Командная строка должна вернуть что-то вроде:

    Это значит, что наш репозиторий был успешно создан, но пока что пуст. Теперь создайте текстовый файл под названием hello.txt и сохраните его в директории git_exercise.

    status — это еще одна важнейшая команда, которая показывает информацию о текущем состоянии репозитория: актуальна ли информация на нём, нет ли чего-то нового, что поменялось, и так далее. Запуск git status на нашем свежесозданном репозитории должен выдать:

    В git есть концепция области подготовленных файлов. Можно представить ее как холст, на который наносят изменения, которые нужны в коммите. Сперва он пустой, но затем мы добавляем на него файлы (или части файлов, или даже одиночные строчки) командой add и, наконец, коммитим все нужное в репозиторий (создаем слепок нужного нам состояния) командой commit.
    В нашем случае у нас только один файл, так что добавим его:

    Если нам нужно добавить все, что находится в директории, мы можем использовать

    Проверим статус снова, на этот раз мы должны получить другой ответ:

    Коммит представляет собой состояние репозитория в определенный момент времени. Это похоже на снапшот, к которому мы можем вернуться и увидеть состояние объектов на определенный момент времени.
    Чтобы зафиксировать изменения, нам нужно хотя бы одно изменение в области подготовки (мы только что создали его при помощи git add), после которого мы может коммитить:

    Сейчас наш коммит является локальным — существует только в директории .git на нашей файловой системе. Несмотря на то, что сам по себе локальный репозиторий полезен, в большинстве случаев мы хотим поделиться нашей работой или доставить код на сервер, где он будет выполняться.

    1. Подключение к удаленному репозиторию

    Проект может иметь несколько удаленных репозиториев одновременно. Чтобы их различать, мы дадим им разные имена. Обычно главный репозиторий называется origin.

    Сейчас самое время переслать наш локальный коммит на сервер. Этот процесс происходит каждый раз, когда мы хотим обновить данные в удаленном репозитории.
    Команда, предназначенная для этого - push. Она принимает два параметра: имя удаленного репозитория (мы назвали наш origin) и ветку, в которую необходимо внести изменения (master — это ветка по умолчанию для всех репозиториев).

    В зависимости от сервиса, который вы используете, вам может потребоваться аутентифицироваться, чтобы изменения отправились. Если все сделано правильно, то когда вы посмотрите в удаленный репозиторий при помощи браузера, вы увидите файл hello.txt

    3. Клонирование репозитория

    Сейчас другие пользователи GitHub могут просматривать ваш репозиторий. Они могут скачать из него данные и получить полностью работоспособную копию вашего проекта при помощи команды clone.

    Новый локальный репозиторий создается автоматически с GitHub в качестве удаленного репозитория.

    4. Запрос изменений с сервера

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

    Так как новых коммитов с тех пор, как мы склонировали себе проект, не было, никаких изменений доступных для скачивания нет.

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

    • Уже рабочая, стабильная версия кода сохраняется.
    • Различные новые функции могут разрабатываться параллельно разными программистами.
    • Разработчики могут работать с собственными ветками без риска, что кодовая база поменяется из-за чужих изменений.
    • В случае сомнений, различные реализации одной и той же идеи могут быть разработаны в разных ветках и затем сравниваться.

    1. Создание новой ветки

    Основная ветка в каждом репозитории называется master. Чтобы создать еще одну ветку, используем команду branch <name>

    Это создаст новую ветку, пока что точную копию ветки master.

    2. Переключение между ветками

    Сейчас, если мы запустим branch, мы увидим две доступные опции:

    master — это активная ветка, она помечена звездочкой. Но мы хотим работать с нашей “новой потрясающей фичей”, так что нам понадобится переключиться на другую ветку. Для этого воспользуемся командой checkout, она принимает один параметр — имя ветки, на которую необходимо переключиться.

    3. Слияние веток

    Наша “потрясающая новая фича” будет еще одним текстовым файлом под названием feature.txt. Мы создадим его, добавим и закоммитим:

    Изменения завершены, теперь мы можем переключиться обратно на ветку master.

    Теперь, если мы откроем наш проект в файловом менеджере, мы не увидим файла feature.txt, потому что мы переключились обратно на ветку master, в которой такого файла не существует. Чтобы он появился, нужно воспользоваться merge для объединения веток (применения изменений из ветки amazing_new_feature к основной версии проекта).

    Теперь ветка master актуальна. Ветка amazing_new_feature больше не нужна, и ее можно удалить.

    В последней части этого руководства мы расскажем о некоторых дополнительных трюках, которые могут вам помочь.

    1. Отслеживание изменений, сделанных в коммитах

    У каждого коммита есть свой уникальный идентификатор в виде строки цифр и букв. Чтобы просмотреть список всех коммитов и их идентификаторов, можно использовать команду log:
    [spoiler title='Вывод git log']

    [/spoiler]
    Как вы можете заметить, идентификаторы довольно длинные, но для работы с ними не обязательно копировать их целиком — первых нескольких символов будет вполне достаточно. Чтобы посмотреть, что нового появилось в коммите, мы можем воспользоваться командой show [commit]
    [spoiler title='Вывод git show']

    [/spoiler]
    Чтобы увидеть разницу между двумя коммитами, используется команда diff (с указанием промежутка между коммитами):
    [spoiler title='Вывод git diff']

    [/spoiler]
    Мы сравнили первый коммит с последним, чтобы увидеть все изменения, которые были когда-либо сделаны. Обычно проще использовать git difftool, так как эта команда запускает графический клиент, в котором наглядно сопоставляет все изменения.

    2. Возвращение файла к предыдущему состоянию

    Гит позволяет вернуть выбранный файл к состоянию на момент определенного коммита. Это делается уже знакомой нам командой checkout, которую мы ранее использовали для переключения между ветками. Но она также может быть использована для переключения между коммитами (это довольно распространенная ситуация для Гита - использование одной команды для различных, на первый взгляд, слабо связанных задач).
    В следующем примере мы возьмем файл hello.txt и откатим все изменения, совершенные над ним к первому коммиту. Чтобы сделать это, мы подставим в команду идентификатор нужного коммита, а также путь до файла:

    3. Исправление коммита

    Для остальных будем использовать идентификаторы:

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

    4. Разрешение конфликтов при слиянии

    Помимо сценария, описанного в предыдущем пункте, конфликты регулярно возникают при слиянии ветвей или при отправке чужого кода. Иногда конфликты исправляются автоматически, но обычно с этим приходится разбираться вручную — решать, какой код остается, а какой нужно удалить.
    Давайте посмотрим на примеры, где мы попытаемся слить две ветки под названием john_branch и tim_branch. И Тим, и Джон правят один и тот же файл: функцию, которая отображает элементы массива.
    Джон использует цикл:

    Тим предпочитает forEach:

    Система не смогла разрешить конфликт автоматически, значит, это придется сделать разработчикам. Приложение отметило строки, содержащие конфликт:
    [spoiler title='Вывод']

    [/spoiler]
    Над разделителем ======= мы видим последний (HEAD) коммит, а под ним - конфликтующий. Таким образом, мы можем увидеть, чем они отличаются и решать, какая версия лучше. Или вовсе написать новую. В этой ситуации мы так и поступим, перепишем все, удалив разделители, и дадим git понять, что закончили.

    Когда все готово, нужно закоммитить изменения, чтобы закончить процесс:

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

    5. Настройка .gitignore

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

    1. Создайте вручную файл под названием .gitignore и сохраните его в директорию проекта.
    2. Внутри файла перечислите названия файлов/папок, которые нужно игнорировать, каждый с новой строки.
    3. Файл .gitignore должен быть добавлен, закоммичен и отправлен на сервер, как любой другой файл в проекте.

    Вот хорошие примеры файлов, которые нужно игнорировать:

    • Логи
    • Артефакты систем сборки
    • Папки node_modules в проектах node.js
    • Папки, созданные IDE, например, Netbeans или IntelliJ
    • Разнообразные заметки разработчика.

    Файл .gitignore, исключающий все перечисленное выше, будет выглядеть так:

    Символ слэша в конце некоторых линий означает директорию (и тот факт, что мы рекурсивно игнорируем все ее содержимое). Звездочка, как обычно, означает шаблон.

    Вот и все! Наше руководство окончено. Мы очень старались собрать всю самую важную информацию и изложить ее как можно более сжато и кратко.
    Git довольно сложен, и в нем есть еще много функций и трюков. Если вы хотите с ними познакомиться, вот некоторые ресурсы, которые мы рекомендуем:

    Читайте также: