Часто у разработчиков возникает выбор между Merge (слияние) и Rebase (перемещение). В Гугле вы увидите разное мнение, многие советуют не использовать Rebase, так как это может вызвать серьезные проблемы. В статье я объясню, что такое слияние и перемещение, почему вы должны (или не должны) использовать их и как это сделать.

image

Git Merge и Git Rebase преследуют одну и ту же цель. Они предназначены для интеграции изменений из одной ветки в другую. Хотя конечная цель одинаковая, принципы работы разные.

Некоторые считают, что вы всегда должны использовать Rebase, другие предпочитают Merge. В этом есть свои плюсы и минусы.

Git Merge


Слияние — обычная практика для разработчиков, использующих системы контроля версий. Независимо от того, созданы ли ветки для тестирования, исправления ошибок или по другим причинам, слияние фиксирует изменения в другом месте. Слияние принимает содержимое ветки источника и объединяет их с целевой веткой. В этом процессе изменяется только целевая ветка. История исходных веток остается неизменной.
image
Плюсы:

  • простота;
  • сохраняет полную историю и хронологический порядок;
  • поддерживает контекст ветки.

Минусы:

  • история коммитов может быть заполнена (загрязнена) множеством коммитов;
  • отладка с использованием git bisect может стать сложнее.

Как это сделать

Слейте ветку master в ветку feature, используя команды checkout и merge.

$ git checkout feature
$ git merge master
(or)
$ git merge master feature

Это создаст новый «Merge commit» в ветке feature, который содержит историю обеих веток.

Git Rebase


Rebase — еще один способ перенести изменения из одной ветки в другую. Rebase сжимает все изменения в один «патч». Затем он интегрирует патч в целевую ветку.

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

image

Плюсы:

  • Упрощает потенциально сложную историю
  • Упрощение манипуляций с единственным коммитом
  • Избежание слияния коммитов в занятых репозиториях и ветках
  • Очищает промежуточные коммиты, делая их одним коммитом, что полезно для DevOps команд

Минусы:

  • Сжатие фич до нескольких коммитов может скрыть контекст
  • Перемещение публичных репозиториев может быть опасным при работе в команде
  • Появляется больше работы
  • Для восстановления с удаленными ветками требуется принудительный пуш. Это приводит к обновлению всех веток, имеющих одно и то же имя, как локально, так и удаленно, и это ужасно.

Если вы сделаете перемещение неправильно, история изменится, а это может привести к серьезным проблемам, поэтому убедитесь в том, что делаете!

Как это сделать

Переместите ветку feature на главной ветке, используя следующие команды.

$ git checkout feature
$ git rebase master

Это перемещает всю ветку функции в главную ветку. История проекта изменяется, создаются новые коммиты для каждого коммита в основной ветке.

Интерактивное перемещение


Это позволяет изменять коммиты при их перемещении в новую ветку. Это лучше, чем автоматическое перемещение, поскольку обеспечивает полный контроль над историей коммитов. Как правило, используется для очистки истории до слияния ветки feature в master.

$ git checkout feature
$ git rebase -i master

Это откроет редактор, перечислив все коммиты, которые будут перемещены.

pick 22d6d7c Commit message#1
pick 44e8a9b Commit message#2
pick 79f1d2h Commit message#3

Это точно определяет, как будет выглядеть ветка после выполнения перемещения. Упорядочивая объекты, вы можете сделать историю такой, как захотите. Вы можете использовать команды fixup, squash, edit, и так далее.

image

Какой из них использовать?


Так что же лучше? Что рекомендуют эксперты?

Трудно принять единственно правильное решение о том, что лучше использовать, поскольку все команды разные. Всё зависит от потребностей и традиций внутри команды.

Принимайте решения на основании компетенции команды в Git. Для вас важна простота или перезаписывание истории, а может быть что-то другое?

Что рекомендую я?

По мере роста команды становится сложно управлять или отслеживать изменения в разработке, применяя слияние. Чтобы иметь чистую и понятную историю коммитов, разумно использовать Rebase.

Преимущества Rebase:

  • Вы разрабатываете локально: если вы не делились своей работой с кем-либо еще. На данный момент вы должны предпочесть перемещение слиянию, чтобы сохранить свою историю в порядке. Если у вас есть личная вилка репозитория, которая не используется совместно с другими разработчиками, вы можете делать rebase даже после того, как переместились в свою ветку.
  • Ваш код готов к ревью: вы создали пулл реквест. Другие анализируют вашу работу и потенциально стягивают ее к своей вилке для локального ревью. На данный момент вы не должны перемещать свою работу. Вы должны создать коммит «переделать» и обновить ветку. Это помогает отслеживать запросы на пулл реквест и предотвращает случайную поломку истории.
  • Ревью сделано и готово к интеграции в целевую ветку. Поздравляем! Вы собираетесь удалить свою ветку feature. Учитывая, что с этого момента другие разработчики не будут fetch-merging эти изменения, это ваш шанс изменить вашу историю. На этом этапе вы можете переписать историю и сбросить оригинальные коммиты, и эти надоедливые «переделки» и «слияние» сливаются в небольшой набор целенаправленных коммитов. Создание явного слияния для этих коммитов является необязательным, но имеет значение. Он записывает, когда функция достигла master.

Теперь вы знаете хоть и незначительную, но разницу между Merge и Rebase. Уверен, вы примете правильное решение и будете использовать то, что подходит именно вам.

Не забывайте:

code = coffee + developer

Комментарии (15)


  1. Neusser
    07.12.2018 17:08
    +1

    поэтому убедитесь в том, что делаете!

    Ммм, что это означает?


  1. NorthDakota
    07.12.2018 17:18
    -11

    Rebase — это то что нельзя делать.


  1. worldmind
    07.12.2018 17:25
    +5

    Статья ни о чём — rebase (интерактивный) можно и нужно использовать для наведения порядка (собрать коммиты в осмысленные и законченные коммиты) в своей ветке перед мерджем как бы он ни выполнялся, также rebase разумно использовать для синхронизации своего форка/ветки с главной веткой чтобы не иметь мусора про мерджи в истории.
    А вот мердж в мастер уже скорее стоить делать именно merge'ем, во всяком случае если вы не гуру гита.


    1. ilammy
      07.12.2018 17:57

      Из-за стремительного уменьшения относительной доли гуру гита среди пользователей гита уже давно набирает популярность squash merge, когда в master ваша ветка всегда попадает в виде одного коммита.


      1. horror_x
        08.12.2018 00:11

        А чем адепты squash merge аргументируют свой подход? Типа раз никто не следит за своей историей коммитов, то вместо наведения порядка тупо «округлим» её до коммита на фичу?


        1. Dreyk
          08.12.2018 00:33

          некоторые contribution считают под количеству коммитов, поэтому 1 фича — 1 коммит. что в целом не так-то уж и плохо


        1. powerman
          08.12.2018 08:00
          +1

          Я не то, чтобы адепт, но плюсы есть:


          • Стимулирует делать в одной ветке одну задачу.
          • При следовании спеке Conventional Commits получаем чистые и полезные описания всех коммитов в master, при этом над описаниями коммитов в фиче-бранчах можно не париться (ибо зачастую их в принципе невозможно контролировать, напр. когда получаешь сторонний пул-реквест на гитхабе).
          • Упрощает revert при проблемах.
          • Упрощает bisect.

          А вот явных минусов особо не видно. Да, теряется пошаговая история как кто-то пилил фичу и лажал в процессе — ценность этой истории после мержа фичи обычно нулевая. Если будет очень надо — в течении месяца её можно будет ручками восстановить из reflog.


        1. ilammy
          08.12.2018 13:48

          Да, типа того.


          Когда людям искренне наплевать на описания коммитов, мейнтейнеру проще подвести всех под одну гребёнку, схлопнуть все изменения в один коммит и, если ему хочется и не лень, то добавить туда нормально описание самостоятельно. К сожалению, подобный подход подобно замкнутому кругу поддерживает наплевательское отношение к комментариям и разделению работы на коммиты, так как «ведь всё равно засквошится».


          Это может бесить людей, которые заморачиваются с разбиением на логические куски, с нормальными описаниями, возможностью сделать bisect и revert… — когда вся эта красота выбрасывается squash. Но когда твой средний пулл-реквест выглядит так — и люди искренне не понимают, что в этом плохого, так как единицей ревью и работы является пулл-реквест, а не коммит — уж лучше squash, чем эти сообщения сохранять в истории.


          Не все мейнтейнеры достаточно принципиальны в вопросах гигиены и не всегда в достаточной мере наделены властью, чтобы разработчики самостоятельно держали планку качества истории на уровне как у Linux или самого git.


      1. worldmind
        08.12.2018 21:37
        +1

        Это отличный способ если мерджатся изменения которые имеет смысл иметь в виде одного коммита.


  1. dbagaev
    07.12.2018 18:19
    +4

    Rebase — еще один способ перенести изменения из одной ветки в другую. Rebase сжимает все изменения в один «патч». Затем он интегрирует патч в целевую ветку.

    Rebase не изменяет целевую ветку, он «переигрывает» изменения из текущей ветки на вершине целевой ветки, оставаясь в текущей ветке. Для интеграции изменений вам все равно прийдется сделать merge. Однако поскольку в целевой ветке более не будет изменений, сделанных после точки отбранчевания, мердж будет проведен как fast forward, что означает перенесение маркера вершины целевой ветки на конец текущей ветки без merge-комита.

    Я понимаю, что возможно, я придираюсь к словам, и в результате происходит ровно то, что вы и описали. Но эти неточности в понимании тонкостей работы git только вредят. И лишний раз приводят к спорам, что лучше, merge или rebase, хотя это вообще в принципе разные функции!


    1. maxim_ge
      08.12.2018 14:46

      Да, это описание гораздо лучше, чем то, что в статье.



  1. house2008
    07.12.2018 22:58

    На мой взгляд rebase очень удобен только при работе в своей feature ветке для выравнивания с общей (develop) веткой. С shared ветками довольно проблематично, учитывая, что в среднем многие разработчики не очень сильно разбираются в таких тонкостях, проще просто не создавать проблем коллегам и просто использовать merge. Вливание в основную ветку у нас в целом заведено делать через megre с опцией —no-ff.


  1. gecube
    09.12.2018 00:35
    +2

    Статья, к сожалению, слишком поверхностна, чтобы быть полезной. К тому же, она только больше запутывает в механизмах git.
    В целом, rebase существенно более мощный и многофункциональный инструмент, чем просто merge. Но обратной стороной является то, что им так же легко репозиторий привести к испорченному состоянию.

    Что было бы полезно — сделать какую-то основательную статью с анализом всех популярных подходов к git flow и их подводных камней. В частности, в реалиях наличия ci/cd (когда мы хотим недоделанный код катить и сразу видеть результат его выполнения, кстати, ребейз в этом случае чреват ещё дополнительными рассинхронами с теми же средствами сборки и хранения артефактов)

    За перевод спасибо, каких-то сильных ляпов в нем не нашел, но особо и не вчитывался.


  1. gdt
    09.12.2018 06:20

    Rebase и merge — не одно и то же, и после rebase вам все равно придётся делать merge. Так что сама постановка вопроса является некорректной.