Git/rebase: различия между версиями
м (init) |
(→rebase vs merge: typo fixed) |
||
Строка 41: | Строка 41: | ||
# бранч с именем 'boyarsh' | # бранч с именем 'boyarsh' | ||
git fetch boyarsh | git fetch boyarsh | ||
# надо | # надо находиться в своем master | ||
git checkout master | git checkout master | ||
# делаем rebase | # делаем rebase |
Текущая версия от 15:24, 16 августа 2013
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).