В один прекрасный день, наш TFS2012 был обновлен до TFS2013.
Вместе с TFS2013 у нас появилась возможность, оставаясь в рамках привычного интерфейса администрирования и менеджмента прав, перейти на распределенную source control git. (Про плюс и минусы распределенного source control сказано уже столько, что повторять не стоит, т.к. холиваров уже было много.)
Задача: перенести разработку build на git, сохранив при этом историю изменений.
Риски
- Не всю историю удастся перенести: если вы активно перемещали ваш код, иногда часть теряется;
- Код придется переразложить на ветки руками;
- Возможно, придется некоторое время «жить» параллельно на 2 системах контроля версий;
- Если при build вы использовали более одного проекта, то придется подключить их в качестве submodules или придумывать другие способы подключения зависимостей (nuget, к примеру в .net, gem в ruby и т. п.);
- Придется перенести не только код, но и build.
Перед началом миграции кода...
Перед тем, как будете переносить репозиторий, рекомендую погасить технические долги в плане version control: удалить мертвые ветки; смержить то, что нужно смержить; удалить ненужный/неиспользуемый код, файлы, которые не должны быть в source control, в принципе (промежуточные этапы компиляции, индексы resharper и т.п.). Это лучше сделать на TFS, т.к. тащить это в GIT все равно смысла не имеет. Чем меньше сущностей для переноса, тем лучше.
Заранее стоит подумать и о том, как в git будет этот переносимый код разложен. Например, TFS позволял в рамках одного TFS Project вести хоть сотню проектов в смысле кода, и каждым из них управлять отдельно, делать коммиты в отдельную папку и т.п. Git такого не позволит. По крайней мере, управление разными проектами в рамках одного репозитория по отдельности тут невозможно.
Начинаем миграцию
Git-TF – это основная утилита, которая, исключая
Установка Git-TF
Качаем утилиту миграции. Она на java.
Открыв ее, читаем документацию по настройке. Это не долго, советую потратить на это пару минут.
В целом, она тривиальная. Поставить java, прописать пути в переменной окружения на java и на команду Git-TF. Java нужна, чтобы утилиту можно было запустить и на не Windows машинах.
Clone кода
После того, как настройки завершили, выполняем клонирование из консоли.
Синтаксис: git tf clone tfs-server:8080/tfs/*Collection $/Project –deep
–deep означает клонирование с историей, а не только последний слепок кода.
После окончания выкачки кодов в home каталоге (C:/users/myuser) будет находиться проект с именем, соответствующим проекту в TFS.
При этом будет перенесена вся история, которая была в этом репозитории.
Каждый коммит будет помечен тэгом. Тэг — это номер коммита в TFS (очень помогает, если нужно посмотреть, как все было до git).
Варианты миграции
Концепции системы хранения кода в TFS и GIT различные. Поэтому сконвертировать одной командой с сохранением веток TFVC в Git репозитории не так уж просто.
Тут есть ещё второй момент: будет ли это перманентная миграция или какое-то время вы готовы работать.
В зависимости от этого я бы выделил такие варианты миграции:
Перманентно №1
Git-TF может скопировать весь рапозиторий, и будет одна ветка в git- master. Все TFS ветки будут вместе как папки. Вам придется руками создать ветки и так же вручную разложить в них код так, как вам нужно.
Проблема тут очевидна — до точки разделения кода в Git все изменения лежат скопом.
Уже потом придется приводить проект к нормальному для Git виду.
Перманентно №2
Используя GIT TF, можно вытащить все нужные ветки и сделать их ветками-сиротами (orphan), и уже по необходимости в git делать merge. Тут проблема диаметрально противоположная — до точки слияния коды разных веток независимые.
Не перманентно
Если вы готовы некоторое время жить частично в Git, частично TFVC, то этот вариант для вас. Миграция ветки main из TFVC в ветку main на Git. Далее уже бранчуемся от этой перенесенной ветки.
По мере готовности веток в TFVC к слиянию делать merge внутри TFVC, а затем делать Git TF pull из ветки main TFVC в Git main. Таким образом, мы заберем разницу между коммитами в TFVC и Git. Далее делаем rebase по необходимости. Проблема тоже очевидная — жить в 2 разных project какое-то время с 2 разными системами контроля версий. Можно очень хорошо разойтись на уровне кода.
Общие моменты для всех вариантов
После того, как вы локально получили код и его историю, нужно сделать несколько вещей:
Создать в новом TFS projectCollection (это опционально) и все projects. Не буду на этом заострять внимание — можно погуглить или прочесть мою статью про миграцию SVN в Git-TFS.
Push на remote
Далее добавить новый удаленный репозиторий (remote), сделать туда push. Обязательно вместе с tags, чтобы потом можно было искать соответствие.
Раскладывание кода, в случае Перманентно 1
В моем случае, весь перенос растянулся на 2 дня.
Проект был разделен на 12 подпроектов, т.к. именно столько разных логических проектов в нем жило, и все это вынесено в отдельный projectCollection.
Основным ограничивающим фактором было:
- Наличие определенного беспорядка в репозитории;
- Необходимость самому создать и проверить на собираемость все ветки;
- Подключить submodules;
- Создать бинарные ветки в репозиториях с кодом;
- Непонимание в одном из проектов какая ветка жива, какая не жива.
Все это относится не к методу, а к проекту в целом.
Merge, Git TF pull, Rebase в случае не перманентной миграции
Если схематически рисовать, то будет примерно так.
При такой миграции нам повезло — никаких submodules не было, т.о. коды мы перекладывали один в один. Наличие старого кода, мертвых веток, конечно, осложнило жизнь, но мертвые ветки мы не переносили и, следовательно, в Git код был чище.
Ограничение истории:
Git-TF ничего не знает про другие проекты, кроме того, который вы указали. Т.е. если ваш код когда-то был в другом project collection или другом project, то в TFS коммиты из них не увидите — только те, которые в текущем проекте (файлы при этом конечно же будут на диске). Как вы видели историю в TFS, так же вы ее увидите в Git.
Пример: вы сделали move куска кода из одного TFS project в другой. Тогда команда
git tf clone tfs:8080/tfs/DefaultCollection $/ TargetProjects --deep вытащит только последний коммит, без истории.
Если move был сделан недавно, то можно забрать историю из старого проекта, указав последний перед move коммит.
git tf clone tfs:8080/tfs/DefaultCollection $/SourceProjects --deep --version=57012
Если же эти изменения были сделаны давно, то это будет крайне тяжело — вытащить историю, т.к. нужно будет проделать много ручных манипуляций.
Не наступайте на наши грабли:
- “Решили переходить на Git, переходите на Git”, – эта фраза кажется банальной, но если переходить на Git по полгода, то в это время возникнет слишком много проблем, которые сильно измотают всем нервы.
- Git — это source control. Не надо пытаться вместе с Git сразу поменять build систему и процессы разработки. Сделайте сначала одно, затем другое. Именно из-за того, что Git рассматривался в контексте изменения процесса разработки-тестирования-внедрения-сопровождения у одной из команд очень долго идет процесс миграции. Уже полгода…
- Убедитесь, что все разработчики хотя бы минимальную практику работы с Git получили до начала. Иначе может наступить момент, когда “некогда думать, надо делать”. А тут вдруг окажется, что человек не знает, как свой код объединить с другой ветки и выложить на remote repository.
P.S.
Есть у меня небольшой проект GitQuiz (тестовые примеры, видео как их выполнить).
Очень помогает для обучение разработчиков, у которых есть некоторые теоретические знания по работе с распределенными системами хранения исходных кодов, но нет практического опыта. Я его на своих коллегах опробировал.
Ну а совсем для профи, можно мне с этим проектом помочь, есть набор задач, до которых у меня пока руки не дошли.
Автор: SychevIgor