Git/MergingBranches: различия между версиями

Материал из ALT Linux Wiki
< Git
м (→‎Схема с последовательным прикладыванием патчей: пояснил про то, что topgit сам знает)
 
(не показана 21 промежуточная версия этого же участника)
Строка 5: Строка 5:
===Предисловие===
===Предисловие===
Наш mutt1.5 это такой mutt2ng-new с большим количеством «левых» патчей. Из-за такого количества каждое обновление версии превращается в изощрённую пытку для мантейнера. Посмотрим, как git может помочь решить подобные проблемы.
Наш mutt1.5 это такой mutt2ng-new с большим количеством «левых» патчей. Из-за такого количества каждое обновление версии превращается в изощрённую пытку для мантейнера. Посмотрим, как git может помочь решить подобные проблемы.
Это касается схемы работы над пакетом в gear [[Руководство по gear#Репозиторий с отдельной веткой upstream и topic-ветками|Репозиторий с отдельной веткой upstream и topic-ветками]].


===Общий мёрж в master===
===Общий мёрж в master===
Строка 68: Строка 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-странице.
Строка 83: Строка 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 (см. описание в README), чтобы автоматически сделать необходимые merges (ведь просто сравнивая истории веток в git, он сможет понять, что она ещё не полностью смёрджена в зависимые без доплнительных подсказок). Также есть другие полезные команды для работы.
=====Принципы 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-ами, тоже работающими над этим пакетом.


=== Ссылки===
=== Ссылки===
Строка 89: Строка 210:


{{Category navigation|title=git|category=git|sortkey={{SUBPAGENAME}}}}
{{Category navigation|title=git|category=git|sortkey={{SUBPAGENAME}}}}
{{Category navigation|title=gear|category=gear|sortkey=*}}

Текущая версия от 14:13, 21 ноября 2018

Merge-arrow.svg
Необходимо перенести содержимое этой статьи в статью Руководство по gear
Вы можете помочь проекту, объединив их.


Хранение патчей в отдельных бранчах 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 (см. описание в README), чтобы автоматически сделать необходимые merges (ведь просто сравнивая истории веток в git, он сможет понять, что она ещё не полностью смёрджена в зависимые без доплнительных подсказок). Также есть другие полезные команды для работы.

Принципы topgit

В topgit формально сохраняется информация о зависимостях веток-патчей друг от друга. Работа команд опирается на эту информацию.

Но сама по себе запись зависимостей в известном формате полезна, потому что на основе неё можно автоматизировать обработку веток-бранчей так, как задумано их создателем.

Например, я подумываю об инструменте для экспорта репозитория topgit в кучку репозиториев darcs, где каждый репозиторий-ветка будет подмножеством зависимых репозиториев; в общем случае задача транслировать историю Git с ветками в darcs так, чтобы отношение быть подмножеством было хорошим, кажется не очень простой (и по крайней мере, на сегодня окончательно не решённой с помощью автоматики).

Схема с общим 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-ами, тоже работающими над этим пакетом.

Ссылки