Если нельзя, но очень хочется, то нужно обязательно и ничего в мире не стоит того, чтобы делать из этого проблему!


Интересна Java? Кликай по ссылке и изучай!
Если тебе полезно что-то из того, чем я делюсь в своем блоге - можешь поделиться своими деньгами со мной.
с пожеланием
столько времени читатели провели на блоге - 
сейчас онлайн - 

вторник, 15 октября 2019 г.

Разделение git репозитория на два с сохранением истории

Привет!

Сегодня возникла интересная задачка, ее хочу сохранить на память, т.к. уверен, что буду неоднократно использовать ее в будущем. А блог мой - это записная книга.

Предыстория. Есть большой Codenjoy репозиторий со всеми проектами: сервер, игры, клиенты к играм, скрипты для запуска, воспомагательные скрипты и так далее. Есть сообщество контрибюторов, и они делают форки. Иногда в этих форках они что-то полезное фиксят. Мне это надо в моем master. Я прошу их предлагать пулриквесты, но часто в этих пулриквестах содержится много всякого разного, что мне не очень надо в master - скажем изменения правил и дизайна под ивент, фиксы с которыми я не согласен. А так как все делалось наспех (ивент готовится обычно в свободное от работы время), то естественно контрибьютор не работал с веткой и может предложить либо все либо ничего. А мне потом выбирать коммит за коммитом. Брррр...

Задача. Разделить проекты на подпроекты так, чтобы с одной стороны у меня была возможность одной git командой (clone, pull, log, commit, push) работать сразу со всеми проектами; а с другой - принимать только те Pull Request что мне надо фильтруя лишнее. Читать дальше...
Гипотеза в том, что делать это будет проще, если проекты будут разделены по разным репозиториям. Контрибьюторам удобно - они так же имеют возможность качать то, что им надо: либо весь проект либо один модуль. Задачи можно тречить по разным репозиториям так же отдельно.

Это все гипотеза, и ее надо проверять, т,к. опыта у меня нет. Если вчера я был категорически против, то сегодня на утро уже не против проверить эту гипотезу на какой-то небольшой части проекта.

Дополнительная задача - сохранить в новом репозитории всю историю из старого, но только ту, что относится к текущей папке.

Из гугления по ключевым словам "git how to create submodule from folder" (как думаю, то и пишу/гуглю) я нашел две статьи. Первая, пошаговая инструкция на StackOverflow, а вторая поясняющая чуть больше что за зверь этот git sumbmodules.

Дальше я пошел акуратно выполнять инструкции одну за другой. Собственно это и есть скрипт для разделения, который я желаю сохранить. В нем я выделяю папку /СodingDojo/portable/linux-docker-compose в отдельный репозиторий со всей связанной историей.
git clone https://github.com/codenjoyme/codenjoy.git
cd codenjoy
git remote rm origin
git filter-branch --subdirectory-filter ./CodingDojo/portable/linux-docker-compose -- --all
git remote add origin https://github.com/codenjoyme/codenjoy-portable-linux.git
git push --set-upstream origin master
cd ..
mv codenjoy codenjoy-portable-linux
git clone https://github.com/codenjoyme/codenjoy.git
cd codenjoy
git rm -r CodingDojo/portable/linux-docker-compose

git commit -m "Moving folder to other repo"
git submodule add https://github.com/codenjoyme/codenjoy-portable-linux.git ./CodingDojo/portable/linux-docker-compose
cat .git/config
    [core]
        repositoryformatversion = 0
        filemode = false
        bare = false
        logallrefupdates = true
        symlinks = false
        ignorecase = true
    [remote "origin"]
        url = https://github.com/codenjoyme/codenjoy.git
        fetch = +refs/heads/*:refs/remotes/origin/*
    [branch "master"]
        remote = origin
        merge = refs/heads/master
    [submodule "CodingDojo/portable/linux-docker-compose"]
        url = https://github.com/codenjoyme/codenjoy-portable-linux.git
git status
    new file:   .gitmodules
    new file:   CodingDojo/portable/linux-docker-compose
git add .
git commit -m "Moved folder to other repo"
git push --recurse-submodules=check

Дальше все просто, идем в эту папку на GitHub и смотрим, что появилась ссылка на новый репозиторий внутри текущего. Кликнув по ней можно ознакомиться с этой частью проекта и его историей. 

Тут я мог бы вырезать всю иторию из исходного проекта, как написано в статье, но не сделал это, т.к. мне больше нравится число коммитов 5645 ) Прежде чем пойти дальше надо сконфигурить так, чтобы некоторые команды работали рекурсивно
git config --global status.submoduleSummary true

А вот пару команд, чтобы работать со всеми подмодулями
# клонирование основного репозитория с подмодулями
git clone --recursive https://github.com/codenjoyme/codenjoy.git

# подтянуть все изменения в основном репозитории с изменениями в подмодулях, при условии что они инициализированы
git pull --recurse-submodules
# подтянуть все изменения в каждом подмодулями, при условии что они могут быть еще не инициализированы
git submodule update --remote --init

# показать статус основного репозитория 
git status
# и всех подмодулей
git submodule foreach --recursive 'git status -s'

# команды add / commit для подмодулей
git submodule foreach --recursive 'git add .' 
git submodule foreach 'git commit -m "message"'
git add $(grep path .gitmodules | sed 's/.*= //')
git commit -m "message"

# показать коммиты основного репозитория с подмодулями
git log -p --submodule

# push изменений основного репозитория с подмодулями
git push --recurse-submodules=on-demand
Если вдруг старый проект надо оживить, то в каждом под проекте надо выполнить следующие команды (лучше конечно склонировать его рекурсивно заново, за одно и проверить как все работает с чистого листа)

cd CodingDojo/portable/linux-docker-compose
git submodule init
git submodule update

А вот и видосик на эту тему
Важно! Выявленные неприятности во время работы

При попытке откатиться на старую ветку, которая была создана до разделения я получил 
error: The following untracked working tree files would be overwritten by checkout:
        CodingDojo/portable/linux-docker/README.md
        CodingDojo/portable/linux-docker-compose/.env
        ...
Please move or remove them before you switch branches.
Aborting
Но я настоял и git немного вздохнув, все же выдал мне результат:
git checkout --force new-snake-ui
warning: unable to rmdir 'CodingDojo/portable/linux-docker': Directory not empty
warning: unable to rmdir 'CodingDojo/portable/linux-docker-compose': Directory not empty
warning: unable to rmdir 'CodingDojo/portable/windows-cmd': Directory not empty
Checking out files: 100% (1572/1572), done.
Switched to branch 'new-snake-ui'
При этом конечно же все новые изменения со старой ветки остались. Причем интересно, что консольная версия git видела не все изменения, которые видела Idea. В корне родительского репозитория больше небыло информации про подмодули в файле .gitmodules, а вот в .git/config было. Я изменения откатил на свой страх и риск из Idea. И теперь Idea ничего не показывает, а в консольной версии git купа удалений - кандидаты на коммит. В общем опасносте галактико. Я продолжил
git add .
git stash
Automatic merge failed; fix conflicts and then commit the result. Теперь в консоли чисто, а в Idea купа изменений :) Такое. Видимо они (Git for Windows v2.19.1 и Idea Ultimate 2018.02) смотрят в разные места. Но моя цель спасти эту ветку, подтянув все изменения с мастера в нее.
git merge master
Выдала мне вполне справедливо, долго я ветку не обновлял мастером
Бла бла бла 
Automatic merge failed; fix conflicts and then commit the result.
Конфликты я правил в Idea - их было не много. А вот когда вернулся в консоль и попробовал закоммититься, то наткнулся на субмодули
git commit
error: Committing is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm '
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.
U       CodingDojo/portable/linux-docker
U       CodingDojo/portable/linux-docker-compose
U       CodingDojo/portable/windows-cmd
Помогла банальное игнорирование проблемы
git add .
$ git commit
[new-snake-ui 94c8e3a97] Merge branch 'master' into new-snake-ui
Нюню ) что я закоммитил, сейчас разузнаем... Папки с подмодулями были абсолютно пустыми. Не беда, проведем для них обновление
git submodule update --remote --init
Ноль на массу. Только удалив файлики .git из каждой папки с подмодулем команда ожила
git submodule update --remote --init
Submodule path 'CodingDojo/portable/linux-docker': checked out '12620cb2690fd3505b559baf966f7d8736ef0572'
Submodule path 'CodingDojo/portable/linux-docker-compose': checked out 'f153cd049af33fc0246d40482b3549ef81ef2600'
Submodule path 'CodingDojo/portable/windows-cmd': checked out '33a9aca42003b1db1f48cd6aa6015ec8c0f87c5e'
И показания по git status в Idea и консоли больше не отличались. Фух!
Для веток без конфликтов, вообще все просто решилось. В общем скрипт такой:
git checkout -f some-branch
git merge master
# решить конфликты вручную
git add .
git commit
# :q - выйти из редактора описания merge-коммита 
# удалить все содержимое папок подмодулей (включая файл .git)
git submodule update --remote --init
# profit!

Комментариев нет:

Отправить комментарий