Начало использования Git напоминает посещение новой страны, языка которой вы не знаете. Пока ясно, где вы и куда идти, все хорошо, но стоит заблудиться — и начинаются большие проблемы.
В интернете размещена масса руководств по командам Git, но в этой статье работа Git рассмотрена глубже, чем просто изучение команд.
Это первая часть гайда по Git из блога Pierre de Wulf в переводе команды Mail.ru Cloud Solutions.
Новым пользователям бывает трудно освоиться с 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». Таким образом, файлы будут разложены по 256 подпапкам и в каждой не будет слишком много файлов.
У нас, как вы видите, три хеша. Один для файла file_1.txt, второй для снимка, сделанного при коммите. Для чего же третий? Третий хеш создается потому, что коммит — тоже объект, он также архивируется и помещается в папку объектов.
Вам нужно запомнить, что коммит состоит из четырех вещей:
- Имя (хеш) снимка рабочей директории.
- Комментарий.
- Информация о том, кто выполнил коммит.
- Хеш родительского коммита.
Посмотрите сами, что произойдет, если распаковать файл коммита:
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
Вы видите, как и ожидалось, хеш снимка, автора и комментарий коммита. Тут важны две вещи:
- Хеш снимка «86550...» также является объектом и его можно увидеть в папке объектов.
- Так как это первый коммит, у него нет родительского коммита.
Что же в снимке в действительности?
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 хранит список файлов, которые вы хотите коммитить? Он сохраняет этот список в индексном файле. Мы не будем сильно углубляться в этот вопрос, если вам интересно, подробнее можно посмотреть здесь.
Переведено при поддержке Mail.ru Cloud Solutions.
- Простые способы кэширования в GitLab CI: руководство в картинках.
- Как технический долг и лже-Agile убивает ваши проекты.
- Мой второй год в качестве независимого разработчика.