Почему так много проектов предпочитают «git rebase», а не «git merge»?

69

Одним из преимуществ использования DVCS является рабочий процесс edit-commit-merge (по сравнению с edit-merge-commit, часто применяемый CVCS). Благодаря тому, что каждое уникальное изменение записывается в хранилище независимо от слияний, DAG точно отражает истинную родословную проекта.

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

Уточнение: поведение по умолчанию для DVCS - создание коммитов слияния. Почему так много мест говорят о желании увидеть историю линейного развития, которая скрывает эти коммиты слияния?

Джейс Браунинг
источник
18
Инструменты не всегда делают это правильно. И когда они ошибаются, о мальчик, они ошибаются.
Одед
2
@Oded Вы имеете в виду автоматические коммиты слияния (например, созданные git pull)? Я понимаю, почему это может быть проблематично, но мой вопрос касается всех коммитов слияния.
Джейс Браунинг
возможные дубликаты Являются ли частые осложненные конфликты слиянием признаком проблем? «Слияния и перестановки должны вызывать те же самые конфликты, для тех врожденных конфликтов, которые должен разрешить человек (т.е. два разработчика изменяют одну и ту же строку кода) ...»
gnat
После публикации моего ответа у меня возникла мысль, что ИМО не совсем подходит в качестве ответа: git pull --rebase == svn up(свободные-равные, а не строгие-равные)
Изката,
в централизованном хранилище, таком как SourceSafe, изменения будут продолжать регистрироваться в постоянном усилии, но как только будет достигнуто стабильное плато, мы будем маркировать все файлы на определенную дату. Тогда, по крайней мере, вы можете соотнести ошибки либо до, либо после такой-то версии, либо перебазировать строку.
Andyz Smith

Ответы:

65

Люди хотят избежать коммитов слияния, потому что это делает журнал красивее. Шутки в сторону. Похоже на централизованные журналы, с которыми они выросли, и локально они могут делать всю свою разработку в одной ветке. Нет никакой выгоды, кроме этой эстетики, и нескольких недостатков в дополнение к тем, которые вы упомянули, например, создание склонности к конфликтам при извлечении напрямую из коллеги без прохождения через «центральный» сервер.

Карл Билефельдт
источник
5
Вы единственный, кто правильно понял мой вопрос. Как я могу сделать это более понятным?
Джейс Браунинг
12
Может быть, перефразировать «Каковы преимущества линейной истории развития?»
Карл Билефельдт
11
«Часто вы будете делать это, чтобы убедиться, что ваши коммиты применяются корректно в удаленной ветке - возможно, в проекте, в который вы пытаетесь внести свой вклад, но который вы не поддерживаете. В этом случае вы выполняете свою работу в ветке, а затем перенесите свою работу на origin / master, когда вы будете готовы отправить свои исправления в основной проект. Таким образом, сопровождающему не нужно выполнять какую-либо интеграционную работу - просто быстрая перемотка вперед или чистое применение. " git-scm.com/book/en/Git-Branching-Rebasing
Andyz Smith
12
Вы делаете это звучащим, как будто это просто косметика, но на самом деле это практично: гораздо проще понять, что происходит в линейной истории, чем из сложной DAG с пересечением и слиянием линий.
Даниэль Гершкович
20
«Нет никакой пользы от этой эстетики» - я не знаю об этом. Действительно трудно понять, что происходит в вашей истории, когда ваша история похожа на печатную плату. (Предполагается несколько членов команды, каждый со своими ветвями и рабочими процессами.)
Archagon
46

В двух словах: git bisect

Линейная история позволяет вам точно определить источник ошибки.


Пример. Это наш начальный код:

def bar(arg=None):
    pass

def foo():
    bar()

Ветвь 1 выполняет некоторый рефакторинг, который argбольше не действует:

def bar():
    pass

def foo():
    bar()

В Branch 2 появилась новая функция, которую необходимо использовать arg:

def bar(arg=None):
    pass

def foo():
    bar(arg=1)

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

О дерьмо. Вот что он видит:

(master: green)
|             \_________________________
|                \                      \
(master: green)  (branch 1: green)     (branch 2: green)
|                 |                     |
|                 |                     |
(master/merge commit: green)            |
|                         ______________/
|                        /
(master/merge commit: red)
|
...days pass...
|
(master: red)

Поэтому, когда мы отправляемся, git bisectчтобы найти коммит, который нарушил сборку, он точно определит коммит слияния. Ну, это немного помогает, но по сути это указывает на пакет коммитов, а не на один. Все предки зеленые. С другой стороны, с перебазированием вы получаете линейную историю:

(master: green)
|
(master: green)
|
(all branch 1 commits: green)
|
(some branch 2 commits: green)
|
(branch 2 commit: red)
|
(remaining branch 2 commits: red)
|
...days pass...
|
(master: still red)

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

Эффект усугубляется только в больших проектах, когда сопровождающие не написали весь код, поэтому они не обязательно помнят, почему был сделан какой-либо конкретный коммит / для чего была создана каждая ветка. Таким образом, точное определение точного коммита (а затем возможность исследовать коммиты вокруг него) является большой помощью.


Тем не менее, я (в настоящее время) все еще предпочитаю слияние. Перебазирование в ветку релиза даст вам линейную историю для использования с git bisectсохранением истинной истории для повседневной работы.

Izkata
источник
2
Спасибо за это объяснение. Можете ли вы рассказать, почему вы все еще предпочитаете слияние? Я знаю, что многие люди предпочитают объединяться с перебазированием в большинстве ситуаций, но я никогда не мог понять, почему.
Филипп
@Philip Я немного колеблюсь, потому что я использую git только несколько месяцев в небольших личных проектах, а не в команде. Но при отслеживании того, что я сделал недавно, возможность видеть поток коммитов между ветвями gitk --allчрезвычайно полезна (тем более, что у меня обычно есть 3-х веточные ветки в любой момент времени, и я переключаюсь между ними каждый день в зависимости от моего настроения в время).
Изката
16
Но проблема заключается в слиянии, поэтому вам нужен коммит слияния bisect. Достаточно легко найти отдельные коммиты с blameили другим, bisectесли вы хотите копать глубже. С линейной историей, слияние выполняется вне книги, поэтому вы больше не можете сказать, что проблема была в плохом слиянии. Это выглядит как идиот, использующий аргумент, который не существует, особенно если аргумент был удален несколькими коммитами ранее. Пытаться выяснить, почему он это сделал, гораздо сложнее, когда вы уничтожаете историю.
Карл Билефельдт
1
Действительно не согласен с этим ответом. Перебазировка разрушает полезность bisect время от времени. За исключением коммитов с ребасом, коммиты из ребаз не могут быть запущены. Пример: Вы разветвились на A и сделали B, C, D, кто-то нажал E. Вы B и C и работаете, но перебазировали на E, вы B и C не работоспособны или работоспособны и не работаете. В лучшем случае вы сдаетесь, в худшем случае вы думаете, что B или C содержит проблему, когда на самом деле это D.
Лан
4
@Izkata Почему вы используете bisect? Потому что ошибки / ошибки случаются. Это унизительная реальность. Возможно, «перетаскивание мыши влево» раньше не входило в список автоматического / ручного тестирования, и теперь мы замечаем, что эта функция не работает, но мы знаем, что раньше она работала.
Лан
17

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

Итак, допустим, мы работаем над новым экраном управления учетной записью, и оказывается, что в рабочем процессе «Новая учетная запись» обнаружена ошибка. Хорошо, мы выбираем два разных пути - вы заканчиваете управление учетными записями, а я исправляю ошибку с новыми учетными записями. Поскольку мы оба имеем дело с учетными записями, мы работали с очень похожим кодом - возможно, нам даже пришлось настраивать одни и те же фрагменты кода.

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

Что ж, пришло время слиться, но ... дерьмо, что теперь происходит? Мы могли бы очень хорошо перейти от двух рабочих наборов программного обеспечения к одному, унифицированному, ужасно сломанному куску недавно ошибочного программного обеспечения, в котором ваше Управление учетными записями не работает, а Новые учетные записи повреждены, и я даже не знаю, сохранилась ли старая ошибка. ,

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

Тем временем 8 других парней пишут код, как и садисты, я сделал несколько небольших исправлений ошибок и отправил их, прежде чем узнал, что у нас конфликт слияния, и, кажется, настало время сделать перерыв, и, возможно, вы выходной или застрял на собрании или что-то еще. Может, мне просто взять отпуск? Или сменить карьеру.

И вот, чтобы избежать этого кошмара, некоторые люди стали очень бояться обязательств (что еще нового, не так ли?). Мы, естественно, рискуем избегать подобных сценариев - если только мы не думаем, что мы отстой и не собираемся все испортить, и в этом случае люди начинают действовать с опрометчивой энергией. вздох

Итак, поехали. Да, современные системы спроектированы так, чтобы облегчить эту боль, и предполагается, что она сможет легко отступать, перебазировать, обесценивать, freebase, hanglide и все такое.

Но это все больше работы, и мы просто хотим нажать кнопку на микроволновой печи и приготовить обед из 4 блюд, прежде чем мы успеем найти вилку, и все это кажется очень неэффективным - код это работа, он продуктивен, его значимое, но изящное управление слиянием просто не считается.

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

BrianH
источник
5
Удивительно, сэр!
Левша заяц
10
Это объясняет, почему люди боятся слияния, а не почему коммиты слияния считаются плохими для многих.
Джей
23
Проблема с этим ответом состоит в том, что это все еще верно для перебазирования . В конце концов, rebase все еще выполняет слияние, когда вы изменили строку кода, которая уже была изменена. Я думаю, что это проблема с тем, как вопрос был сформулирован.
deworde
2
Я отказался от этого ответа, потому что, поскольку он все еще красноречив, он не имеет смысла, imho.
Евгений
6
Похоже, что это не влияет на rebase vs merge-merge.
Andyz Smith
5

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

Слияние оставляет вашу базовую линию в исходной точке ветвления. Если вы объединяете несколько недель изменений из линии, от которой вы ответвились, у вас теперь есть много изменений из вашей точки ветвления, многие из которых будут в вашей базовой линии. Это затрудняет выявление ваших изменений в вашей ветке. Выдвижение изменений обратно к базовой линии может привести к конфликтам, не связанным с вашими изменениями. В случае конфликтов возможно продвижение противоречивых изменений. Текущие слияния требуют усилий для управления, и относительно легко потерять изменения.

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

BillThor
источник
1
поэтому все, что он делает, это понижает новые изменения до уровня, который они считают «неправильным», потому что они являются единственным изменением . Если все старые изменения включены в слияние, все изменения находятся на ровном месте, относительно того, являются ли они «правильными» сегодня.
Andyz Smith
@AndyzSmith Я бы не стал считать новые изменения неправильными . Когда вы пытаетесь определить, что меняется и что нужно подтолкнуть, это правильная вещь . После того, как вы сделали ребазинг, вы можете игнорировать старые изменения, поскольку они являются частью вашей базовой линии.
BillThor
правильно, так что если вы не уверены, каков ваш базовый уровень, т. е. то, что вы изменили, уже готовы к слиянию, является «правильной вещью», то не перебрасывайте.
Andyz Smith
И, если вы получаете от кого-то изменения, и это нарушает сборку, потому что прототип их функции не соответствует существующему прототипу, вы сразу же узнаете из-за перебазирования, что новый парень ошибается. вам не нужно догадываться, что, возможно, у нового парня есть правильный прототип, а ранее не примененные, не примененные изменения чейнсета - это те, у которых был неправильный прототип.
Andyz Smith
4

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

Реальная проблема с ветвлением и слиянием, на мой взгляд, состоит в том, что он пинает банку пословиц в будущем. Это позволяет вам говорить «Я просто буду работать в своем маленьком мире» в течение недели и решать любые возникающие проблемы позже. Но исправлять ошибки всегда быстрее / дешевле, когда они свежие. К тому времени, когда все ветви кода начнут объединяться, вы уже можете забыть некоторые нюансы того, что было сделано.

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

MrFox
источник
4
Это объясняет, почему люди боятся слияния, а не почему коммиты слияния считаются плохими для многих.
Джей
Итак, этот ствол, на который вы ссылаетесь, включает в себя ребаз? Разъясните пожалуйста.
Andyz Smith
@AndyzSmith "trunk" - это термин svn, эквивалентный "master" ветке
git
@Izkata, поэтому этот ответ не требует слияния или перебазирования?
Andyz Smith
@AndyzSmith Да, я тоже так читаю. И я не согласен с этим; после работы в команде с 7 другими разработчиками предложенным способом, я не предлагаю это. Мы должны были использовать ветви функций больше, чем мы, но большинство из них боятся слияния (как говорит BrianDHall в своем ответе).
Изката
0

Еще один важный момент: с помощью перебазирования я могу легко выбрать или отменить функцию в моей ветке релизов.

Бруно Шеппер
источник
1
Это подробно об этом?
Akavall
Конечно :-) В git flow мы регулярно создаем новые ветки релизов. Если после создания ошибка исправлена ​​в ветке разработки, мы используем ее git cherry-pick -x <commit>для применения к ветке релиза. Или мы могли бы отменить что-то для релиза, с git revert <commit>. Если <commit>это слияние, оно становится волосатым. Это является возможным, насколько я знаю, но не так легко сделать. При использовании rebase каждое «слияние» представляет собой один гигантский, но регулярный коммит, легко выбираемый через черри и обратимый.
Бруно Шеппер
0

Три вещи не были сказаны ни в одном из ответов:

  • Разница внутри ветки:

    • Различие между произвольной парой коммитов в ветви становится чрезвычайно трудным, когда есть коммиты слияния.
  • Объединение одного элемента за раз:

    • Когда вы решаете разницу между двумя ветвями, слияние обычно происходит одновременно, и любые конфликты слияний, которые вы оставляете без учета контекста того, какой конкретный коммит был ответственным за конфликт. Если вы перебазируете, перебазирование остановится в тот момент, когда возник конфликт, и позволит вам разрешить его в этом контексте.
  • Очистка перед нажатием:

    • Если вы сделали коммит с ошибкой, который позже необходимо исправить, если вы не нажали интерактивный ребаз, вы сможете комбинировать / разбивать / изменять / перемещать коммиты. Хотя вы все еще можете сделать это, если вы слились, это становится очень трудным, если вы хотите объединить / разделить / изменить / переместить границу слияния.
Catskul
источник