Стоит следующая задача: мне надо создать хранилище, где будут разные файлы скрипто
(например, 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+ веток по каждому из когда-либ
начатых проектов. Даже и говорить не стоит о том чтобы убрать какую-то ветку в архив или делегировать что-то кому-то. Нет, нельзя сказать что это невозможно...
Комментариев нет:
Отправить комментарий