Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Как организовать работу с системами контроля версий для разработки нескольких проектов с общей основой?


Стоит следующая задача: мне надо создать хранилище, где будут разные файлы скрипто
(например, A). Также будет хранилище, где будет стандартный набор файлов для начала программирования сайта (например, B). Когда я создаю новый проект — создается новое хранилище (например, C). 

Теперь мне нужно в С перенести стандартный набор из хранилища В. Потом мне нужн
из А перенести 2 скрипта в С. После этого я вижу, что в стандартных файлах (которые хранятся в В) есть ошибка. Я её исправляю и хочу залить как на В, так и на С. И так далее. То есть мне надо с ними 3-мя работать одновременно. 

Уже пробовал git и svn, ничего не получилось. Решение через git submodule не подходит
т.к. общие файлы нужно редактировать в конкретных проектах, и при этом иметь возможность подтягивать изменения в этих же файлах из основного набора.

В какой системе контроля версий и каким образом можно организовать хранилища дл
решения этой задачи? 
    


Ответы

Ответ 1



Важно: если для вашего языка есть хорошая система управления зависимостями, то приведённо здесь решение лучше не использовать. Потому что если она есть, гораздо проще сделать из основы отдельную библиотеку и устанавливать её, как зависимость. В Node.js — NPM-пакет, в Ruby — gem, в PHP — пакет Composer, в Rust — crate, и т. д. А то получится избыточность и могут добавиться хлопоты при обновлениях, которых можно избежать, изначально не используя систему контроля версий не по назначению. git может такое. Вообще у меня впечатление, что git может всё. Правда, механизм работы не слишко простой, нужно понимать, как он при этом будет работать. Поскольку git по природе своей распределённый, я сэмулирую ваш порядок работы в одно локальном репозитории на нескольких несвязанных ветках: a, b и master. Изменения в эти ветках могут запросто появляться из других репозториев (разные ветки могут следить за разными серверами!), но при использовании такой методики у вас локально должен быть репозиторий, в котором есть все три. Поехали по пунктам: Положим, что git init вы сделали, а С (сам проект) лежит в ветке master (что совершенно необязательно). создать хранилище, где будут разные файлы скриптов (например, A) Делаем "несвязанную ветку": git checkout --orphan a Зачищаем папку и индекс, чтобы начать с чистого листа: git reset && git clean -f Делаем коммит с фигнёй: echo 'скрипт' | tee script_a_1 script_a_2 git add . git commit # Тут вас попросят ввести сообщение для коммита хранилище, где будет стандартный набор файлов для начала программирования сайта (например, B) То же самое. git checkout --orphan b git reset && git clean -f echo 'скрипт' | tee script_b_1 script_b_2 git add . git commit # Сообщение для коммита Когда я создаю новый проект - создается новое хранилище (например, C). Будем считать, что это ветка master. И в настоящий момент она должна быть пуста для git это означает, что она не существует, поэтому придётся делать её заново: git checkout --orphan master git reset && git clean -f Теперь мне нужно в С перенести стандартный набор с хранилища В. Мёрдж: git merge b При этом произойдёт fast-forward до b, master будет совпадать с веткой b. Это нормально. Ведь на этом этапе состояния файловой системы проекта совпадают, верно? Потом мне нужно с А перенести 2 скрипта в С. Мёрдж с a, но на сей раз с "тормозами", чтобы прямо перед коммитом git остановился: git merge --no-commit --no-ff a Зачем? Затем, что вам не все файлы нужны. На этом этапе вы можете убрать из индекс ненужные вам файлы из индекса с помощью reset, зачистить оставшееся и закоммитить результат: git reset script_a_2 git clean -f git commit Теперь немного "поработаем", для вида: echo work > work git add work git commit в стандартных файлах (которые хранятся в В) есть ошибка. Я ей исправляю и хоч залить как на В... Поскольку у вас (семантически) master основан на b, а не наоборот, ошибку вам над починить именно в b, чтобы потом изменения "растеклись" (не автоматически!) по тем, кто ею пользуется. Идём в ветку b и чиним: git checkout b # clean уже не нужен, т. к. ветка не пустая echo script > script_b_1 # Ну, допустим, что кириллица не переварилась. Мало ли. git add script_b_1 git commit На этом этапе, если репозитории с b и master всё-таки разные, должен быть git pus ветки b в соответствующий репозиторий, а в репозитории проекта нужно сделать git pul --ff-only (--ff-only разрешает только перемотку ветки — чтобы ваши изменения не "затекли" в b) в ветке, которая за тем репозиторием следит. Это уже отдельная тема, если интересно, расскажу и об этом. ...так и на С. Переходим в ветку с проектом: git checkout master И делаем мердж ветки b в проект. git merge b Готово. Да, вот так просто! После проделывания вышеописанных манипуляций, я получил в master следующую схем из коммитов (git log --graph): * commit 2e9219cb2922f70382a8f069fdc917bdbfccd4b8 |\ Merge: 2cc463a 1fc0b61 | | Author: Имя <адрес> | | Date: Sun Dec 27 14:46:12 2015 +0300 | | | | мердж фикса | | | * commit 1fc0b61c1017acf4b4ef1941e1cdcb01a7e86be4 | | Author: Имя <адрес> | | Date: Sun Dec 27 14:41:23 2015 +0300 | | | | фикс основы | | * | commit 2cc463ae97500bff4a304990aa4e42159da96fe9 | | Author: Имя <адрес> | | Date: Sun Dec 27 14:38:37 2015 +0300 | | | | работа | | * | commit cfa122c954f77b3282f51511f94bc6d97c95d569 |\ \ Merge: 7da066c f9304d8 | |/ Author: Имя <адрес> |/| Date: Sun Dec 27 14:31:20 2015 +0300 | | | | мердж с удалением лишних | | | * commit f9304d82d89600add270544bec32cd6661a8c150 | Author: Имя <адрес> | Date: Sun Dec 27 13:46:39 2015 +0300 | | скриптики | * commit 7da066c234216057ff19775a355342ed501fe9a4 Author: Имя <адрес> Date: Sun Dec 27 13:54:01 2015 +0300 основа И для наглядности, перерисовал:

Ответ 2



Если не хочется делать несколько веток, отслеживающих разные сервера, и так дале как в другом ответе, то можно работать аналогично тому как разрабатываются крупные распределённые проекты вроде ядра Linux или языка Go - через файлы с патчами, с тем лишь отличием что ничего никому не нужно посылать по почте. Вносим изменения в файлы проекта C, которые нужно перенести обратно в A или B: git add -p; git commit У нас получился коммит в котором меняются файлы имеющие место только лишь в A или B, обязательно не одновременно. Проверяем это с помощью git show. Экспортируем этот коммит в файл: git format-patch -1 Команда создаёт файл с изменениями вида 0001-example.patch. Переходим в проект A или B, применяем там ранее полученный патч: git am ../ProjectC/0001-example.patch В лучшем случае ничего больше делать не надо. В худшем случае может потребоваться решить обычные конфликты, которые появляютс при слиянии. Например, так: find . -type f -name \*orig -print -delete find . -type f -name \*rej -print -delete patch -p1 < .git/rebase-apply/patch find . -name \*.rej | while read rej do echo $rej wiggle -r ${rej%.rej} $rej done Таким же образом изменения переносятся обратно. Вот и всё. Ограничение остаётся всё то же самое: для удобного и простого отслеживания истории файлы в проекте C должны оставаться там же где и в проектах A и B. То же относится к работе с несколькими проектами: чтобы переносить какое-то изменени из проекта-шаблона B в копии С1, С2, С3... При любом подходе вам нужно будет писать программу или скрипт. Недостатки такого подхода Недостатки очевидны - это относительно ручная работа для переноса изменений (которая в принципе поддаётся автоматизации), но и о достоинствах забывать не стоит. Достоинства такого подхода Самое очевидное достоинство такого подхода: простота при использовании и отсутстви необходимости помнить где какая ветка, а значит меньше шансов сделать merge не того не туда. Меньше ошибок - меньше времени на их исправление. Больше времени на более интересные занятия. Ещё одно достоинство: конфиденциальность. Если вам когда-либо нужно будет дать досту к Git посторонним (например, вашему подрядчику для проведения рутинных работ), то посторонний не увидит всей-всей истории изменений в проектах A и B, которая обязательно попадет в C при любом слиянии веток. Например, если вы начинаете новый проект из шаблона так: # начинаем новый проект git clone TemlateB NewProjectC cd NewProjectC git reset $(echo "NewProjectC Started" | git commit-tree HEAD^{tree}) git remote rm origin # спустя некоторое время git am ../TemlateB/0001-example-fix.patch То в такой ситуации в проекте C посторонний увидит только какой-то самый недавни коммит и коммиты-исправления, а не всю историю проектов A и B за всё время. Естественно, посторонний не увидит и изменений из других проектов, аналогичных C. Масштабируемость - ещё одно достоинство этого подхода. Поддерживать 30-50-80 копи проекта? Нет проблем! Делегировать работу над каким-то проектом подрядчику? Проще простого! Убрать какой-то проект в архив с глаз долой? Нет проблем! Вернуть из архива? Легко! Теперь представьте себе сколько экранов будут занимать 80+ веток по каждому из когда-либ начатых проектов. Даже и говорить не стоит о том чтобы убрать какую-то ветку в архив или делегировать что-то кому-то. Нет, нельзя сказать что это невозможно...

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

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