Зачем?
Те, кто хочет побыстрее приступить к делу, могут перейти сразу к пункту «Процесс миграции».
Недавно пришлось заниматься такой странной вещью, как миграция репозитория из TFS в SVN. На то были веские причины: начиная от Микрософстовского лицензирования®, которое ограничивало нас в возможности настроить CI на проекте, и заканчивая неприятными особенностями, которые просто жутко бесят в повседневной работе. Например, тем, что TFS самостоятельно навешивает на каждый файл флаг Read Only, что усложняет процесс сохранения в некоторых редакторах. Или тем, что клиент требует постоянного подключения к серверу. Вероятно, переход на Git обрадовал бы меня еще больше, но корпоративные особенности ограничивали выбор. При этом стояла задача сохранить всю историю изменений старого репозитория.Инструменты
В Интернете достаточно много статей о том, как мигрировать из TFS в SVN. В некоторых предлагаются рабочие способы, в других – достаточно странные. Чаще всего можно найти упоминание о tfs2svn. Его я и попробовал первым. К сожалению, оказалось, что эта утилита довольно древняя, построена на базе старого клиента, и с новым SVN работать отказывается. К тому же, судя по отзывам, она не вполне стабильна, любит вылететь с ошибкой на середине миграции, поэтому пришлось поискать другой способ.Описание другого способа я нашел в одном блоге, и заключался он в миграции через Git. Этот способ оказался достаточно простым и сработал как часы, но в процессе я наткнулся на несколько подводных камней, которые заставили меня, как не особенно опытного пользователя Git, немного попотеть. Поэтому я и решил подробно описать весь процесс и те трудности, с которыми пришлось столкнуться.
Процесс миграции
Идея миграции состоит в том, чтобы сначала клонировать репозиторий из TFS со всей историей в локальный Git, потом создать в нем репозиторий SVN, сделать rebase ветки master на trunk и результат залить в SVN. История при этом сохраняется полностью.
Есть, правда, у этого способа одна особенность: все изменения в SVN будут зафиксированы от имени единственного пользователя, который непосредственно будет производить коммит в SVN (т.е. от вашего имени). Я в этом не вижу никакой проблемы, хотя некоторые считают, что для кого-то это может стать препятствием.
Есть, правда, у этого способа одна особенность: все изменения в SVN будут зафиксированы от имени единственного пользователя, который непосредственно будет производить коммит в SVN (т.е. от вашего имени). Я в этом не вижу никакой проблемы, хотя некоторые считают, что для кого-то это может стать препятствием.
Итак, сам процесс по шагам выглядит примерно так.
- Установка Git-TF. В качестве моста с SVN подойдет стандартный git-svn. Для работы с TFS понадобится установить стороннее средство. Предлагалось воспользоваться git-tfs, но оказалось, что он не работает с сервером TFS 2012. Вместо него, однако, нашелся Git-TF, который мало того, что рекомендуется Микрософтом, но и не требует установленного Team Explorer. Насколько я могу судить, он должен нормально работать и с более старыми серверами: 2007 или 2010.
- Клонирование 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
- Инициализация SVN в локальном репозитории. Следующим шагом нужно инициализировать в только что созданном клоне рабочую копию SVN. Не забудьте перейти в нужную папку:
$ cd tfs-migration/ $ git svn init -s https://svn.server.com/svn/target_repo/
- Обновление. Прежде чем приступить непосредственному перемещению изменений, нужно привести в актуальное состояние рабочую копию. Делается это с помощью команды fetch. Она обновить локальную папку и выведет в консоль хэши всех скачанных ревизий.
$ git svn fetch r1 = 4862c0c6fd9b392e66595f8bb6f8b742106596ba (refs/remotes/trunk) r2 = fdaaffc0568606b9bccd135d44ed42d1e703951a (refs/remotes/trunk)
- Сам ритуал! Теперь нужно произвести 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 ...
- Заключительный шаг – коммит. Осталось только отправить изменения на сервер. Этот этап занимает наиболее значительную часть времени и у меня на небольшом проекте длился несколько часов. К счастью, он не требует постоянного контроля, и даже если процесс прервется (например, отвалится соединение с сервером, как это произошло у меня), команду можно будет повторить.
$ 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 со всей историей проекта.