git/MergingBranches
Материал из ALT Linux Wiki
м (→Схема с последовательным прикладыванием патчей: лучше воспринимаемое форматирование старой и новой информации) |
|||
(22 промежуточные версии не показаны) | |||
Строка 1: | Строка 1: | ||
- | + | {{DISPLAYTITLE:git/MergingBranches}} | |
{{merge|Руководство по gear}} | {{merge|Руководство по gear}} | ||
- | |||
- | |||
== Хранение патчей в отдельных бранчах git-репозитория == | == Хранение патчей в отдельных бранчах git-репозитория == | ||
===Предисловие=== | ===Предисловие=== | ||
Наш mutt1.5 это такой mutt2ng-new с большим количеством «левых» патчей. Из-за такого количества каждое обновление версии превращается в изощрённую пытку для мантейнера. Посмотрим, как git может помочь решить подобные проблемы. | Наш mutt1.5 это такой mutt2ng-new с большим количеством «левых» патчей. Из-за такого количества каждое обновление версии превращается в изощрённую пытку для мантейнера. Посмотрим, как git может помочь решить подобные проблемы. | ||
+ | |||
+ | Это касается схемы работы над пакетом в gear [[Руководство по gear#Репозиторий с отдельной веткой upstream и topic-ветками|Репозиторий с отдельной веткой upstream и topic-ветками]]. | ||
===Общий мёрж в master=== | ===Общий мёрж в master=== | ||
Строка 48: | Строка 48: | ||
1. upstream | 1. upstream | ||
- | patchA <- upstream | + | <pre>patchA <- upstream |
patchB <- patchA | patchB <- patchA | ||
… | … | ||
- | master <- patchZ | + | master <- patchZ</pre> |
2. patchX | 2. patchX | ||
- | + | <pre> | |
branch patchX-tmp upstream | branch patchX-tmp upstream | ||
накладываем новый patchX в patchX-tmp | накладываем новый patchX в patchX-tmp | ||
Строка 63: | Строка 63: | ||
… | … | ||
master <- patchZ | master <- patchZ | ||
+ | </pre> | ||
3. patchX и upstream | 3. patchX и upstream | ||
Строка 69: | Строка 70: | ||
=== Автоматизация процесса последовательного мёржа=== | === Автоматизация процесса последовательного мёржа=== | ||
+ | |||
+ | ====gear-merge==== | ||
Автоматизировать процесс можно с помощью утилиты [http://lists.altlinux.org/pipermail/devel/2007-November/065830.html gear-merge]. Утилита довольно простая. Краткий синтаксис правил описан в заголовке (vim /usr/bin/gear-merge), опции описаны в man-странице. | Автоматизировать процесс можно с помощью утилиты [http://lists.altlinux.org/pipermail/devel/2007-November/065830.html gear-merge]. Утилита довольно простая. Краткий синтаксис правил описан в заголовке (vim /usr/bin/gear-merge), опции описаны в man-странице. | ||
Строка 84: | Строка 87: | ||
merge: patches/alt/007-krb5 patches/alt/008-Makefile | merge: patches/alt/007-krb5 patches/alt/008-Makefile | ||
merge: patches/alt/008-Makefile master</pre> | merge: patches/alt/008-Makefile master</pre> | ||
+ | |||
+ | ====topgit==== | ||
+ | |||
+ | Также при такой схеме работы с ветками-патчами разработчику поможет довольно широко известная утилита [http://packages.altlinux.org/en/Sisyphus/srpms/topgit topgit]. О её возможностях см. [https://github.com/greenrd/topgit/blob/master/README README (upstream)]. | ||
+ | |||
+ | (К сожалению, на git.alt репозиторий с пакетом не может хранить ветки topgit. {{altbug|30718}}) | ||
+ | |||
+ | =====Схема с последовательным прикладыванием патчей===== | ||
+ | |||
+ | Из примера выше вместо <code>git branch patchA upstream</code> для создания ветки патча надо было бы сделать: | ||
+ | |||
+ | tg create patchA upstream | ||
+ | |||
+ | или (тот же эффект): | ||
+ | |||
+ | git checkout upstream | ||
+ | tg create patchA | ||
+ | # теперь patchA is checked out | ||
+ | |||
+ | После этого будет предложено внести описание патча в .topmsg (что в принципе полезно). | ||
+ | |||
+ | И так с другими ветками, скажем: | ||
+ | |||
+ | tg create patchB [patchA] | ||
+ | ... | ||
+ | tg create master [patchZ] | ||
+ | |||
+ | Работать с ветками как обычно в Git. | ||
+ | |||
+ | Когда какая-нибудь ветка подрастает (добавились коммиты), у topgit есть команда tg update, чтобы автоматически сделать необходимые merges (см. описание в README). Также есть другие полезные команды для работы. | ||
+ | |||
+ | =====Принципы topgit===== | ||
+ | В topgit формально сохраняется информация о зависимостях веток-патчей друг от друга. Работа команд опирается на эту информацию. | ||
+ | |||
+ | Но сама по себе запись зависимостей в известном формате полезна, потому что на основе неё можно автоматизировать обработку веток-бранчей так, как задумано их создателем. | ||
+ | |||
+ | Например, | ||
+ | я подумываю об инструменте для экспорта репозитория topgit в кучку репозиториев darcs, где каждый репозиторий-ветка будет подмножеством зависимых репозиториев; в общем случае задача транслировать историю Git с ветками в darcs так, чтобы отношение быть подмножеством было хорошим, кажется не очень простой (и по крайней мере, на сегодня [окончательно не решённой с помощью автоматики](http://darcs.net/DarcsBridgeUsage)). | ||
+ | |||
+ | =====Схема с общим merge-м патчей===== | ||
+ | |||
+ | Схема с общим merge-м патчей (как описано ещё ближе к началу этой страницы) тоже возможна в topgit: | ||
+ | |||
+ | tg create patchA upstream | ||
+ | tg create patchB upstream | ||
+ | ... | ||
+ | tg create patchZ upstream | ||
+ | tg create master upstream patchA patchB ... patchZ | ||
+ | |||
+ | =====если предпочитаете держать патчи не приложенными===== | ||
+ | |||
+ | (Как в схеме работы [[Руководство по gear#Репозиторий с отдельными ветками для upstream и патчей|Репозиторий с отдельными ветками для upstream и патчей]].) | ||
+ | |||
+ | Как известно, в [http://lists.altlinux.org/pipermail/devel/2007-November/146729.html gear-merge] вместо директивы merge можно использовать gendiff, если предпочитаете держать патчи не приложенными. | ||
+ | |||
+ | В topgit тоже можно не прикладывать все патчи в общий master, а использовать (см. README): | ||
+ | |||
+ | tg patch [patchB] | ||
+ | |||
+ | или | ||
+ | |||
+ | tg export --quilt patches | ||
+ | |||
+ | Первая команда выведет один патч (с описанием), вторая создаст серию всех патчей в отдельных файлах в директории patches. | ||
+ | |||
+ | ([https://github.com/greenrd/topgit/issues/40#issuecomment-74869506 Кажется, судя по README, если хочется сделать красивую историю], без бешеного количества merge -- из-за нараставших постепенно веток -- то пригодится "tg export --collapse new" или просто "tg export"; это похоже на "git rebase" для множества веток сразу. Не уверен, что я правильно понял README; могу здесь ошибаться.) | ||
+ | |||
+ | ======вместе с gear====== | ||
+ | |||
+ | А ещё можно дать соответствующие правила для gear: по сути, нужно его попросить генерировать diff между "$(tg base patchB)" и patchB. Для "$(tg base patchB)" есть на самом деле ссылка в .git/refs/ -- её и надо бы записать в .gear/rules. | ||
+ | |||
+ | Простой (но не очень красивый) [http://git.altlinux.org/people/imz/packages/?p=topgit.git;a=commitdiff;h=d214640a6fe88cf6f4153617ec19f58827271e2e пример], как добавить в пакет патч из topgit -- | ||
+ | |||
+ | 1. в .gear/rules добавить: | ||
+ | |||
+ | diff: top-bases/tg_rename:. tg_rename:. name=topgit-tg_rename.patch | ||
+ | |||
+ | 2. а в .spec: | ||
+ | |||
+ | Patch0: topgit-tg_rename.patch | ||
+ | |||
+ | 3. и в .spec в %setup: | ||
+ | |||
+ | %patch0 -p1 | ||
+ | |||
+ | 4. Потом ещё [http://git.altlinux.org/people/imz/packages/?p=topgit.git;a=commit;h=3c4d7d798859402bf79a01be38fcf9b2d86a7e97 не забыть] "git merge -s ours tg_rename" (а если патчей много, то и аргументов много). (Можно в другом порядке, например, 4 сделать в начале, перед 1.) | ||
+ | |||
+ | 5. и [http://git.altlinux.org/people/imz/packages/?p=topgit.git;a=commitdiff;h=ec3608fcb55736cba9da5ba4f69fb36620d465b7 ещё] "gear-update-tag --all". | ||
+ | |||
+ | 4 и 5 надо делать перед каждым релизом, если обновились topic-branches. (Как обычно в таком workflow с gear.) | ||
+ | |||
+ | Недостатки: | ||
+ | |||
+ | * Так изготовленный патч не очень красив (включает служебные файлы с описанием): | ||
+ | |||
+ | Patch #0 (topgit-tg_rename.patch): | ||
+ | + /usr/bin/patch -p1 | ||
+ | patching file .topdeps | ||
+ | patching file .topmsg | ||
+ | patching file README | ||
+ | patching file tg-rename.sh | ||
+ | |||
+ | * и даже приходится в .spec в %prep делать после каждого патча (иначе они конфликтуют по этим файлам!) | ||
+ | |||
+ | rm -f .topdeps .topmsg # clean up topgit internal files | ||
+ | |||
+ | * и к тому же мы сами должны выписывать список патчей | ||
+ | * и следить вручную за порядком приложения (хотя этой информацией обладает topgit) | ||
+ | * и делать вручную "git merge -s ours patchA patchB ..." (хотя это похоже на то, что умеет сам topgit: tg update). | ||
+ | |||
+ | Красивее было бы обучить gear брать патч в виде как от tg patch. А по результату работы "tg export --quilt patches-dir" (по файла series) автоматически строить последовательность команд для прикладывания набора патчей. Или есть [https://github.com/greenrd/topgit/issues/38 предложение другого подхода], который позволит избавиться от части этих неудобств: служебная информация TopGit не должна храниться в дереве в Git, её нужно присоединять к объектам Git как notes, tags или т.п.; если это будет реализовано, то лишнее не будет попадать в результаты работы gear, а gear можно будет не делать зависимым от topgit. | ||
+ | |||
+ | Также [https://github.com/greenrd/topgit/issues/42 обучить] topgit делать "git merge -s ours patchA patchB ..." в ветку (например, master), где .spec (тогда с точки зрения topgit master зависит от этих патчей, но с особой стратегией merge). | ||
+ | |||
+ | '''Как сейчас можно работать (благодаря дополнительным возможностям topgit из ALT): [[:en:Gear with topgit]].''' | ||
+ | |||
+ | Также [[git.alt]] [https://bugzilla.altlinux.org/show_bug.cgi?id=30718 не позволяет хранить] служебные refs/top-bases, что неудобно для хранения рабочего topgit-репозитория и взаимодействия с другими maintainer-ами, тоже работающими над этим пакетом. | ||
=== Ссылки=== | === Ссылки=== | ||
* http://lists.altlinux.org/pipermail/sisyphus/2007-September/207264.html | * http://lists.altlinux.org/pipermail/sisyphus/2007-September/207264.html | ||
+ | |||
+ | |||
+ | {{Category navigation|title=git|category=git|sortkey={{SUBPAGENAME}}}} | ||
+ | {{Category navigation|title=gear|category=gear|sortkey=*}} |
Текущая версия на 08:59, 3 июля 2015
Содержание |
Хранение патчей в отдельных бранчах git-репозитория
Предисловие
Наш mutt1.5 это такой mutt2ng-new с большим количеством «левых» патчей. Из-за такого количества каждое обновление версии превращается в изощрённую пытку для мантейнера. Посмотрим, как git может помочь решить подобные проблемы.
Это касается схемы работы над пакетом в gear Репозиторий с отдельной веткой upstream и topic-ветками.
Общий мёрж в master
Посмотрим на kernel-image. Там каждый патч живёт в отдельном бранче, перед релизом всё мержится в master. Примерно так:
master * merge-C /| / * merge-B / /| / / * merge-A / / /| / / / * merge-upstream / / / /| * | | | | patchC | * | | | patchB | | * | | patchA \ \ \| | +-+-* | upstream | |
По каждому патчу конфликт разруливается сначала в patchX, потом в merge-X. При обновлении версии upstream и/или patchX приходится делать маленький закат солнца вручную с повторным разруливаением всех конфликтов. Это не ядро и патчи имеют обыкновение пересекаться во множестве мест.
Последовательный мёрж бранчей
Есть второй способ, который применяет voins (например, см. его stklos.git и WindowMaker.git). upstream мержится в patchA, patchA в patchB и так далее. При этом конфликты разруливаются практически только один раз при мерже patchN-1 в patchN:
master * |\ | * patchC | |\ | | * patchB | | |\ | | | * patchA | | | |\ | | | | * upstream | | | | |
Что делать при обновлении upstream и/или patchX?
1. upstream
patchA <- upstream patchB <- patchA … master <- patchZ
2. patchX
branch patchX-tmp upstream накладываем новый patchX в patchX-tmp patchX-tmp <- patch(X-1) patchX <- patchX-tmp branch -d patchX-tmp patch(X+1) <- patchX … master <- patchZ
3. patchX и upstream
До patch(X-1) поступаем аналогично 1., потом аналогично 2.
Автоматизация процесса последовательного мёржа
gear-merge
Автоматизировать процесс можно с помощью утилиты gear-merge. Утилита довольно простая. Краткий синтаксис правил описан в заголовке (vim /usr/bin/gear-merge), опции описаны в man-странице.
Вот так выглядит файл правил для мёржа на примере inn.git:
% cat .gear/merge merge: upstream patches/debian/0001-libdb-4.4 merge: patches/debian/0001-libdb-4.4 patches/alt/001-cdb merge: patches/alt/001-cdb patches/alt/002-db4 merge: patches/alt/002-db4 patches/alt/003-docs merge: patches/alt/003-docs patches/alt/004-gdbm merge: patches/alt/004-gdbm patches/alt/005-pie merge: patches/alt/005-pie patches/alt/006-2.4.2-alt merge: patches/alt/006-2.4.2-alt patches/alt/007-krb5 merge: patches/alt/007-krb5 patches/alt/008-Makefile merge: patches/alt/008-Makefile master
topgit
Также при такой схеме работы с ветками-патчами разработчику поможет довольно широко известная утилита topgit. О её возможностях см. README (upstream).
(К сожалению, на git.alt репозиторий с пакетом не может хранить ветки topgit. altbug #30718)
Схема с последовательным прикладыванием патчей
Из примера выше вместо git branch patchA upstream
для создания ветки патча надо было бы сделать:
tg create patchA upstream
или (тот же эффект):
git checkout upstream tg create patchA # теперь patchA is checked out
После этого будет предложено внести описание патча в .topmsg (что в принципе полезно).
И так с другими ветками, скажем:
tg create patchB [patchA] ... tg create master [patchZ]
Работать с ветками как обычно в Git.
Когда какая-нибудь ветка подрастает (добавились коммиты), у topgit есть команда tg update, чтобы автоматически сделать необходимые merges (см. описание в README). Также есть другие полезные команды для работы.
Принципы topgit
В topgit формально сохраняется информация о зависимостях веток-патчей друг от друга. Работа команд опирается на эту информацию.
Но сама по себе запись зависимостей в известном формате полезна, потому что на основе неё можно автоматизировать обработку веток-бранчей так, как задумано их создателем.
Например, я подумываю об инструменте для экспорта репозитория topgit в кучку репозиториев darcs, где каждый репозиторий-ветка будет подмножеством зависимых репозиториев; в общем случае задача транслировать историю Git с ветками в darcs так, чтобы отношение быть подмножеством было хорошим, кажется не очень простой (и по крайней мере, на сегодня [окончательно не решённой с помощью автоматики](http://darcs.net/DarcsBridgeUsage)).
Схема с общим merge-м патчей
Схема с общим merge-м патчей (как описано ещё ближе к началу этой страницы) тоже возможна в topgit:
tg create patchA upstream tg create patchB upstream ... tg create patchZ upstream tg create master upstream patchA patchB ... patchZ
если предпочитаете держать патчи не приложенными
(Как в схеме работы Репозиторий с отдельными ветками для upstream и патчей.)
Как известно, в gear-merge вместо директивы merge можно использовать gendiff, если предпочитаете держать патчи не приложенными.
В topgit тоже можно не прикладывать все патчи в общий master, а использовать (см. README):
tg patch [patchB]
или
tg export --quilt patches
Первая команда выведет один патч (с описанием), вторая создаст серию всех патчей в отдельных файлах в директории patches.
(Кажется, судя по README, если хочется сделать красивую историю, без бешеного количества merge -- из-за нараставших постепенно веток -- то пригодится "tg export --collapse new" или просто "tg export"; это похоже на "git rebase" для множества веток сразу. Не уверен, что я правильно понял README; могу здесь ошибаться.)
вместе с gear
А ещё можно дать соответствующие правила для gear: по сути, нужно его попросить генерировать diff между "$(tg base patchB)" и patchB. Для "$(tg base patchB)" есть на самом деле ссылка в .git/refs/ -- её и надо бы записать в .gear/rules.
Простой (но не очень красивый) пример, как добавить в пакет патч из topgit --
1. в .gear/rules добавить:
diff: top-bases/tg_rename:. tg_rename:. name=topgit-tg_rename.patch
2. а в .spec:
Patch0: topgit-tg_rename.patch
3. и в .spec в %setup:
%patch0 -p1
4. Потом ещё не забыть "git merge -s ours tg_rename" (а если патчей много, то и аргументов много). (Можно в другом порядке, например, 4 сделать в начале, перед 1.)
5. и ещё "gear-update-tag --all".
4 и 5 надо делать перед каждым релизом, если обновились topic-branches. (Как обычно в таком workflow с gear.)
Недостатки:
- Так изготовленный патч не очень красив (включает служебные файлы с описанием):
Patch #0 (topgit-tg_rename.patch): + /usr/bin/patch -p1 patching file .topdeps patching file .topmsg patching file README patching file tg-rename.sh
- и даже приходится в .spec в %prep делать после каждого патча (иначе они конфликтуют по этим файлам!)
rm -f .topdeps .topmsg # clean up topgit internal files
- и к тому же мы сами должны выписывать список патчей
- и следить вручную за порядком приложения (хотя этой информацией обладает topgit)
- и делать вручную "git merge -s ours patchA patchB ..." (хотя это похоже на то, что умеет сам topgit: tg update).
Красивее было бы обучить gear брать патч в виде как от tg patch. А по результату работы "tg export --quilt patches-dir" (по файла series) автоматически строить последовательность команд для прикладывания набора патчей. Или есть предложение другого подхода, который позволит избавиться от части этих неудобств: служебная информация TopGit не должна храниться в дереве в Git, её нужно присоединять к объектам Git как notes, tags или т.п.; если это будет реализовано, то лишнее не будет попадать в результаты работы gear, а gear можно будет не делать зависимым от topgit.
Также обучить topgit делать "git merge -s ours patchA patchB ..." в ветку (например, master), где .spec (тогда с точки зрения topgit master зависит от этих патчей, но с особой стратегией merge).
Как сейчас можно работать (благодаря дополнительным возможностям topgit из ALT): en:Gear with topgit.
Также git.alt не позволяет хранить служебные refs/top-bases, что неудобно для хранения рабочего topgit-репозитория и взаимодействия с другими maintainer-ами, тоже работающими над этим пакетом.
Ссылки