Sunday, July 28, 2013

Миграция из TFS в SVN

Зачем?

Те, кто хочет побыстрее приступить к делу, могут перейти сразу к пункту «Процесс миграции».
Недавно пришлось заниматься такой странной вещью, как миграция репозитория из TFS в SVN. На то были веские причины: начиная от Микрософстовского лицензирования®, которое ограничивало нас в возможности настроить CI на проекте, и заканчивая неприятными особенностями, которые просто жутко бесят в повседневной работе. Например, тем, что TFS самостоятельно навешивает на каждый файл флаг Read Only, что усложняет процесс сохранения в некоторых редакторах. Или тем, что клиент требует постоянного подключения к серверу. Вероятно, переход на Git обрадовал бы меня еще больше, но корпоративные особенности ограничивали выбор. При этом стояла задача сохранить всю историю изменений старого репозитория.

Инструменты

В Интернете достаточно много статей о том, как мигрировать из TFS в SVN. В некоторых предлагаются рабочие способы, в других – достаточно странные. Чаще всего можно найти упоминание о tfs2svn. Его я и попробовал первым. К сожалению, оказалось, что эта утилита довольно древняя, построена на базе старого клиента, и с новым SVN работать отказывается. К тому же, судя по отзывам, она не вполне стабильна, любит вылететь с ошибкой на середине миграции, поэтому пришлось поискать другой способ.
Описание другого способа я нашел в одном блоге, и заключался он в миграции через Git. Этот способ оказался достаточно простым и сработал как часы, но в процессе я наткнулся на несколько подводных камней, которые заставили меня, как не особенно опытного пользователя Git, немного попотеть. Поэтому я и решил подробно описать весь процесс и те трудности, с которыми пришлось столкнуться.

Процесс миграции

Идея миграции состоит в том, чтобы сначала клонировать репозиторий из TFS со всей историей в локальный Git, потом создать в нем репозиторий SVN, сделать rebase ветки master на trunk и результат залить в SVN. История при этом сохраняется полностью.
Есть, правда, у этого способа одна особенность: все изменения в SVN будут зафиксированы от имени единственного пользователя, который непосредственно будет производить коммит в SVN (т.е. от вашего имени). Я в этом не вижу никакой проблемы, хотя некоторые считают, что для кого-то это может стать препятствием.
Итак, сам процесс по шагам выглядит примерно так.
  1. Установка Git-TF В качестве моста с SVN подойдет стандартный git-svn. Для работы с TFS понадобится установить стороннее средство. Предлагалось воспользоваться git-tfs, но оказалось, что он не работает с сервером TFS 2012. Вместо него, однако, нашелся Git-TF, который мало того, что рекомендуется Микрософтом, но и не требует установленного Team Explorer. Насколько я могу судить, он должен нормально работать и с более старыми серверами: 2007 или 2010.
  2. Клонирование TFS в локальный Git. Здесь важно выполнить команду с параметром --deep, иначе клонируется только последний changeset, а нам нужна вся история. Пример команды выглядит так:
$ git tf clone http://tfs.server.com:8080 "$/Path/To/Your/project" ./tfs-migration --deep

Connecting to TFS...
Username: username
Password:
Cloning $/Path/To/Your/project into D:\Projects\temp\tfs-migration: 100%, done.
Cloned 446 changesets. Cloned last changeset 794995 as d147076
  1. Инициализация SVN в локальном репозитории. Следующим шагом нужно инициализировать в только что созданном клоне рабочую копию SVN. Не забудьте перейти в нужную папку:
$ cd tfs-migration/
$ git svn init -s https://svn.server.com/svn/target_repo/

  1. Обновление. Прежде чем приступить непосредственному перемещению изменений, нужно привести в актуальное состояние рабочую копию. Делается это с помощью команды fetch. Она обновить локальную папку и выведет в консоль хэши всех скачанных ревизий.
$ git svn fetch

r1 = 4862c0c6fd9b392e66595f8bb6f8b742106596ba (refs/remotes/trunk)
r2 = fdaaffc0568606b9bccd135d44ed42d1e703951a (refs/remotes/trunk)

  1. Сам ритуал! Теперь нужно произвести rebase из основной ветки нашего гита, куда TFS сложил все свое добро, в trunk. Это команда, как мы знаем, последовательно применит все изменения из одной ветки к другой ветке, начиная  с указанной ревизии. Указать правильную ревизию – важный момент в этом шаге. Я потратил достаточно много времени, пытаясь понять, почему вместо необходимой операции Git чистит мне репозиторий. rebase нужно делать на последнюю доступную ревизию. Хэш проще всего получить из вывода предыдущей команды – взять тот, который был выведен в списке последним (значение r2 в данном случае).
$ git rebase --onto trunk fdaaffc master

First, rewinding head to replay your work on top of it...
Applying: Initial check-in of the project
Applying: Added launch config
Applying: Separated RootController from deprecated / removed base class
Applying:
Applying: updated collection set rank options - they were out of sync
...

  1. Заключительный шаг – коммит. Осталось только отправить изменения на сервер. Этот этап занимает наиболее значительную часть времени и у меня на небольшом проекте длился несколько часов. К счастью, он не требует постоянного контроля, и даже если процесс прервется (например, отвалится соединение с сервером, как это произошло у меня), команду можно будет повторить.
$ git svn dcommit

Committing to https://sami.cdt.int.thomsonreuters.com/svn/nt_newsroom/trunk ...
        A       .tpignore
        A       pom.xml
        A       src/main/java/META-INF/webapp/WEB-INF/css/main.css
...
Committed r446
        M       src/main/java/META-INF/webapp/WEB-INF/js/core.js
r446 = 25b8eea5e33d5b1c25a2c44e3035470cc51716da (refs/remotes/trunk)
No changes between d7aff0968e1def1234215622581db9df35d7e528 and refs/remotes/trunk

Resetting to the latest refs/remotes/trunk

Вот и все. Теперь можно наслаждаться SVN со всей историей проекта.

Saturday, July 27, 2013

Starting off, explaining things

Идея этого блога родилась в те далекие времена, когда в первичном бульоне, подогреваемом в космическом котле дыханием вечного дракона времени, в соответствии с божественным планом формировались первые сложные аминокислоты...
В реальности все было несколько проще: однажды я понял, что опыта и знаний, которыми можно поделиться с миром, у меня уже достаточно, поэтому вот он – результат – перед вашими глазами.
Я долго думал, вести ли блог на русском или английском. С одной стороны, большинство моих потенциальных читателей говорят по-русски, с другой – люди редко ищут в Интернете что-то полезное не по-английски. Определиться я так и не смог, поэтому решил, что языков будет два – под настроение.
Как следует из описания блога, в основном я собираюсь писать о разработке ПО и об Agile, т.е. о том, что мне особенно интересно. С тех пор, как я впервые начал работать в отрасли, я успел побывать в роли разработчика на C#, Java и даже инженера техподдержки (и это, скажу вам, был довольный позновательный опыт, которого недостает многим программистам). Сегодня я работаю на JavaScript, по совместительству являюсь скрам-мастером, а на досуге интересуюсь Ruby.
В общем, жду всех в гости: читайте и комментируйте. Будет интересно.