Добавлю свои 5 коп. на тему svn vs. git. На svn сидел довольно долго - порядка 12 лет. Перешёл в своё время на svn с cvs. По сравнению с cvs svn - это был, конечно, прорыв! Ушли в небытие геморрои с неатомарными комитами, липкими метками и прочими анахронизмами, процесс фиксации стал предсказуемым и стабильным.
Но на протяжении всего этого времени не покидало чувство, что чего-то не хватает, что нет какой-то свободы, что-ли, что каждый комит - это как штамп, высечка в граните... В итоге года 4 назад начал пошупать git/mercurial. Остановился на git. Не скажу, что сразу всё стало хорошо - потребовалось некоторое время (где-то с полгода), чтобы прочувствовать, понять его концепцию - достаточно сильно мешали стереотипы, выращенные на svn.
На основании личного опыта могу сказать, что
svn по сравнению с
git - это
не система
управления версиями, это скорее система
автоматизированного архивирования. svn позволяет делать фиксации с комментариями, хранит это инкрементально в централизованном хранилище (репозитории). Она хорошо подходит для линейного процесса фиксации изменений - сделали что-то, зафиксировали. Но она очень плохо подходит для работы в стиле "а ну-ка попробую-ка я это или это". Для такого стиля очень нужна возможность легко создавать и сливать ветки. И она в git есть, а в svn нет. В svn все комиты летят в центральный реп и нет никакой возможности ими пилотировать - ни откатить, ни изменить.
Типовой пример. Всякий разработчик сталкивается с ситуацией, когда его проект достиг какого-то промежуточного итога: код стабильно собирается, предсказуемо работает. Можно двигаться дальше - добавлять новые фичи, но очень не хочется сломать то, что уже есть. Самый простой способ - делать архивы таких промежуточных точек, но это есть ни что иное, как попытка вести контроль версий вручную. Архивы плодятся, комментарии к ним теряются, сравнить две зафиксированные точки является довольно обременительной задачей... Собственно, с этого и начались системы управления версиями - та же древняя cvs поначалу представляла собой набор скриптов, который автоматизировал эти операции. svn довела реализацию этой модели до совершенства - фиксировать промежуточные состояния проекта стало удобно, безопасно и эффективно (благодаря дельтам).
К сожалению, модель с центральным репозиторием не очень хорошо подходит именно для версионирования кода, т.е. когда хочется легко и быстро создавать альтернативные ветки развития, не ломая и не теряя уже полученного. И с этой задачей хорошо справляется git. Например, вот достиг я такого вот промежуточного результата, теперь мне надо двигаться дальше - добавить какую-то фичу. Я не пилю код в стабильной ветке - в ней все комиты всегда рабочие (да, в них могут быть баги, как и везде, но они ненамеренные), код всегда собирается и фичи все допиленные, никаких полуразобранных состояний там не бывает - они только в фичебранчах (feature-branch). Фичебранчи, когда требуемый функционал достигнут, сливаются в стабильную ветку и удаляются. Стабильная ветка у нас называется develop.
Так вот, для новой фичи я завожу фичебранч:
Код
git co -b fb-newfeature
(fb- означает feature branch)
и спокойно кромсаю код "по живому", не боясь ничего сломать или потерять. Если вдруг срочно понадобился стабильный вариант, просто откатываюсь на него:
Код
git co develop
Если допилил код в фичебранче, то сливаю его в стабильную (символ # - это типа комментарий, в реальной работе этого нет):
Код
git co develop # переключаемся на требуемую ветку
git merge --no-ff -e fb-newfeature # --no-ff - создать комит слияния, -e - открыть редактор для ввода комментария
После этого имеем красивую историю - чётко видна этапность: все комиты в ветке develop обозначают добавление той или иной фичи (или багфикс), а процесс разработки фичи хорошо виден как "отросток" слитый в стабильную ветку (по комментариям в комитах слияния видно, из какой ветки и какую фичу слили):
Нажмите для просмотра прикрепленного файлаОтчётливо видно, что делалось (правую часть я для экономии обрезал, там даты и авторы комитов, т.ч. видно кто и когда внёс изменения).
Самое главное - это ощущение лёгкости процесса. Абсолютное отсутствие страха что-то сломать или потерять. Например, искромсал код на экспериментах до невосстановимого состояния - не беда:
Код
git co <branch-name>/<commit-hash>
Если эксперимент затягивается и хочется в его процессе тоже фиксировать - то создаём просто ещё ветку прямо на текущей:
Код
git co -b fb-mad-experiment
и продолжаем хулиганить. Если результат оказался неудовлетворительным, просто откатываемся на предыдущую ветку:
Код
git co fb-<branch-name>
не заботясь о мусорном коде - гит всё вернёт в нужное состояние.
Именно на гите я впервые прочувствовал совет делать т.н. "атомарные" комиты - т.е. такие, которые содержат набор изменений, касающихся только чего-то одного. Если в коде в результате правок появилось два и более логически развязанных изменения, то лучше фиксировать их разными комитами. В этом есть большой смысл. Например, возвращаясь к предыдущему примеру с экспериментальной веткой - вот наворотили там кода, поняли, что в общем-то тупик, но кое-что оказалось полезным и это кое-что неплохо бы взять с собой. Поскольку мы делали все комиты атомарными, то это кое-что оказалось зафиксированным отдельно от остальных правок. Теперь нам достаточно, откатившись на базовую ветку, выдернуть нужный комит в эту ветку:
Код
git cherry-pick <commit-hash>
Кстати, этот же подход неоднократно проявлял себя и полезной стороны и при коллективной работе. Реальный случай: мы с товарищем работали над прибором, я отвечал за железку, а он за хостовую часть. Доведя дивайс до рабочей кондиции, я передал его товарищу и продолжил работать над очередной фичей, а товарищ писал протокол управления прибором. В какой-то момент, он обнаружил баг со стороны дивайса - что-то там с таймаутом не срасталось. Потребовалось моё участие, а я уже в разгаре работы над новой фичей. Но ведь ему-то ведь меня ждать, когда я закончу с этой фичей, не с руки, приходится переключаться. Действия такие:
Код
git stash # прячем незафиксированные изменения во временный комит, чтобы не плодить мусорных комитов
git co develop # переключаемся на ветку, в которой обнаружен баг
git co -b bfix-timeout # создаём ветку для работы над багфиксом
Работаем над багом, попутно находится ещё кое-что, что нуждается в правке, итого несколько комитов в этой багфиксной ветке. Закончили, потестировали - работает. Сливаем
Код
git co develop # переключаемся на основную рабочую ветку
git merge --no-ff -e bfix-timeout # сливаем правки бага
После этого товарищ продолжает работать, я возвращаюсь к своей прерванной работе:
Код
git co fb-<new-feature> # переключаемся на ветку, с которой ушли на правку бага
git stash pop # вытаскиваем спрятанные незафиксированные правки
и продолжаю работать как ни в чём не бывало. В этот момент осознаю, что в той багфиксой ветке была одна правка (не связанная напрямую с багом), которая актуальна мне и в этой текущей ветке. Можно, конечно, по горячим следам поправить руками, но зачем, когда есть замечательная возможность сделать это автоматизировано:
Код
git cherry-pick <commit-hash> # <commit-hash> - хэш того комита, где зафиксированы нужные мне изменения
В результате на текущую ветку как бы накатывается набор зафиксированных в другом комите изменений. Это может быть один файл или хоть десяток файлов, а так же тащится и комментарий комита. В общем, ничего тут не теряется и не забывается. В дальнейшем, когда буду сливать эту свою фичебранч в стабильную ветку, в которой вышеупомянутый комит уже есть, умный гит учтёт это и дублирования данных не произойдёт.
Вот всего этого нет в системах с центральным репозиторием. Там даже откатить комит уже проблема. Вести такую вольную работу с кодом тоже не получается. Любая фиксация оставляет неизгладимый след в репозитории. В гите очень просто можно поддерживать красивую историю развития проекта, когда видно, когда, что и кем делалось, видна этапность. Для этого помимо описанного выше есть другие способы влиять на вид - например git rebase (хотя тут надо пользоваться осторожнее). Красивая история - не фетиш, это один из способов поддерживать сопровождаемость проекта, когда сразу видны этапы работы.
Гит по духу напоминает язык программирования С. И в том, и в другом при неправильном использовании можно наворотить безобразий, но при правильном использовании это мощный инструмент.
Есть ещё немало очень "вкусных" возможностей гита - как, например, возможность поправить сделанный по ошибке комит или возможности по формированию данных комита - это когда, к примеру, текущее состояние проекта содержит несколько логически развязанных изменений, и можно их добавлять, отделяя друг от друга, создавая логически законченные целостные "атомарные" комиты. На svn комит нередко содержал в себе целый набор разнообразных правок и некоторые комментарии к комитам напоминали приличный по размеру changelog. Но там и смысла делать атомарные комиты немного, там вообще лёгкое версионирование практически нереально.