Для тех, кто незнаком с бесплатными VCS с открытым кодом, следует сказать, что Subversion фактически стала стандартом некоммерческой VCS, заменившим старую и добрую CVS (Concurrent Versions System). Системы CVS все еще отлично подходят для ограниченного использования, но Subversion привлекает тем, что для ее работы требуется практически только небольшая настройка Web-сервера. Subversion имеет некоторые проблемы, которые мы здесь обсудим, но в большинстве случаев она отлично работает.
Так почему же нам нужно еще что-то? Система Git (с заглавной "G"; т.к.
git
- это инструмент командной строки) во многом спроектирована так, чтобы быть лучше Subversion.
Она является одной из многих
распределенных
VCS. Лично мне довелось работать с Arch/tla, Mercurial, Bazaar, darcs и некоторыми другими. По многим причинам, о которых я буду рассказывать, когда они будут становиться важными для нашего рассказа, Git стала популярной и часто считается одним из двух лидеров (наряду с Subversion) среди персональных и корпоративных VCS.
Есть две причины, по которым вы, будучи пользователем Subversion, можете заинтересоваться Git.
- Вы планируете перейти к использованию Git, так как Subversion вас в чем-либо ограничивает.
- Вам интересно узнать о Git и вы хотите сравнить ее с Subversion.
Хотя, возможно, есть и третья причина: Git – это относительно новая технология, которую вы хотите указать в своем резюме. Я надеюсь, это не является вашей главной целью; изучение Git – это одна из самых полезных вещей, которые может сделать разработчик. Даже если вы не используете Git сейчас, концепции и рабочий процесс, реализованные в этой распределенной VCS, наверняка станут важными знаниями для большинства сегментов ИТ-индустрии в следующие 10 лет, поскольку отрасль переживает крупные перемены в своих границах и географической структуре.
Наконец, хотя это может и не быть весомой причиной, если вы не занимаетесь разработкой ядра Linux, - Git используется для сопровождения ядра и множества других важных проектов. Поэтому, если вы планируете внести свой вклад в какой-либо из открытых проектов, вам нужно познакомиться с этой системой.
Эта статья предназначена для пользователей Subversion начального и среднего уровня. Читатель должен быть знаком с Subversion и с общими принципами работы систем управления версиями. Информация в этой статье главным образом предназначена для пользователей UNIX®-подобных систем (таких как Linux® и Mac OS X) и в меньшей степени для пользователей Windows®.
В части 2 этой серии мы обсудим продинутые приемы использования Git: слияние ветвей, генерация файлов различий и другие типичные задачи.
Далее вместо "Subversion" я буду использовать аббревиатуру "SVN", чтобы меньше изнашивать клавиши U, B, E, R, S, I и O на моей клавиатуре.
Итак, чем же хороша SVN? Возможно, вы уже знаете, что главным в VCS являются не файлы, а изменения. SVN, работающая на центральном сервере, добавляет изменения в свой репозиторий данных и может предоставить вам копию этих данных после каждого изменения. Эта копия имеет номер версии; номер версии очень важен для SVN и для людей, работающих с ней. Если ваше изменение следует после моего, ваш номер версии гарантированно будет больше моего.
Git имеет похожую цель – отслеживание изменений, - но не имеет централизованного сервера. Это очень важное отличие. SVN – система централизованная, а Git – распределенная, поэтому в Git не может быть возрастающего порядка версий, так как в ней нет "последней версии". Однако в ней все же имеются уникальные идентификаторы версий, просто сами по себе они не так полезны, как номера версий (ревизий) в SVN.
В Git важнейшим действием является не фиксация изменений (commit), а их слияние (merge). Кто угодно может клонировать репозиторий и вносить изменения в этот клон. Слияние изменений из клона в основной репозиторий делается по усмотрению владельца реозитория. В качестве альтернативы разработчики могут сами публиковать (push) свои изменения в основном репозитория. Мы рассмотрим только последнюю модель авторизованной публикации изменений.
Начнем с простого типичного примера – отслеживание содержимого директории с помощью SVN. Нам понадобится SVN-сервер и, очевидно, директория с файлами, а также учетная запись на этом сервере с правом фиксации изменений как минимум в этой директории. Начнем с добавления и фиксации директории:
Листинг 1. Настраиваем директорию для работы в SVN
% svn co http://svnserver/...какой-либо путь.../top % cd top % cp -r ~/my_directory . % svn add my_directory % svn commit -m 'добавляем директорию' |
Что это нам дает? Теперь мы можем получать из репозитория последние версии любых файлов в этой директории, удалять файлы, переименовывать их, создавать новые файлы и директории, фиксировать изменения в существующих файлах и т.д:
Листинг 2. Простые файловые операции в SVN
# получаем из репозитория последнюю версию файлов % svn up # проверяем статус % svn st # удаляем файлы % svn delete # переименовываем файлы (фактически здесь выполняется delete + add) % svn rename # создаем директорию % svn mkdir # добавляем файл % svn add # фиксируем изменения (всё, что было сделано выше и изменения содержимого файлов) % svn commit |
Мы не будем здесь подробно изучать эти команды, а просто их запомним.
Чтобы ознакомиться с базовой справочной информацией по любой из этих команд, наберите
svn help COMMAND, а для получения подробных сведений обращайтесь к руководству пользователей.
Я буду действовать таким же образом, как и в примере с SVN. Как и прежде, я предполагаю, что у вас есть директория с данными.
В качестве удаленного сервера я использовал бесплатный сервис github.com, но вы, конечно же, можете использовать любой другой сервер. GitHub –это простой способ попробовать поработать с удаленным Git-репозиторием. На момент написания статьи объем данных для бесплатной учетной записи ограничен 300 МБ, кроме того, репозиторий должен быть публичным. Я завел учетную запись с именем “tzz” и создал публичный репозиторий "datatest"; можете его использовать. Я предоставил свой публичный SSH-ключ. Если у вас еще нет своего ключа, следует его сгенерировать. Также можно попробовать серверы Gitorious или repo.or.cz. В Wiki-разделе сайта git.or.cz имеется большой список сайтов, предоставляющих услуги хостинга репозиториев Git (см. ссылку в разделе Resources).
GitHub хорош тем, что он дружелюбен к пользователям. Он сообщает, какие именно команды следует использовать, чтобы настроить Git и подготовить к работе репозиторий. Выполним эти действия вместе.
Сначала нужно установить систему Git, свою для каждой платформы, и выполнить для нее начальную настройку. На странице загрузки Git (см. Resources)
перечислено несколько вариантов для различных платформ. (На Mac OS X я использовал команду
port install git-core, но, чтобы ее использовать, сначала нужно установить MacPorts. На странице загрузки Git также имеется ссылка на отдельный установщик Git для MacOS X Git, который, возможно, лучше подойдет в большинстве случаев.)
После установки выполним базовую настройку системы с помощью следующих команд (конечно же, укажите свое имя пользователя и адрес электронной почты):
Листинг 3. Базовая настройка Git
% git config --global user.name "Ted Zlatanov" % git config --global user.email "tzz@bu.edu" |
Уже сейчас можно заметить отличие от SVN: в SVN имя пользователя назначается на стороне сервера. В Git вы можете задать какое угодно имя, даже "Великолепная мартышка Витгенштейна", если оно вам нравится (я удержался от этого соблазна).
Далее я настрою файлы данных и инициализирую ими свой репозиторий. (GitHub также может импортировать их из репозитория SVN, что может оказаться полезным.)
Листинг 4. Настройка директории и первая фиксация
# возьмем несколько файлов % cp -rp ~/.gdbinit gdbinit % mkdir fortunes % cp -rp ~/.fortunes.db fortunes/data.txt # инициализация % git init # "Initialized empty Git repository in /Users/tzz/datatest/.git/" # add the file and the directory % git add gdbinit fortunes % git commit -m 'инициализация' #[master (root-commit) b238ddc] инициализация # 2 files changed, 2371 insertions(+), 0 deletions(-) # create mode 100644 fortunes/data.txt # create mode 100644 gdbinit |
Git выводит информацию о режимах доступа к файлам;
100644 является восьмеричным представлением битов прав доступа
к этим файлам. На них можно не обращать внимания,
а вот сообщение о 2371 вставке может показаться загадочным. Ведь мы изменили всего 2 файла, не так ли? На самом деле это число обозначает количество вставленных строк. И, конечно же, мы не удалили ни одной строки.
А что насчет публикации (переноса) наших изменений на сервере GitHub? В документации об этом рассказывается на примере сервера с именем “origin” (вы можете использовать любое другое имя). Следует сказать, что для того, чтобы узнать больше о какой-либо команде Git,
например, git remote, нужно набрать
git remote --help или
git help remote. Такое поведение типично для инструментов командной строки, SVN также работает очень похожим образом.
Листинг 5. Публикация изменений в удаленном репозитории
# помните, что наш удаленный репозиторий называется "datatest"? % git remote add origin git@github.com:tzz/datatest.git # публикуем изменения % git push origin master #Warning: Permanently added 'github.com,65.74.177.129' (RSA) to the list of known hosts. #Counting objects: 5, done. #Delta compression using 2 threads. #Compressing objects: 100% (4/4), done. #Writing objects: 100% (5/5), 29.88 KiB, done. #Total 5 (delta 0), reused 0 (delta 0) #To git@github.com:tzz/datatest.git # * [new branch] master -> master |
Здесь OpenSSH выдает предупреждение, связанное с тем, что ранее машина github.com была ему неизвестна. Не обращайте на него внимания.
Сообщения, выдаваемые Git, чрезвычайно подробны. В отличие от легких для понимания сообщений SVN, в Git сообщения написаны посвященными и предназначены для посвященных. Если вы родом из Дюны Герберта Фрэнка и обучены как биокомпьютер, возможно вы поймете их, хотя тогда, возможно, вы уже написали свою собственную версию Git. Ну а остальным сообщения о дельта-сжатии и количестве используемых им потоков просто не очень важны (и могут вызывать головную боль).
Перенос изменений был сделан через SSH, но также можно использовать другие протоколы, такие как HTTP, HTTPS, rsync и file. Более подробную информацию см. в справке:
git push --help.
Мы подошли к самому главному различию между SVN и Git. В SVN операция commit указывает: «надо скопировать это на центральный сервер». В SVN ваши изменения остаются неосязаемыми, пока вы их не зафиксируете (commit). В Git фиксация изменений является локальной, у вас есть локальный репозиторий, которому нет дела до того, что происходит на удаленной стороне. Можно откатить назад изменение, ветвь, зафиксировать изменение в ветви и т.д. без какого-либо взаимодействия с удаленным сервером. Публикация изменений (push) в Git по сути является синхронизацией состояния вашего репозитория с удаленным сервером.
Хорошо, напоследок давайте посмотрим, как то, что мы только что сделали, отображается в журнале Git:
Листинг 6. Журнал Git
% git log #commit b238ddca99ee582e1a184658405e2a825f0815da #Author: Ted Zlatanov <tzz@lifelogs.com> #Date: ...commit date here... # # инициализация |
В журнале есть запись только о фиксации изменений (обратите внимание, что здесь, в отличие от номера ревизии в SVN, фигурирует длинный случайный идентификатор операции фиксации). Нет никакого упоминания о синхронизации, проводимой по команде git push.
Совместная работа с помощью Git
До сих пор мы использовали Git в качестве замены SVN. Конечно, чтобы сделать все интереснее, необходимо иметь нескольких пользователей и несколько наборов изменений. Давайте работать с нашим репозиторием с еще одной машины (я буду использовать машину с Ubuntu GNU/Linux; на ней
пакет, который надо установить, называется не
git, а
git-core):
Листинг 7. Настраиваем еще один экземпляр Git и загружаем в нем содержимое репозитория
% git config --global user.name "The Other Ted" % git config --global user.email "tzz@bu.edu" % git clone git@github.com:tzz/datatest.git #Initialized empty Git repository in /home/tzz/datatest/.git/ #Warning: Permanently added 'github.com,65.74.177.129' (RSA) to the list of known hosts. #remote: Counting objects: 5, done. #remote: Compressing objects: 100% (4/4), done. #Indexing 5 objects... #remote: Total 5 (delta 0), reused 0 (delta 0) # 100% (5/5) done % ls datatest #fortunes gdbinit % ls -a datatest/.git # . .. branches config description HEAD hooks index info logs objects refs % ls -a datatest/.git/hooks # . .. applypatch-msg commit-msg post-commit post-receive post-update # pre-applypatch pre-commit pre-rebase update |
OpenSSH опять выдает предупреждение о том, что раньше эта машина не работала через SSH с GitHub.
Команда git clone похожа на команду SVN checkout, но здесь вместо получения определенной версии содержимого мы получаем весь репозиторий целиком.
Я вывел здесь содержимое директории datatest/.git и ее поддиректории hooks, чтобы показать, что мы получили действительно все. По умолчанию Git не таит никаких секретов, в отличие от SVN, которая по умолчанию хранит репозиторий в тайне и разрешает доступ только к копиям содержимого.
Кстати, если вы хотите настроить для своего Git-репозитория какие-либо правила, которые бы выполнялись, например, при каждой фиксации изменений, это можно сделать с помощью сценариев в директории hooks. Это обычные сценарии оболочки, во многом похожие на “ловушки” в SVN, которые согласно стандартам UNIX возвращают ноль в случае успеха и что-либо другое в случае ошибки. Я не буду здесь вдаваться в детали сценариев-«ловушек», но если вы собираетесь использовать Git в командной работе, вам определенно следует их подробно изучить.
Итак, пользователь "The Other Ted" хочет добавить новый файл в главную ветвь (которую можно грубо соотнести с ветвью TRUNK в SVN), а также создать новую ветвь с некоторыми изменениями в файле gdbinit.
Листинг 8. Добавляем файл и создаем новую ветвь
# копируем файл, который будет добавлен... % cp ~/bin/encode.pl . % git add encode.pl % git commit -m 'добавляем encode.pl' #Created commit 6750342: добавляем encode.pl # 1 files changed, 1 insertions(+), 0 deletions(-) # create mode 100644 encode.pl % git log #commit 675034202629e5497ed10b319a9ba42fc72b33e9 #Author: The Other Ted <tzz@bu.edu> #Date: ...commit date here... # # добавляем encode.pl # #commit b238ddca99ee582e1a184658405e2a825f0815da #Author: Ted Zlatanov <tzz@lifelogs.com> #Date: ...commit date here... # # инициализация % git branch empty-gdbinit % git branch # empty-gdbinit #* master % git checkout empty-gdbinit #Switched to branch "empty-gdbinit" % git branch #* empty-gdbinit # master % git add gdbinit % git commit -m 'empty gdbinit' #Created commit 5512d0a: empty gdbinit # 1 files changed, 0 insertions(+), 1005 deletions(-) % git push #updating 'refs/heads/master' # from b238ddca99ee582e1a184658405e2a825f0815da # to 675034202629e5497ed10b319a9ba42fc72b33e9 #Generating pack... #Done counting 4 objects. #Result has 3 objects. #Deltifying 3 objects... # 100% (3/3) done #Writing 3 objects... # 100% (3/3) done #Total 3 (delta 0), reused 0 (delta 0) |
Это был длинный пример, и я надеюсь, что вы не заснули, читая его. Если же вы заснули, я надеюсь, вам снились репозитории Git, синхронизирующиеся в бесконечном вальсе наборов изменений (о, не беспокойтесь, вам будет это сниться).
Сначала я добавил и зафиксировал файл (encode.pl, всего из одной строки). После фиксации удаленный репозиторий ничего не знал о том, что я сделал изменения. Затем я создал новую ветвь
empty-gdbinit и переключился в нее (также это можно было сделать командой git checkout -b empty-gdbinit).
В этой ветви я добавил пустой файл gdbinit и зафиксировал это изменение. И, наконец, я перенес изменения на удаленный сервер.
Если переключиться в главную ветвь, мы не увидим в журнале записи об «empty gdbinit». Т.е. каждая ветвь имеет собственный журнал, что представляется логичным.
Листинг 9. Просматриваем журналы различных ветвей
# мы все ещё находимся в ветви empty-gdbinit % git log #commit 5512d0a4327416c499dcb5f72c3f4f6a257d209f #Author: The Other Ted <tzz@bu.edu> #Date: ...commit date here... # # empty gdbinit # #commit 675034202629e5497ed10b319a9ba42fc72b33e9 #Author: The Other Ted <tzz@bu.edu> #Date: ...commit date here... # # добавляем encode.pl # #commit b238ddca99ee582e1a184658405e2a825f0815da #Author: Ted Zlatanov <tzz@lifelogs.com> #Date: ...commit date here... # # инициализация % git checkout master #Switched to branch "master" % git log #commit 675034202629e5497ed10b319a9ba42fc72b33e9 #Author: The Other Ted <tzz@bu.edu> #Date: ...commit date here... # # добавляем encode.pl # #commit b238ddca99ee582e1a184658405e2a825f0815da #Author: Ted Zlatanov <tzz@lifelogs.com> #Date: ...commit date here... # # инициализация |
Когда мы выполнили команду push, Git сообщил серверам GitHub: «Эй, посмотрите, добавился новый файл с именем encode.pl»
Web-интерфейс GitHub теперь будет отображать файл encode.pl.
Однако в GitHub все еще имеется только одна ветвь. Почему ветвь empty-gdbinit
не была синхронизована? По умолчанию Git не предполагает, что вы хотите публиковать ветви и их изменения. Чтобы сделать это, нужно опубликовать все:
Листинг 10. Публикуем все изменения
% git push -a #updating 'refs/heads/empty-gdbinit' # from 0000000000000000000000000000000000000000 # to 5512d0a4327416c499dcb5f72c3f4f6a257d209f #updating 'refs/remotes/origin/HEAD' # from 0000000000000000000000000000000000000000 # to b238ddca99ee582e1a184658405e2a825f0815da #updating 'refs/remotes/origin/master' # from 0000000000000000000000000000000000000000 # to b238ddca99ee582e1a184658405e2a825f0815da #Generating pack... #Done counting 5 objects. #Result has 3 objects. #Deltifying 3 objects... # 100% (3/3) done #Writing 3 objects... # 100% (3/3) done #Total 3 (delta 1), reused 0 (delta 0) |
Интерфейс для мыслителей снова предстает во всей красе. Но мы сможем разобраться в том, что происходит, не так ли? Возможно, мы не великие мыслители, но наш здравый смысл подскажет нам, что
0000000000000000000000000000000000000000 – это некая специальная начальная метка.
Также из журнала, показанного в листинге 9, мы можем заметить, что метка
5512d0a4327416c499dcb5f72c3f4f6a257d209f является последней (и единственной) фиксацией в ветви
empty-gdbinit.
Все остальное для большинства пользователей могло быть написано и на арамейском, так как им до этого нет никакого дела. Теперь в GitHub отображается новая ветвь и ее изменения.
Также имеются команды git mv и
git rm, с помощью которых можно соответственно перемещать и переименовывать файлы.
В этой статье я рассказал о базовых концепциях Git и организовал с ее помощью управление версиями в простой директории, сравнивая ее по ходу рассказа с Subversion. На простом примере я продемонстрировал работу с ветвями.
В части 2 я расскажу о выполнении слияний, генерации файлов различий и некоторых других командах Git. Я настоятельно рекомендую вам прочитать легко доступное для понимания справочное руководство по Git или хотя бы просмотреть учебное пособие. И то, и другое находится на домашней странице Git, поэтому советую потратить некоторое время на их изучение (см. ссылку в разделе Ресурсы). Имея опыт работы с SVN, сделать это будет не очень сложно.
С другой стороны, Git является очень богатой DVCS; узнав больше об ее возможностях, вы почти наверняка станете их использовать для упрощения и улучшения своего стиля работы с VCS. Также, возможно, вы увидите пару снов о Git-репозиториях.
Научиться
- Ознакомьтесь с оригиналом статьи:
Git for Subversion users, Part 1: Getting started
(EN, developerWorks, август 2009 г.).
- Сайт
Git - ускоренный курс для пользователей SVN
является удобным источником информации для тех, кто уже знаком с SVN.
На сайте Флавио Кастелли есть другое хорошее обучающее руководство -
как совместно использовать Git и SVN (EN).
- Попробуйте воспользоваться услугами хостинга Git,
предоставляемыми серверами
GitHub,
Gitorious,
repo.or.cz, а также
перечисленными здесь сайтами (EN).
- В Википедии имеется достаточно полная
статья о Subversion,
а также не менее полная
статья о Git (EN).
- Почитайте Роби Рассела,
который сумел пережить
миграцию с Subversion на Git
и рассказывает об этом в своем блоге (EN).
- Следите за новостями в разделе
технических
мероприятий и Web-трансляций (EN) developerWorks.
Получить продукты и технологии
- Загрузите
Git, а также
ознакомьтесь с множеством инструментов и документов, представленных на
странице загрузки Git (EN).
- Разработайте ваш следующий Linux-проект с помощью пробного ПО от IBM, которое можно загрузить непосредственно с сайта
developerWorks.
Обсудить
- Участвуйте в жизни
сообщества developerWorks (EN) - создав свой личный профиль и домашнюю страницу, вы можете приспособить
developerWorks к своим интересам и взаимодействовать с другими пользователями developerWorks.
Теодор Златанов (Teodor Zlatanov) получил диплом магистра по вычислительной технике в Boston University в 1999. Он работает программистом с 1992, используя Perl, Java, C, и C++. Он интересуется работами с открытым исходным кодом по синтаксическому анализу текста, трехуровневыми архитектурами клиент-серверных баз данных, системным администрированием UNIX, CORBA и управлением проектами.