Введение в GIT

Русский перевод книги Pro Git. Самый полезный и лучший источник информации.

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

Установка

! Рассматривается только версия для Windows. !

Для Windows существует следующий дистрибутив. Он содержит утилиту командной строки (Git Bash) и простенький графический интерфейс (Git GUI). Ни один из известных графических интерфейсов (в т.ч. Git GUI) не предоставляет полной свободы действий, потому от его использования стоит отказаться. При установке, однако, следует выбрать пункт меню, добавляющий в контекстное меню проводника команды git (Git Bash Here, Git Init Here и т.д.).

Система git очень дружелюбна к пользователю. В случае возникновения ошибок, в случае опечаток и т.д. она предложит варианты устранения проблемы. Обычно достаточно проследовать этим инструкциям для разрешения проблемы.

В частности, при попытке выполнения первого коммита система предложит настроить учетную запись, состоящее из логина и почтового адреса:

$ git config --global user.name "John Doe"
$ git config --global user.email "[email protected]"

Основы работы

Современные IDE, такие как Visual Studio, NetBeans, Eclipse, уже имеют поддержку git на примитивном уровне. Для повседневной качественной работы, однако, часто требуется (в некоторых случаях необходимо) использовать средства Git Bash.

Рассмотрим по порядку базовые команды git. Будем полагать, что работа ведется в Git Bash.

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

Репозиторий -- единица персистентности системы Git. В репозитории обычно находится один проект или несколько связанных проектов.

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

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

git clone "https://github.com/kalaider/act-photo-random/"

Результат команды -- папка act-photo-random (и ее содержимое) в текущей рабочей директории.

При необходимости поместить содержимое репозитория в другую директорию, она указывается следующим параметром:

git clone "https://github.com/kalaider/act-photo-random/" "whatever/you/type"

Результат выполнения команды -- папка, путь до которой -- whatever/you/type.

Во всех дальнейших примерах будем исходить из того, что текущий рабочий каталог -- как раз папка с репозиторием (act-photo-random).

Выкачивание изменений из удаленного репозитория

git pull

Данная команда автоматически выкачивает изменения из удаленного репозитория и сливает их с локальными. При наличии конфликтов принимаются меры по их устранению.

Создание локальных изменений

После изменения файла необходимо зафиксировать сделанные в нем изменения. Производится это в два этапа.

На первом файлы добавляются в индекс командой

git add "my/file1.cpp"
git add "my/file2.cpp"

Также в индекс могут быть добавлены сразу все измененные файлы или файлы, удовлетворяющие указанной маске:

git add '*.cpp'

Проверить репозиторий на локальные изменения можно командой git status. Она выведет на экран все файлы, содержащие какие-либо изменения относительно текущего коммита.

На втором все добавленные изменения фиксируются. Производится создание нового коммита.

git commit -m "Commit message - a text describing the new commit"

После выполнения команды будет создан новый коммит, а в истории изменений репозитория появится новая запись. Просмотреть историю коммитов можно следующим образом:

git log

Пример вывода команды:

commit 12e46ef16e9fd7500d28ce1580ea5578779d75a7
Author: kalaider <[email protected]>
Date:   Wed Mar 8 17:37:09 2017 +0300

    Readme added

commit 20b976e66a2e2e724760c68a94338e31b6939d24
Author: kalaider <[email protected]>
Date:   Wed Mar 8 16:32:51 2017 +0300

    ATmega8A component added

commit f284766b0eda5473238139809f2dda8f030cec8a
Author: kalaider <[email protected]>
Date:   Wed Mar 8 15:38:08 2017 +0300

    Initial

Коммиты идентифицируются своими хэшами (большими шестнадцатеричными числами).

Внесение изменений в удаленном репозитории

Локальные изменения заливаются в удаленный репозиторий командой

git push

Все конфликты при этом разрешаются руками.

Откат к предшествующим состояниям

Для отката изменений (добавленных или не добавленных в индекс, перемотка к более ранним коммитам) осуществляется командой

git reset <начало хэша коммита, имя тега, название ветки т.д.>

После этого все локальные изменения (по сравнению с целевым состоянием отката) будут помечены как не проиндексированные. Для физического отката всех таких не проиндексированных изменений к команде добавляется опция --hard:

git reset --hard <начало хэша коммита, имя тега, название ветки т.д.>

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

Пример отката к коммиту Initial:

git reset --hard f28476

Работа с ветками

Ветка (branch) -- альтернатива развития проекта. Она может быть создана для исправления ошибки в определенной версии продукта, для внесения экспериментальных изменений в код без "порчи" основной ветки разработки и т.д.

Создание ветки:

git checkout -b my-new-branch

Команда создает ветвь (на это указывает опция -b) и устанавливает текущую ветвь (checkout) в my-new-branch.

Для переключения между ветками используется команда checkout:

git checkout master

master -- главная ветка разработки любого git-репозитория.

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

После успешного завершения разработки фичи (устранения бага) изменения должны быть слиты с основной веткой разработки (или просто другой веткой). Существует несколько типов слияния. Рассмотрим только merge.

Команда merge создает новый коммит, который получается слиянием текущего состояния текущей ветки с состоянием сливаемой ветки.

git checkout master
git merge my-new-branch

Данная команда переключается на ветку master и сливает в нее ветку my-new-branch. После успешного слияния сливаемая ветка может быть удалена:

git branch -d my-new-branch

При конфликте изменений они разводятся руками.

Еще раз о git push и git pull

Команда push сливает текущую ветку с удаленной веткой (указанной или установленной по умолчанию). Так что результат команды push зависит от текущей ветки. Мало чем отличается и поведение pull.

Подмодули

git имеет очень хороший инструмент работы с зависимостями, лежащими также в git-репозиториях. Он называется submodules.

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

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

...подмодули на самом деле попытка «легитимизации» такого подхода.

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

Добавление репозитория как подмодуля

Для добавления в репозиторий A репозитория B в качестве подмодуля в директорию /lib/module-B необходимо выполнить единственную команду:

git submodule add <URL-or-path-to-module-B> /lib/module-B

Например, добавим исходный код самого git'а в качестве подмодуля к нашему репозиторию:

git submodule add https://github.com/git/git /lib/git

Эта команда создаст файл .gitmodules в корне текущего репозитория со следующим содержанием:

[submodule "lib/git"]
    path = lib/git
    url = https://github.com/git/git

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

git submodule init
git submodule update --remote

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

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

git add .gitmodules lib/git
git commit -m "New submodule added and its state fetched"

Выкачивание изменений подмодуля

Для выкачивания изменений следует выполнить уже известный git submodule update --remote и зафиксировать изменения в lib/git командами:

git submodule update --remote
git add lib/git
git commit -m "git submodule state updated to latest"

Клонирование репозиториев с подмодулями

В случае клонирования репозитория с подмодулями подмодули автоматически склонированы не будут. Не будут они даже инициализированы. Поэтому следует выполнять не одно, а несколько действий, что весьма утомительно:

git clone "https://github.com/someuser/repo-with-submodules.git"
git submodule init
git submodule update

Обратите внимание на то, что флаг --remote у git submodule update отсутствует. Это необходимо для того, чтобы получить именно то состояние подмодуля, которое соответствует текущему состоянию данного репозитория, а не последнее состояние удаленного репозитория, на который ссылается подмодуль.

Однако можно поступать гораздо проще, используя специальный флаг --recurse-submodules команды clone:

git clone --recurse-submodules "https://github.com/someuser/repo-with-submodules.git"

Это полный эквивалент предыдущего трехкомандного варианта.

Внесение изменений в подмодули

Команда git submodule update --remote получает репозиторий подмодуля в т.н. состоянии detached head (отделенная голова), которое фактически означает, что никакая ветка в данный момент не выбрана, а указатель HEAD указывает не конец какой-то ветки, а на конкретный коммит. Для внесения изменений это недопустимо, поскольку изменения могут вноситься только в ветки.

Для исправления ситуации требуется вручную переключиться на нужную ветку и получить ее состояние:

git submodule update --remote
git checkout master
git pull

Или

git submodule update --remote
git checkout master
git fetch
git merge

Но для этой простой задачи давно уже существует более короткий синоним:

git submodule update --remote --merge master

Он отличается от обычного git submodule update --remote дополнительным флагом --merge, которому в качестве аргумента можно передать ветку.

Git LFS

Git Large File Storage -- CDN, предоставляющая свои услуги по хранению больших файлов вне репозитория git, но при этом связанных с текущим состоянием репозитория, легко получаемых вместе с любым выкачиванием состояния репозитория и автоматически размещаемых в Git LFS CDN при любой выгрузке состояния репозитория.

Git LFS -- отдельная сторонняя утилита, требуюшая отдельной установки и настройки.

Репозитории, использующие Git LFS, уже настроены правильно, так что все, что требуется от пользователя, -- установить Git LFS и склонировать репозиторий обычным образом.

Алгоритм работы утилиты следующий:

  1. При коммите изменений программа заменяет все реальные файлы, управляемые Git LFS, текстовыми файлами, содержащими хэши файлов и некоторые дополнительные параметры. Далее она загружает реальные файлы в свою CDN, где они доступны как раз по своим хэшам.
  2. При загрузке изменений утилита считывает эти текстовые файлы, скачивает реальные файлы из своей CDN, заменяет ими текстовые файлы в рабочем каталоге.

Итого, сами файлы хранятся вне репозитория, а в нем -- только текстовые фейковые файлы с теми же именами. Отсюда дополнительная "проблема" -- команда git archive, пакующая репозиторий в .zip-архив более не работает корректно: вместо реальных файлов, управляемых Git LFS, в архиве будут присутствовать только их хэши.

Настройка Git LFS

Инициализация работы с Git LFS актуальна только для репозиториев, в данный момент не оскверненных данной утилитой. Для инициализации достаточно выполнить

git lfs init

Это добавит хуки в репозиторий, позволяющие Git LFS перехватывать совершение коммита, клонирование и т.д.

Добавление файлов под управление Git LFS осуществляется правкой файла .gitattributes. Например, для добавления целой директории /doc под управление утилиты в .gitattributes следует прописать строчку:

doc/* filter=lfs diff=lfs merge=lfs -text

На этом конфигурирование заканчивается. Производится фиксация изменений обычным коммитом файла .gitattributes:

git add .gitattributes
git commit -m "doc directory is now managed by Git LFS utility"
git push

Правила хорошего тона при работе с репозиториями

  1. Не использовать ветку master в качестве единственной ветки разработки. Ветка master -- почти что ранний релиз продукта. В ней должен располагаться максимально стабильный код.
  2. Использовать Pull-реквесты при необходимости внесения изменений в ветку master из другой ветки. После одобрения pull-request'а изменения сливаются администратором проекта в общую ветку.
  3. Не коммитить в чужие ветки без разрешения автора ветки.
  4. Не плодить тонны веток в удаленном репозитории (в локальном, разумеется, никто не запрещает).
  5. Удалять ветки только когда они действительно больше не нужны, т.е. слиты в master или другую ветку.
  6. Оставлять внятные сообщения коммитов (желательно на едином для проекта и участников проекта (предпочтительно английском) языке для однородности и понимания друг друга; более того, это устраняет необходимость постоянно переключать раскладку клавиатуры для ввода команд и ввода текста)

results matching ""

    No results matching ""