Миграция нескольких SVN-репозиториев в один GIT-репозиторий
Мы хотим постоянно переходить с svn на git, чтобы иметь возможность использовать лучшие функции git с точки зрения ветвления и совместной работы.
Наш текущий SVN-репозиторий выглядит так
svnrepo/
frontend/
trunk
branches/
ng/
...
tags/
1.x
...
backend/
trunk
branches/
ng/
...
tags/
1.x
...
Рабочий макет состоит в том, что мы извлекаем проект внешнего интерфейса и внутри него создаем внутреннюю папку и извлекаем внутренний проект.
Теперь мы хотим перейти на git и отказаться от разделения между внешним и внутренним интерфейсом (с точки зрения разделения проектов), потому что это дает нам больше проблем, чем преимуществ. Мы хотим, чтобы они оба были в одном репозитории git.
Я хотел использовать svn2git для конвертации. К сожалению, последние разработки произошли в ветке, а не в стволе, но я думаю, что это не должно быть проблемой для svn2git. Поэтому новый макет репозитория git должен выглядеть так:
/ => svnrepo/frontend/branches/ng
/backend => svnrepo/backend/branches/ng
Где => означает "перенесено / преобразовано из".
Для конвертации нам не нужно конвертировать все теги и ветки из репозитория svn в git. Это не важно для нас. Однако важно то, что у нас есть полная история всех коммитов для всех файлов в каталоге branch /ng, возвращаясь к ветвлению из транка и всех коммитов, которые происходили в транке до этого. И мы хотим, чтобы все эти коммиты были с указанным макетом в одном репозитории git. Это вообще возможно? И как бы мы это сделали?
Я уже искал с помощью Google, а также в stackoverflow 1, 2, но не смог найти точное решение для нашей проблемы.
3 ответа
Одним из решений будет генерировать каждый из репозиториев отдельно с помощью svn2git или просто git svn
(это хороший маленький инструмент, уже встроенный в git), а затем соедините их вместе с git filter-branch
,
- Клонируйте каждое хранилище SVN в отдельности.
- В хранилище, в котором вы хотите быть пользователем root, добавьте другие хранилища в качестве удаленных и извлеките их ветви, которые хотите объединить с этим хранилищем (вы получите предупреждения, так как ветви не имеют общей истории; это ожидается).
- казнить
git filter-branch
в этих новых ветвях, используя индексный фильтр для создания нового подкаталога для них. - Объединить отфильтрованные ветви в
master
(или любую другую ветку, которую вы хотели) в корневом хранилище. Полная история будет сохранена.
Команда для шага 3 будет выглядеть примерно так:
git filter-branch --index-filter '
git ls-files -s |
perl -pe "s{\t\"?}{$&newsubdir/}" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE
' HEAD
Волшебство, и каждый раз, когда я должен это делать, это немного похоже на магию, это perl
заявление. git filter-branch
фильтрует индекс при каждом коммите и добавляет все пути BLOB-объектов (т. е. изменяет пути к файлам рабочего дерева) с помощью "newsubdir". Возможно, вам придется поэкспериментировать, чтобы получить правильные пути. Несколько уроков, извлеченных от кого-то, кто прошел этот путь раньше:
- Поддержи все.
git filter-branch
история разрушительна Как только вы измените его, вы не сможете легко изменить его обратно. Обязательно сделайте резервную копию всех копий репозитория, которые вы используете. Нет ничего хуже, чем закончить сложную операцию и обнаружить, что ты пропустил/
в пути. - Сценарий все. Если у вас нет серьезных навыков; вы не поймете это правильно с первого раза. Составьте сценарий каждого отдельного шага, чтобы завершить любой из них. Кроме того, если вы обнаружите, что через неделю вы испортили флаг, вы можете повторить это мгновенно.
- Потратьте $20 на экземпляр кластерного вычисления в EC2.
git filter-branch
очень сильно загружает процессор. Индексный фильтр для глубокой истории может занять несколько часов, чтобы работать в вашей локальной среде, но часть этого времени на экземпляре вычисления кластера AWS . Конечно, они стоят чуть больше 2 долларов в час, но вам понадобится всего несколько часов. Избавьте себя от боли и используйте те сценарии, которые вы написали на оборудовании, что делает работу тривиальной. Это стоит цена хорошего обеда.
Все, что я могу думать, это то, что это потребует некоторого крайнего взлома, если svn2git
(из которых я не эксперт) изначально поддерживает это как-то.
Проблема в том, что коммит frontend
полностью независим от коммита в backend
, Нет реального способа определить, какой коммит будет сопоставлен с каким коммитом в одном репозитории. Это оставляет нам только одну реальную опцию: история будет состоять из двух ветвей, которые будут объединены вместе, что представляет историю исходного проекта, а затем, когда они объединены, новая ветвь является "лучшей моделью".
С этого момента, я собираюсь предположить, что у вас есть frontend
в svn-frontend
ветка импортирована и backend
в svn-backend
Ветка импортирована, и обе содержат свою историю.
Первая проблема заключается в исправлении svn-backend
быть в backend/
каталог:
git checkout svn-backend
git filter-branch --index-filter '
git ls-files -s |
perl -pe "s{\t\"?}{$&newsubdir/}" |
GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &&
mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
(См. Эту документацию, а также ответ @Christopher)
Теперь, если они не содержат тот же коммит в качестве базы (вряд ли, если svn2git
создает какой-то предопределенный базовый коммит или что-то...), мы должны сделать один. Не должно иметь значения, на какой ветке вы находитесь, чтобы начать.
git symbolic-ref HEAD refs/heads/svn-base
rm .git/index
git clean -dxf
Git не может отслеживать пустые каталоги. Я никогда не проверял, относится ли это к корневому каталогу, но мое предположение не так, поэтому создайте пустой файл git ignore и commit:
touch .gitignore
git add .gitignore
git commit -m "Base for SVN branches"
Перепишем историю:
git rebase svn-base svn-frontend
git rebase svn-base svn-backend
Мы почти закончили. Теперь давайте создадим основную ветку. Если он уже существует:
git update-ref master "$head"
Иначе:
git branch master
Давайте проверим это:
git checkout master
Наконец, слияние:
git merge svn-backend
Хорошая идея пометить старые ветви и затем удалить их:
git checkout svn-frontend
git tag svn-frontend
git branch -d svn-frontend
git checkout svn-backend
git tag svn-backend
git branch -d svn-backend
git checkout master
git branch -d svn-base
Одним из решений является преобразование обоих репозиториев проектов SVN в 2 репозитория Git, а затем добавление одного репозитория Git в качестве подмодуля Git другого.
Чтобы преобразовать ваш SVN-репозиторий в Git-репозитории, вы можете использовать любой скрипт на основе git-svn или SubGit. С последним инструментом вы запускаете одну команду
$ subgit install path/to/svn/repository
Преобразованные репозитории git будут находиться по пути / к /svn/repository/git.
Затем вы настраиваете доступ к обоим репозиториям Git и добавляете один в качестве подмодуля другого:
$ git clone <frontend_GitURL> frontend
$ git co
$ cd frontend
$ git submodule add -b ng <backend_GitURL> backend