Назад | Перейти на главную страницу

Перенести несколько репозиториев 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 но не смог найти точного решения нашей проблемы.

Одним из решений было бы сгенерировать каждый из репозиториев отдельно с помощью svn2git или просто git svn (это симпатичный маленький инструмент, уже встроенный в git), а затем соедините их вместе с git filter-branch.

  1. Клонируйте каждый репозиторий svn индивидуально.
  2. В репозитории, в котором вы хотите быть root, добавьте другие репозитории в качестве пультов дистанционного управления и извлеките их ветки, которые вы хотите объединить, в это репо (вы получите предупреждения, поскольку у ветвей нет общей истории; это ожидается).
  3. Выполнить git filter-branch в этих новых ветвях, используя индексный фильтр, чтобы создать для них новый подкаталог.
  4. Объедините отфильтрованные ветви в 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 и фиксируйте:

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 будут по пути / to / svn / repository / git.

Затем вы настраиваете доступ к обоим репозиториям Git и добавляете один как подмодуль другого:

$ git clone <frontend_GitURL> frontend
$ git co
$ cd frontend
$ git submodule add -b ng <backend_GitURL> backend