Git/rebase

Материал из ALT Linux Wiki
< Git


DISCLAIMER

Внимание: нижеописанное обсуждалось в devel-distro@ в контексте совместной работы над mkimage-profiles-desktop, для которых на данный момент нет автосборочницы с проверкой наследования коммитов. Перед применением к публикуемым на git.alt репозиториям, из которых осуществляется сборка при помощи girar-builder, следует как минимум задуматься над озвучиваемыми ниже вопросами этого самого наследования.

rebase vs merge

Когда над одним проектом работает более одного человека, часто получается следующая ситуация:

A (начальный коммит) -> B (работа одного человека)  \__ merge
                     -> C (работа второго человека) /

Чем это плохо? Нет последовательности коммитов. Вместо линии получается граф, и чем больше людей работает над проектом и делают merge друг у друга — тем более страшной получается история.

Это делает невозможным нормальное применение таких инструментов как git bisect, например. Да и просто разбираться в этой каше — неудобно.

Теперь представим себе следующее — ты апстрим, развиваешь активно проект. Я сделал маленький патчик, базируясь на версии недельной давности. Ты хочешь его интегрировать.

  • Путь 1: git merge
  • Путь 2: git cherry-pick (по сути это «наложить патч из вон того коммита на текущий коммит, сохранив заголовки и авторство патча»)

Второй путь имеет много преимуществ, правда один недостаток — нет гарантии, что этот патч будет работать с новой версией :) Но в проекте, удачно разбитом на модули, обычно скорее будет, чем нет.

Теперь другой вариант — я сделал много-много-много изменений. Делать git cherry-pick ты задолбаешься. Поэтому есть git rebase — упрощенно это как раз «сделать git cherry-pick на все патчи из моего дерева, поверх вон того дерева», но гораздо более умный — может пропускать уже интегрированные патчи, и т. д.

Мой подход выглядит так:

# предварительно настроено чтобы его master ко мне импортировался как
# бранч с именем 'boyarsh'
git fetch boyarsh  
# надо находиться в своем master
git checkout master
# делаем rebase
git rebase boyarsh
# на этом этапе все мои изменения будут наложены поверх самого свежего 
# бранча boyarsh

# если возникли конфликты -- правлю, делаю git update-index (все как при
# merge), но вместо git commit даю команду:
git rebase --continue

# если данный патч надо просто проигнорировать, например он был уже
# интегрирован и теперь просто пустой, то:
git rebase --skip

Под конец процедуры все мои патчи наложены поверх последнего бранча boyarsh.

Теперь остается только сделать повторную сборку, убедиться что все Ok, и можно делать git push.

На этом этапе возникает некоторая сложность — этот commit не будет наследником того, что было у нас раньше за’push’ено. Поэтому git push надо делать с --force, ну или соответствующим образом .git/config писать ;)

(vsu@ про git.alt) В этом случае отдельные патчи сохраняются, но теряется связь с предыдущей историей пакета, что в новой системе сборки недопустимо. Правда, можно восстановить эту связь через git pull -s ours, но при таком способе история будет засоряться многочисленными копиями одних и тех же изменений для разных версий пакета (впрочем, в этом случае старую историю можно будет просто отсечь при просмотре изменений, поскольку она не будет содержать полезной информации — все изменения относительно upstream после git rebase будут воспроизведены в новых коммитах, либо удалены, если они уже вошли в upstream).

Ссылки