Найти тему
VK Cloud

Руководство по Git. Часть №1: все, что нужно знать про каталог .git

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

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

Наверное, единственный путь преодолеть все эти трудности — узнать чуть больше, чем просто git commit/push, понять, как именно работает Git.

Папка .git

Когда вы создаете новый репозиторий командой git init, Git создает волшебную папку, .git. В ней содержится все необходимое для работы Git. Если вы хотите убрать Git из вашего проекта, но оставить проектные файлы на диске, просто удалите папку .git. Хотя кому такое может потребоваться?

├── HEAD
├── branches
├── config
├── description
├── hooks
│ ├── pre-commit.sample
│ ├── pre-push.sample
│ └── ...
├── info
│ └── exclude
├── objects
│ ├── info
│ └── pack
└── refs
├── heads
└── tags

Вот содержимое типовой папки .git перед вашим первым коммитом:

  • HEAD — это мы рассмотрим позже.
  • config — этот файл содержит настройки для вашего репозитория, здесь, например, хранится url вашего репозитория в хранилище, ваше имя, email и так далее. Каждый раз когда вы делаете git config, вы обращаетесь к этому файлу.
  • description — используется gitweb для отображения описания репозитория.
  • hooks — эта папка содержит скрипты, которые могут выполняться на различных этапах выполнения Git. Эти скрипты, называемые хуками, могут запускаться до/после commit/rebase/pull… Имя скрипта определяет время его выполнения. Примером хука может служить скрипт проверки стиля перед выполнением команды push в репозиторий.
  • info — exclude — здесь описываются файлы, которые вы не хотите включать в репозиторий. Функционал этого файла такой же, как у файла .gitignore, за исключением того, что он не передается в репозиторий. На практике обычно для всех задач хватает .gitignore.

Что внутри коммита?

Каждый раз, когда вы создаете файл и коммитите изменения, Git архивирует файл и сохраняет его в своей структуре данных. Архивированный объект создается с уникальным именем и хранится в папке объектов.

Перед изучением папки объектов уточним, что же такое коммит. Коммит — это слепок текущего состояния файлов в рабочей папке, но не только это.

По факту, когда вы коммитите изменения, Git производит всего два действия:

  • Если файл в рабочей папке не изменялся, он просто добавляет имя сжатого файла (хеш) в снимок.
  • Если файл в рабочей папке изменялся, он сжимает его, помещает в папку объектов и добавляет имя сжатого файла (хеш) в снимок.

Конечно, тут все описано несколько упрощенно, однако, этого достаточно для понимания происходящих процессов.

Как только снимок сделан, он также архивируется и именуется при помощи хеша, затем помещается в папку объектов.

├── 4c
│ └── f44f1e3fe4fb7f8aa42138c324f63f5ac85828 // hash
├── 86
│ └── 550c31847e518e1927f95991c949fc14efc711 // hash
├── e6
│ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 // hash
├── info // let's ignore that
└── pack // let's ignore that too

Вот как выглядит папка объектов после того, как я создал файл file_1.txt и закоммитил его. Пожалуйста, учтите, что если хеш вашего файла начинается на «4cf44f1e…», то Git сохранит его с именем «f44f1e…» в подпапке с именем «4c». Этот маленький трюк в 255 раз уменьшает количество файлов в папке /objects.

У нас, как вы видите, три хеша. Один для файла file_1.txt, второй для снимка, сделанного при коммите. Для чего же третий? Третий хеш создается потому, что коммит — тоже объект, он также архивируется и помещается в папку объектов.

Вам нужно запомнить, что коммит состоит из четырех вещей:

  1. Имя (хеш) снимка рабочей директории.
  2. Комментарий.
  3. Информация о том, кто выполнил коммит.
  4. Хеш родительского коммита.

Посмотрите сами, что произойдет, если распаковать файл коммита:

git cat-file -p 4cf44f1e3fe4fb7f8aa42138c324f63f5ac85828

И вот, что вы увидите:

tree 86550c31847e518e1927f95991c949fc14efc711
author Pierre De Wulf
<test[@gmail.com](mailto:pierredewulf31@gmail.com)> 1455775173
-0500
committer Pierre De Wulf
<[test@gmail.com](mailto:pierredewulf31@gmail.com)> 1455775173
-0500

commit A

Вы видите, как и ожидалось, хеш снимка, автора и комментарий коммита. Тут важны две вещи:

  1. Хеш снимка «86550…» также является объектом и его можно увидеть в папке объектов.
  2. Так как это первый коммит, у него нет родительского коммита.

Что же в снимке в действительности?

git cat-file -p 86550c31847e518e1927f95991c949fc14efc711

100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file_1.txt

Здесь мы видим объект, который находился в нашем хранилище объектов, единственный объект в нашем снимке.

Branch, tags, HEAD — это одно и то же

Теперь вы понимаете, что все в Git может быть получено через правильный хеш.

Давайте теперь посмотрим на HEAD. Итак, что же там?

cat HEAD
ref: refs/heads/master

Там нет хеша, и в этом есть смысл, так как HEAD — это указатель на верхушку ветви, с которой вы работаете.

Если вы посмотрите в файл refs/heads/master, то увидите:

cat refs/heads/master
4cf44f1e3fe4fb7f8aa42138c324f63f5ac85828

Выглядит знакомо? Естественно, ведь это хеш первого коммита! Это показывает, что теги (tags) и ветви (branch) — всего лишь указатели на коммит. Понимая это, вы можете удалить все теги, какие захотите, все ветви, какие захотите, а коммит, на который они указывали, останется на месте. Единственное, к нему будет сложнее получить доступ. Если вам хочется больше узнать об этом, почитайте git book.

Последнее замечание

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

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

Автор: Денис Романенко

Источник: https://mcs.mail.ru/blog/rukovodstvo-po-git-chast-odin-pro-katalog-git

Что еще почитать:
Простые способы кэширования в GitLab CI: руководство в картинках
Как технический долг и лже-Agile убивает ваши проекты
Мой второй год в качестве независимого разработчика