1 Вступление
В топике освещаются не столько подробности работы с git, сколько его отличия от схемы разработки других систем контроля версий, и общий подход (выработанный по большей части личным опытом и Git Community Book) к работе.
Современная разработка программного обеспечения стала чем-то большим, чем просто набором исходного кода программы в текстовом редакторе; она обросла целым комплексом дополнительного инструментария, вроде багтреккеров, систем для управления проектами и систем контроля версий (СКВ). Последние, пожалуй, играют особенную роль в проекте, поскольку определяют сам ход работ (или workflow).
2 Централизованные системы контроля версий
Классическим примером подобных программ в мире открытого софта являются CVS и ее потомок — Subversion (лозунг проекта — «CVS the right way»); проприетарные аналоги: Perforce или Clearcase. Эти системы строятся вокруг централизованной модели разработки, в которой существует единственный удаленный репозитарий, в который вносят изменения все разработчики проекта. Ветвление (branching) проекта возможно, но не желательно и приносит, как правило, только дополнительные сложности в проект.
Стандартный ход разработки выглядит примерно следующим образом: выкачивание из репозитария последней версии; разработка новой функциональности или исправление ошибок; повторное обращение к репозитарию для разрешения возможных конфликтов с работой других разработчиков; закачивание очередной ревизии на сервер.
Соответствующие команды: svn checkout (забрать последнюю версию), svn resolve (показать, что конфликт в исходном коде разрешен) и svn commit (создать в репозитарии очередную ревизию).
Подобный линейный подход к разработке прост и очевиден, но здорово ограничивает программиста. А что, если на любой из стадий цикла требуется отвлечься на другой функционал? Или срочно исправить какой-либо баг в предыдущей работе?
Существуют разные выходы из ситуации. Можно проявить твердость, и закончить текущую работу, чтобы потом обратиться к следующим этапам; или, как вариант, нагружать текущий коммит большим количеством изменений.
В первом случае не обеспечивается достаточная гибкость; во втором — усложняется поиск ошибки в коммите, нарушается логическая цельность действия.
Откатиться к начальному состоянию невозможно — значит потерять уже проведенную работу. Ну или, если совсем усложнить, завести отдельную копию проекта, в ней исправить ошибку (внести функционал), закоммитить, затем стереть копию, и вернуться к прежней работе… Сложно? Сложно. Не выход, иными словам.
Кроме того, иногда требуется работать без доступа к центральному репозитарию (удаленная работа, поездка и т.д. и т.п.). Что делать? Лишаться всякой гибкости разработки и заливать монстроидальный коммит весом в неделю?
3 Распределенный подход
Решением подобных проблем явилась альтернативная схема разработки, предлагаемая так называемыми распределенными системами контроля версий (Distributed Version Control System).
Среди открытых разработок на данную тему можно вспомнить git, Mercurial и Bazaar. Первый проект особенно интересен, он используется в некоторых из сложнейших современных программных систем(Linux Kernel, Qt, Wine, Gnome, Samba и многие другие), крайне быстро работает с любым объемом кода и сейчас набирает популярность в открытом мире. Какое-то время на распространении этой программы негативно сказывался недостаток документации; но сейчас этот недостаток можно считать устраненным.
Итак, в чем заключается глобальное отличие git (и DVCS вообще) от централизованных аналогов?
Во-первых, как следует из самого названия, не существует главного (в том смысле, который его понимают разработчики, привыкшие к SVN) репозитария. У каждого разработчика имеется собственный полноценный репозитарий, с которым и ведется работа; периодически проводится синхронизация работы с (чисто условно!) центральным репозитарием.
Во вторых, операции ветвления и слияния веток (merging) ставятся во главу угла при работе программиста, и поэтому они очень легковесны.
Кстати говоря, привычных ревизий также не существует; но об этом — чуть позже.
4 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.
5 Общественные репозитарии
Общественный репозитарий — способ обмениваться кодом в проектах, где участвует больше двух человек. Лично я использую сайт 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Нельзя не оценить всю гибкость, предоставляемую таким средством. Можно вести несколько веток, отсылать только определенную, жонглировать коммитами как угодно.
В принципе, никто не мешает разработать альтернативную модель разработки. Например, использовать иерархическую систему репозитариев, когда «младшие» разработчики делают коммиты в промежуточные репозитарии, где те проходят проверку у «старших» программистов и только потом попадают в главную ветку центрального репозитария проекта.
При работе в парах возможно использовать симметричную схему работы. Каждый разработчик ведет по два репозитария: рабочий и общественный. Первый используется в работе непосредственно, второй же, доступный извне, только для обмена уже законченным кодом.
6 Заключение
Я старался не акцентировать внимание на всех тонкостях использования главных команд, тем более что почти наверняка не знаю их все, да активно использовать git начал сравнительно недавно; но хотел продемонстрировать именно простоту и гибкость этого замечательного средства разработки. Также не стремился объяснить особенности внутренней механики проекта; хотя бы даже потому, что изящность и красота реализации заслуживает как минимум еще одного хабратопика.
Естественно, приветствуются указания на фактические ошибки, подсказки насчет использования системы и общие замечания. :)
UPD: разжился кармой и перенес в блог Git
UPD2: продолжение заметки, несколько неформатное для Хабра :)
