Git — начало

В топике освещаются не столько подробности работы с git, сколько его отличия от схемы разработки других систем контроля версий, и общий подход (выработанный по большей части личным опытом и Git Community Book) к работе.

Современная разработка программного обеспечения стала чем-то большим, чем просто набором исходного кода программы в текстовом редакторе; она обросла целым комплексом дополнительного инструментария, вроде багтреккеров, систем для управления проектами и систем контроля версий (СКВ). Последние, пожалуй, играют особенную роль в проекте, поскольку определяют сам ход работ (или workflow).

Централизованные системы контроля версий

Классическим примером подобных программ в мире открытого софта являются CVS и ее потомок — Subversion (лозунг проекта — «CVS the right way»); проприетарные аналоги: Perforce или Clearcase. Эти системы строятся вокруг централизованной модели разработки, в которой существует единственный удаленный репозитарий, в который вносят изменения все разработчики проекта. Ветвление (branching) проекта возможно, но не желательно и приносит, как правило, только дополнительные сложности в проект.

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

Соответствующие команды: svn checkout (забрать последнюю версию), svn resolve (показать, что конфликт в исходном коде разрешен) и svn commit (создать в репозитарии очередную ревизию).

Подобный линейный подход к разработке прост и очевиден, но здорово ограничивает программиста. А что, если на любой из стадий цикла требуется отвлечься на другой функционал? Или срочно исправить какой-либо баг в предыдущей работе?

Существуют разные выходы из ситуации. Можно проявить твердость, и закончить текущую работу, чтобы потом обратиться к следующим этапам; или, как вариант, нагружать текущий коммит большим количеством изменений.

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

Откатиться к начальному состоянию невозможно — значит потерять уже проведенную работу. Ну или, если совсем усложнить, завести отдельную копию проекта, в ней исправить ошибку (внести функционал), закоммитить, затем стереть копию, и вернуться к прежней работе… Сложно? Сложно. Не выход, иными словам.

Кроме того, иногда требуется работать без доступа к центральному репозитарию (удаленная работа, поездка и т.д. и т.п.). Что делать? Лишаться всякой гибкости разработки и заливать монстроидальный коммит весом в неделю?

Распределенный подход

Решением подобных проблем явилась альтернативная схема разработки, предлагаемая так называемыми распределенными системами контроля версий (Distributed Version Control System).

Среди открытых разработок на данную тему можно вспомнить git, Mercurial и Bazaar. Первый проект особенно интересен, он используется в некоторых из сложнейших современных программных систем(Linux Kernel, Qt, Wine, Gnome, Samba и многие другие), крайне быстро работает с любым объемом кода и сейчас набирает популярность в открытом мире. Какое-то время на распространении этой программы негативно сказывался недостаток документации; но сейчас этот недостаток можно считать устраненным.

Итак, в чем заключается глобальное отличие git (и DVCS вообще) от централизованных аналогов?

Во-первых, как следует из самого названия, не существует главного (в том смысле, который его понимают разработчики, привыкшие к SVN) репозитария. У каждого разработчика имеется собственный полноценный репозитарий, с которым и ведется работа; периодически проводится синхронизация работы с (чисто условно!) центральным репозитарием.

Во вторых, операции ветвления и слияния веток (merging) ставятся во главу угла при работе программиста, и поэтому они очень легковесны.

Кстати говоря, привычных ревизий также не существует; но об этом — чуть позже.

workflow в одну морду

Итак, рассмотрим простейший случай: личный проект, в котором участвует единственное одно лицо — вы.

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

git init

Был создан пустой репозитарий — папка .git в корне проекта, в которой и будет собираться вся информация о дальнейшей работе (и никаких некрасивых .svn, разбросанных по дереву проекта!). Предположим, уже существует несколько файлов, и их требуется проиндексировать командой git add:

git add .

Внесем изменения в репозитарий:

git commit -m "Первоначальный коммит"

Готово! Имеется готовый репозитарий с единственной веткой. Допустим, потребовалось разработать какой-то новый функционал. Для этого создадим новую ветку:

git branch new-feature

И переключимся на нее (обратите внимание на отличие в терминологии по сравнению с SVN):

git checkout new-feature

Вносим необходимые изменения, после чего смотрим на них, индексируем и коммитимся:

git status
git add .
git commit -m "new feature added"

Теперь у нас есть две ветки, одна из которых (master) является условно (технически же ничем не отличается) основной. Переключаемся на нее и включаем изменения:

git checkout master
git merge new-feature

Легко и быстро, не находите? Веток может быть неограниченное количество, из них можно создавать патчи, определять diff с любым из совершенных коммитов.

Теперь предположим, что во время работы выясняется: нашелся небольшой баг, требующий срочного внимания. Есть два варианта действий в таком случае. Первый состоит из создания новой ветки, переключения в нее, слияния с основой… Второй — команда git stash. Она сохраняет все изменения по сравнению с последним коммитом во временной ветке и сбрасывает состояние кода до исходного:

git stash

Исправляем баг и накладываем поверх произведенные до того действия (проводим слияние с веткой stash):

git stash apply

Вот и все. Очень удобно. На самом деле таких «заначек» (stash) может быть сколько угодно; они просто нумеруются.

При такой работе появляется необычная гибкость; но среди всех этих веточек теряется понятие ревизии, характерное для линейных моделей разработки. Вместо этого каждый из коммитов (строго говоря, каждый из объектов в репозитарии) однозначно определяется хэшем. Естественно, это несколько неудобно для восприятия, поэтому разумно использовать механизм тэгов для того, чтобы выделять ключевые коммиты: git tag просто именует последний коммит; git tag -a также дает имя коммиту, и добавляет возможность оставить какие-либо комментарии (аннотацию). По этим тегам можно будет в дальнейшем обращаться к истории разработки.

Плюсы такой системы очевидны! Вы получаете возможность колдовать с кодом как душе угодно, а не как диктует система контроля версий: разрабатывать параллельно несколько «фишек» в собственных веточках, исправлять баги, чтобы затем все это дело сливать в единую кашу главной ветки. Замечательно быстро создаются, удаляются или копируются куда угодно папочки .git с репозитарием, не в пример SVN.

Гораздо удобней такую легковесную систему использовать для хранения версий документов, файлов настроек и т.д, и т.п. К примеру, настройки и плагины для Емакса я храню в директории ~/site-lisp, и держу в том же месте репозитарий; и у меня есть две ветки: work и home; иногда бывает удобно похожим образом управлять настройками в /etc. Естественно, что каждый из моих личных проектов тоже находит под управлением git.

Общественные репозитарии

Общественный репозитарий — способ обмениваться кодом в проектах, где участвует больше двух человек. Лично я использую сайт github.com, настолько удобный, что многие начинают из-за него пользоваться git.

Итак, создаем у себя копию удаленного репозитария:

git clone git://github.com/username/project.git master

Команда создала у вас репозитарий, и внесла туда копию ветки master проекта project. Теперь можно начинать работу. Создадим новую ветку, внесем в нее изменения, закоммитимся:

git branch new-feature
edit README
git add .
git commit -m "Added a super feature"

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

git checkout master
git pull
git merge new-feature

Если не было неразрешенных конфликтов, то коммит слияния готов.

Команда git pull использует так называемую удаленную ветку (remote branch), создаваемую при клонировании удаленного репозитария. Из нее она извлекает последние изменения и проводит слияние с активной веткой.

Теперь остается только занести изменения в центральный (условно) репозитарий:

git push

Нельзя не оценить всю гибкость, предоставляемую таким средством. Можно вести несколько веток, отсылать только определенную, жонглировать коммитами как угодно.

В принципе, никто не мешает разработать альтернативную модель разработки. Например, использовать иерархическую систему репозитариев, когда «младшие» разработчики делают коммиты в промежуточные репозитарии, где те проходят проверку у «старших» программистов и только потом попадают в главную ветку центрального репозитария проекта.

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

 

PS: честно скопипасщено c Хабра

Реклама