Задача: є репозиторії api, admin і shared, і їх треба перенести в один репозиторій з каталогами apps/api, apps/admin, packages/shared - не втративши історію, щоб git log і blame працювали для старого коду.
Крок 1 - у кожному вихідному репозиторії перемістити файли в цільовий каталог через переписування історії (на свіжому клоні):
git clone https://github.com/acme/api.git api-rewrite
cd api-rewrite
git filter-repo --to-subdirectory-filter apps/api
Кожен коміт історії тепер виглядає так, ніби файли завжди лежали в apps/api. Без цього кроку git log -- apps/api/... і blame губили б історію на межі переміщення.
Крок 2 - злити переписані історії в новий репозиторій:
git init monorepo && cd monorepo
git commit --allow-empty -m "Initial monorepo commit"
git remote add api ../api-rewrite
git fetch api
git merge --allow-unrelated-histories --no-edit api/main
--allow-unrelated-histories потрібен, бо історії не мають спільного предка - без нього Git відмовляє: refusing to merge unrelated histories.
Повторити для admin і shared. Результат - коміт злиття, в якому сходяться всі історії.
Що ще врахувати:
- теги конфліктують:
v1.0.0є в кожному репозиторії. Перейменувати при переписуванні:git filter-repo --tag-rename '':'api-'даєapi-v1.0.0; - гілки в роботі: відкриті гілки теж треба перенести - переписати й відновити в монорепозиторії, або домовитися злити їх до міграції;
- pull request і задачі посилаються на старі хеші й репозиторії - старі репозиторії архівувати (не видаляти), лишивши в README посилання на монорепозиторій;
- CI, деплой, права, вебхуки налаштувати заново під каталоги;
- залежності: якщо
apiпідключавsharedяк Composer-пакет за версією - перейти наpath-репозиторій, інакше в монорепозиторії залишаться дві копії коду; .gitignore,.gitattributes, конфіги лінтерів з коренів репозиторіїв опиняться в підкаталогах - вирішити, що лишити локально, а що винести в корінь.
Порядок міграції: заморозити push у старі репозиторії, перенести, перевірити історію (git log --follow, blame на кількох файлах), перемкнути CI - і лише потім відкрити монорепозиторій для роботи.
Зворотна операція - виділення каталогу в окремий репозиторій - git filter-repo --subdirectory-filter packages/shared на свіжому клоні.
Докладніше в документації: git merge: --allow-unrelated-histories