Когда вы используете Git rebase вместо Git merge?

Когда рекомендуется использовать Git rebase или Git merge?

Мне все еще нужно слияние после успешной перебазировки?


person Coocoo4Cocoa    schedule 29.04.2009    source источник
comment
См .: stackoverflow.com/questions / 457927 /   -  person TStamper    schedule 30.04.2009
comment
Это также может помочь в понимании более широкой картины: stackoverflow.com/a/9204499/631619   -  person Michael Durrant    schedule 15.04.2013
comment
Одна проблема с людьми, которым нравится использовать rebase, заключается в том, что это удерживает их от регулярного продвижения своего кода. Таким образом, желание чистой истории не позволяет им делиться своим кодом, что, на мой взгляд, более важно.   -  person static_rtti    schedule 06.10.2015
comment
@static_rtti: Это неправда. Вы неправильно используете поток на основе перебазирования, если он мешает вам регулярно вносить изменения.   -  person juzzlin    schedule 10.01.2016
comment
medium.com/datadriveninvestor/.   -  person Shiwangini    schedule 02.08.2020
comment
Моя эвристика: попробуйте перебазировать, если он растворяется в аду разрешения конфликтов, сдавайтесь, слейте мастер в свою ветку и двигайтесь дальше. - подробнее в моем сообщении timwise.co.uk/2019/10 / 14 / merge-vs-rebase (в контексте коммерческой разработки)   -  person Tim Abell    schedule 02.09.2020


Ответы (17)


Укороченная версия

  • Слияние берет все изменения в одной ветке и объединяет их в другую ветку за один коммит.
  • Rebase говорит, что я хочу, чтобы точка, в которой я разветвился, переместилась в новую отправную точку

Итак, когда вы используете любой из них?

Объединить

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

Rebase

  • Второй сценарий: вы начали разработку, а затем другой разработчик внес изменения, не связанные с этим. Вероятно, вы захотите вытащить, а затем переустановить, чтобы основывать свои изменения на текущей версии из репозитория.
person Rob Di Marco    schedule 29.04.2009
comment
@Rob упомянул о сохранении промежуточных коммитов при слиянии. Я считаю, что по умолчанию слияние ветки B (функциональная ветка, над которой вы работали) в ветвь M (основная ветвь) создаст одну фиксацию в M для каждой фиксации, которая была сделана в B, поскольку они разошлись. Но если вы выполните слияние с помощью опции --squash, все коммиты, сделанные в ветке B, будут объединены вместе и объединены как одна фиксация в ветке M, сохраняя журнал в вашей основной ветке красивым и чистым. Сжатие - это, вероятно, то, что вам нужно, если у вас есть множество разработчиков, работающих независимо и снова сливающихся в мастера. - person spaaarky21; 25.01.2013
comment
@Rob, в вашем объяснении по перебазированию может быть немного больше объяснений для людей, у которых нет концепции перебазирования. Spaaarky21 просто добавил немного тонкости ко всему этому с раздавливанием. - person ATL_DEV; 29.03.2013
comment
Я считаю, что предположение @ spaaarky21 о слиянии неверно. Если вы объедините ветвь B с мастером M, на M будет только одна фиксация (даже если B имеет несколько фиксаций), независимо от того, используете ли вы слияние с простым или --squash. Что сделает --squash, так это исключит ссылку на B как на родителя. Хорошая визуализация здесь: syntevo.com/smartgithg/howtos.html? page = workflows.merge - person jpeskin; 26.05.2013
comment
@jpeskin Я не это вижу. Я просто провел быстрый тест, чтобы убедиться. Создайте каталог с текстовым файлом, init новым репо, add файлом и commit. Оформить новую ветку функции (checkout -b feature.) Измените текстовый файл, сделайте фиксацию и повторите так, чтобы в ветке функции было две новых фиксации. Потом checkout master и merge feature. В log я вижу свою первоначальную фиксацию на мастере, а затем две, которые были объединены из функции. Если вы merge --squash feature, функция объединяется в мастер, но не фиксируется, поэтому единственной новой фиксацией в мастере будет то, что вы сделаете сами. - person spaaarky21; 28.05.2013
comment
@ spaaarky21 Похоже, мы оба наполовину правы. Когда возможно слияние с перемоткой вперед (как в вашем примере), git по умолчанию будет включать все коммиты в функциональную ветку B (или, как вы предлагаете, вы можете использовать --squash для объединения в одну фиксацию). Но в случае, когда есть две расходящиеся ветви M и B, которые вы объединяете, git не будет включать все отдельные коммиты из ветки B, если они объединены с M (независимо от того, используете ли вы --squash). - person jpeskin; 28.05.2013
comment
Почему это (вы не заботитесь о сохранении всех промежуточных коммитов) все еще в этом ответе? Это не имело смысла в 2009 году и не имеет смысла сейчас. Кроме того, вы наверняка захотите перебазировать только в том случае, если другой разработчик внесет связанные изменения, которые вам нужны - если они внесли несвязанные изменения, ваша ветка функций должна легко объединиться без конфликтов, и ваша история будет сохранена. - person Mark Booth; 27.04.2016
comment
если вы начали разработку, а затем другой разработчик внес несвязанное изменение - Вы имеете в виду, что другой разработчик внес изменения и зафиксировал код в основной ветке и имел этот код в ветке, которая была создана для добавления одной функции? - person N Sharma; 02.08.2017
comment
@jpeskin Я использую v2.25.1 и запутался. Вот что я сделал. Я создал ветку foo, добавил одну фиксацию в master и несколько коммитов в foo, затем git merge foo. Если я просматриваю журнал, он показывает все коммиты от foo. Это противоречит тому, как вы сказали, что git не будет включать все отдельные коммиты из ветки B, если они будут объединены с M ... - person Fahmi; 11.03.2020
comment
РАЗЪЯСНЕНИЕ. Если вы разработчик, у которого раньше была мусорная история, то вы объединяете и выжимаете. Если вам важна история git, вы переустанавливаете master в свою ветку. И вы вручную исправляете каждый из ваших собственных коммитов, у которых есть конфликты. И вы знаете несколько git rebase --continue или --abort. Обратите внимание, что коммиты слияния со временем увеличивают размер репо. ПОЖАЛУЙСТА, проголосуйте против тех, кто говорит никогда не переустанавливать мастер и не упоминать ложное золотое правило. - person Thierry Vilmart; 23.03.2021

Это просто. С помощью rebase вы говорите использовать другую ветку в качестве новой базы для вашей работы.

Если у вас есть, например, ветка master, вы создаете ветвь для реализации новой функции и говорите, что называете ее cool-feature, конечно, основная ветвь является основой для вашей новой функции.

Теперь, в определенный момент, вы хотите добавить новую функцию, реализованную в ветке master. Вы можете просто переключиться на master и объединить ветку cool-feature:

$ git checkout master
$ git merge cool-feature

Но таким образом добавляется новый фиктивный коммит. Если вы хотите избежать спагетти-истории, вы можете переустановить:

$ git checkout cool-feature
$ git rebase master

А затем слить в master:

$ git checkout master
$ git merge cool-feature

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

person Aldo 'xoen' Giambelluca    schedule 05.02.2012
comment
but this way a new dummy commit is added, if you want to avoid spaghetti-history - как там плохо? - person アレックス; 19.05.2014
comment
Привет @Alex, это зависит от личных предпочтений и протоколов веток для вашего проекта. Если вы хотите, чтобы эти коммиты были в вашей истории для дальнейшего использования (например, мы слили master в выпуск перед развертыванием?), Сделайте слияние, в противном случае, если вы предпочитаете более чистую историю, используйте rebase. - person Aldo 'xoen' Giambelluca; 20.05.2014
comment
Также очень полезен флаг слияния --no-ff. - person Aldo 'xoen' Giambelluca; 22.05.2014
comment
Да @ s4nk, rebase может переписать историю, и это плохо, когда другие разработчики работают над веткой. Вот почему в 99% случаев вы перемещаете коммиты в своей личной ветке поверх коммитов в основной ветке. - person Aldo 'xoen' Giambelluca; 09.03.2015
comment
@ ア レ ッ ク ス, поскольку пользователь Sean Schofield добавляет это в комментарий: Rebase также хорош, потому что, как только вы в конечном итоге объедините ur-материал обратно в master (что тривиально, как уже описано), вы увидите, что он находится в верхней части истории ваших коммитов. В более крупных проектах, где функции могут быть написаны, но объединены через несколько недель, вы не хотите просто объединять их в мастер, потому что они вставляются в мастер еще в истории. Лично мне нравится иметь возможность вести журнал git и видеть эту недавнюю функцию прямо вверху. Обратите внимание, что даты фиксации сохранены - перебазирование не меняет эту информацию. - person Adrien Be; 01.03.2016
comment
@AdrienBe, это очень хороший аргумент и одна из причин, почему мне нравится делать перебазирование - история выглядит намного аккуратнее;) - person Aldo 'xoen' Giambelluca; 01.03.2016
comment
Я думаю, здесь стоит повторить - помните, что все эти термины (merge, rebase, fast-forward и т.д.) относятся к конкретным манипуляциям с направленным ациклическим графом. Их становится легче рассуждать, имея в виду эту ментальную модель. - person Roy Tinker; 27.01.2017
comment
@RoyTinker - хорошее замечание. По некоторым странным причинам, хотя я знаю, что репозитории - это деревья (как вы указали), мне гораздо проще думать о наборах изменений :) - person Aldo 'xoen' Giambelluca; 12.05.2017
comment
@Aldo Нет ничего чистого или аккуратного в перебазированной истории. Это вообще грязно и ИМХО ужасно, потому что вы понятия не имеете, что на самом деле произошло. Самая чистая история Git - это то, что произошло на самом деле. :) - person Marnen Laibow-Koser; 14.05.2018
comment
@ ア レ ッ ク ス Это очень плохо и может принести долгую историю коммитов в ветку. - person Arefe; 17.07.2020
comment
Ваш ответ очень полезен, но я считаю, что очень необходимо сделать его более ясным, что вы говорите о перебазировании в локальном репозитории (а не в общедоступной ветке). Кроме того, вместо слияния с ускоренной перемоткой вы также можете сделать еще одно ребазирование, перенастроив главную ветку из cool-feature. - person aderchox; 18.07.2020

TL;DR

Если у вас есть сомнения, используйте слияние.

Короткий ответ

Единственные различия между перебазированием и слиянием:

  • Результирующая древовидная структура истории (обычно заметная только при просмотре графика фиксации) отличается (у одного будут ветви, у другого - нет).
  • Слияние обычно создает дополнительную фиксацию (например, узел в дереве).
  • Слияние и перебазирование будут обрабатывать конфликты по-разному. Rebase будет представлять конфликты по одному за раз, а слияние представит их все сразу.

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

Длинный ответ

При выборе операции следует учитывать несколько факторов.

Делится ли ветка, в которой вы получаете изменения, с другими разработчиками, не входящими в вашу команду (например, с открытым исходным кодом, общедоступная)?

Если да, не переустанавливайте. Rebase уничтожает ветку, и у этих разработчиков будут поврежденные / несогласованные репозитории, если они не используют git pull --rebase. Это хороший способ быстро расстроить других разработчиков.

Насколько квалифицирована ваша команда разработчиков?

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

Я работал в командах, где все разработчики были из тех времен, когда компании могли позволить себе выделенный персонал для разветвления и слияния. Эти разработчики мало что знают о Git и не хотят много знать. В этих командах я бы ни по какой причине не рискнул рекомендовать ребазинг.

Представляет ли сама ветка полезную информацию

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

Я также работал с командами, которые использовали модель «ветвь на разработчика» (все мы были в ней). В этом случае сама ветка не несет никакой дополнительной информации (у коммита уже есть автор). Перебазирование не повредит.

Возможно, вы захотите отменить слияние по какой-либо причине?

Отмена (как и отмена) перебазирования значительно труднее и / или невозможно (если перебазирование имело конфликты) по сравнению с откатом слияния. Если вы думаете, что есть шанс вернуться, используйте слияние.

Вы работаете в команде? Если да, готовы ли вы применить в этой ветке подход «все или ничего»?

Операции Rebase необходимо подтянуть с соответствующим git pull --rebase. Если вы работаете в одиночку, вы можете вспомнить, какие из них следует использовать в подходящее время. Если вы работаете в команде, это будет очень сложно координировать. Вот почему большинство рабочих процессов перебазирования рекомендуют использовать перебазирование для всех слияний (и git pull --rebase для всех вытягиваний).

Распространенные мифы

Слияние уничтожает историю (сквош фиксирует)

Предполагая, что у вас есть следующее слияние:

    B -- C
   /      \
  A--------D

Некоторые люди скажут, что слияние «уничтожает» историю коммитов, потому что если вы посмотрите журнал только главной ветки (A - D), вы пропустите важные сообщения фиксации, содержащиеся в B и C.

Если бы это было правдой, у нас не было бы подобные вопросы. По сути, вы увидите B и C, если явно не попросите их не видеть (с помощью --first-parent). Это очень легко попробовать на себе.

Rebase обеспечивает более безопасное / простое слияние

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

Rebase круче / сексуальнее / профессиональнее

Если вам нравится использовать псевдоним от rm до rm -rf, чтобы «сэкономить время», то, возможно, перебазирование подойдет вам.

Мои два цента

Я всегда думаю, что когда-нибудь я наткнусь на сценарий, в котором Git rebase - отличный инструмент, решающий проблему. Как и я, думаю, я столкнусь со сценарием, в котором Git reflog - отличный инструмент, решающий мою проблему. Я работаю с Git более пяти лет. Этого не произошло.

Беспорядочные истории никогда не были для меня проблемой. Я никогда не читаю историю своих коммитов, как увлекательный роман. В большинстве случаев, когда мне нужна история, я все равно буду использовать Git blame или Git bisect. В этом случае наличие фиксации слияния действительно полезно для меня, потому что, если слияние вызвало проблему, для меня это значимая информация.

Обновление (4/2017)

Я считаю своим долгом упомянуть, что я лично смягчился в отношении использования rebase, хотя мой общий совет все еще в силе. В последнее время я много взаимодействую с проектом Angular 2 Material. Они использовали rebase, чтобы сохранить очень чистую историю коммитов. Это позволило мне очень легко увидеть, какая фиксация исправила данный дефект и была ли эта фиксация включена в выпуск. Это отличный пример правильного использования rebase.

person Pace    schedule 13.04.2016
comment
Мне больше всего нравится этот ответ. Но: Rebase не делает чистой истории. Это делает более линейную историю, но это совсем не то же самое, так как кто знает сейчас, много грязи скрывает каждый коммит? Самая чистая и ясная история Git - это та, которая поддерживает целостность ветвей и фиксации. - person Marnen Laibow-Koser; 14.05.2018
comment
Стоит упомянуть, что git недавно изменил свое git pull поведение, включив по умолчанию флаг --rebase. Это означает, что выполнение перебазирования веток, используемых несколькими разработчиками, немного менее опасно. Человек, вносящий ваши изменения, может быть удивлен тем, что во время такой операции необходимо разрешить некоторые конфликты, но катастрофы не будет. - person pkubik; 31.07.2020
comment
Это должно быть наверху, пожалуйста. - person igauravsehrawat; 09.05.2021

В дополнение к моему собственному ответу упоминается от TSamper,

  • довольно часто перед слиянием рекомендуется выполнить перебазирование, потому что идея состоит в том, что вы интегрируете в свою ветку Y работу той ветки, B на которой вы будете выполнять слияние.
    Но опять же, перед слиянием вы разрешаете любой конфликт в своей ветке (например, «перебазировать», как в «воспроизвести мою работу в моей ветке, начиная с недавней точки ветки B). .
    Если все сделано правильно, последующее слияние из вашей ветки в ветку B может быть перемотано вперед.

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


точка слияния после ребаза?

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

Другой сценарий (описан в Git Ready для instance), заключается в том, чтобы перенести вашу работу прямо в B через перебазирование (которое сохраняет все ваши хорошие коммиты или даже дает вам возможность изменить их порядок с помощью интерактивного перебазирования).
В этом случае (когда вы перебазируете, находясь в ветке B), вы правы: дальнейшее слияние не требуется:

Дерево Git по умолчанию, если мы не выполняли слияние и не перебазировали

rebase1

получаем путем перебазирования:

rebase3

Этот второй сценарий посвящен тому, как мне вернуть новую функцию в мастер.

Я хочу сказать, описывая первый сценарий перебазирования, чтобы напомнить всем, что перебазирование также можно использовать как предварительный шаг к этому (то есть «вернуть новую функцию в мастер»).
Вы можете использовать rebase, чтобы сначала перенести master "в" ветку новой функции: rebase будет воспроизводить коммиты новой функции из HEAD master, но все еще в ветке новой функции, эффективно перемещая начальную точку вашей ветки из старый мастер фиксирует HEAD-master.
Это позволяет разрешать любые конфликты в вашей ветке (то есть изолированно, позволяя мастеру продолжать развиваться параллельно, если этап разрешения конфликта занимает слишком много времени).
Затем вы можете переключиться на master и объединить new-feature (или переустановить new-feature на master, если вы хотите сохранить коммиты, сделанные в вашей ветке new-feature).

So:

  • "rebase vs. merge" можно рассматривать как два способа импортировать работу, скажем, на master.
  • Но «перебазирование, а затем слияние» может быть допустимым рабочим процессом, чтобы сначала разрешить конфликт изолированно, а затем вернуть свою работу.
person VonC    schedule 29.04.2009
comment
Судя по вашему ответу, почти кажется, что все равно нужно слияние даже после перебазирования. Если у меня есть основная ветка и ветка с основной функцией. Я создам функцию в master-feature, а затем хочу, чтобы эта функция была доступна в master. Если я перебазирую, все скажут, что N коммитов, которые я сделал в мастер-функции, и покажет историю в мастере. Какой смысл слиться после ребаза? - person Coocoo4Cocoa; 30.04.2009
comment
слияние после перебазирования - это тривиальная перемотка вперед без разрешения конфликтов. - person obecalp; 30.04.2009
comment
@obelcap: В самом деле, это своего рода идея: вы берете все проблемы-конфликты в своей среде (перебазируете мастер в ветке новой функции), а затем вместе с мастером объединяете новую функцию: 1 пикосекунда (перемотка вперед), если у мастера не было эволюции - person VonC; 01.05.2009
comment
Rebase также хорош, потому что, как только вы в конечном итоге объедините свои вещи обратно в master (что тривиально, как уже описано), вы получите его в верхней части вашей истории коммитов. В более крупных проектах, где функции могут быть написаны, но объединены через несколько недель, вы не хотите просто объединять их в мастер, потому что они вставляются в мастер еще в истории. Лично мне нравится иметь возможность вести журнал git и видеть эту недавнюю функцию прямо вверху. Обратите внимание, что даты фиксации сохранены - перебазирование не меняет эту информацию. - person Sean Schofield; 03.09.2009
comment
Итак, мысленно перебазирование говорит: теперь я вношу это изменение в README после новой функции, а не параллельно, а затем тривиально объединяю эту новую функцию с моим изменением? - person Joe; 15.08.2011
comment
@Joe: мысленно вы говорите воспроизвести любые мои изменения (сделанные изолированно в моей частной ветке) поверх этой другой ветки, но оставьте меня в моей частной ветке после того, как перебазирование будет выполнено. Это хорошая возможность очистить местную историю, избежать фиксации контрольных точек, сломанной пополам и неверных результатов поиска виновных. См. Рабочий процесс Git: sandofsky.com/blog/git-workflow.html - person VonC; 15.08.2011
comment
@VonC: Хорошо, спасибо. Может ли когда-нибудь быть случай, когда два человека переустанавливают изменения друг друга так, что теперь у вас есть два репозитория с двумя разными порядками коммитов? Стало бы это сделать их несовместимыми? - person Joe; 15.08.2011
comment
@Joe: да, но это плохая практика - переустанавливать то, что уже было отправлено или что было получено. Так что на практике этого быть не должно. - person VonC; 15.08.2011
comment
@VonC: подумал, что может быть так. Что меня беспокоит, так это то, что пометить что-то как плохую практику никогда не бывает достаточно сильным, чтобы удержать людей от таких деструктивных поступков. - person Joe; 22.08.2011
comment
@Joe: конечно, но это редко бывает деструктивным (у вас есть рефлог, чтобы отслеживать коммиты без ссылок), b / раздел ВОССТАНОВЛЕНИЕ ИЗ UPSTREAM REBASE на git rebase странице руководства подробно описывается возможное решение в этом редком случае. - person VonC; 22.08.2011
comment
Последняя строка сделала все: но перебазирование, а затем слияние может быть допустимым рабочим процессом, чтобы сначала разрешить конфликт изолированно, а затем вернуть свою работу. - person Yugal Jindle; 22.07.2012
comment
@VonC Это позволяет вам разрешать любые конфликты в вашей ветке (то есть изолированно, позволяя мастеру продолжать развиваться параллельно, если ваша стадия разрешения конфликта занимает слишком много времени). То же самое верно и с a хотя слить, не так ли? - person Scott Coates; 24.01.2013
comment
@scoarescoare - да, но я предпочитаю воспроизводить свою работу (перебазировать) поверх целевой ветки (например, master), а не объединять мастер и, возможно, вносить некоторые исправления в мою ветку функций, прежде чем объединять ее обратно в master: история чище. - person VonC; 24.01.2013
comment
@VonC Я согласен с тем, что перебазирование дает более чистую историю, но мне нужна помощь в понимании того, как ребазинг позволяет разрешать конфликты в функциональной ветке лучше / иначе, чем просто слияние? Я все еще учусь, спасибо. - person Scott Coates; 25.01.2013
comment
@scoarescoare - ключ к тому, чтобы увидеть, насколько ваши локальные изменения совместимы поверх последней восходящей ветки. Если один из ваших коммитов вызывает конфликт, вы сразу это увидите. Слияние вводит только одну (объединенную) фиксацию, которая может вызвать множество конфликтов, без простого способа увидеть, какая из ваших локальных коммитов действительно добавила указанный конфликт. Таким образом, в дополнение к более чистой истории, вы получаете более точное представление об изменениях, которые вы вносите, фиксируете путем фиксации (воспроизводятся путем перебазирования), в отличие от всех изменений. введен восходящей веткой (сброшен в одно слияние). - person VonC; 25.01.2013
comment
Я нашел это объяснение удивительным, поскольку, если вы только что убедились (с помощью перезагрузки), что ваш git pull будет быстрой перемоткой вперед, в конце дня git pull - это просто еще один способ поместить хэш в текстовый файл - сброс - эту задачу вы можете выполнить с помощью различных команд (например, git rebase, git reset, даже перенаправления оболочки), все из которых строго эквивалентны. Так что в некотором смысле нет такой вещи, как rebase, then pull, а только rebase и reset. Думаю, многие люди часто используют pull и поэтому находят простой способ понять это, и это нормально! - person Croad Langshan; 01.03.2015

Многие ответы здесь говорят, что слияние превращает все ваши коммиты в один, и поэтому предлагается использовать rebase для сохранения ваших коммитов. Это неверно. И это плохая идея, если вы уже отправили свои коммиты.

Слияние не стирает ваши коммиты. Слияние сохраняет историю! (просто посмотрите на gitk) Rebase переписывает историю, что является плохим моментом после того, как вы ее нажали.

Используйте слияние, а не перебазирование всякий раз, когда вы уже нажали.

Вот Линус (автор Git) возьми на себя (теперь размещен в моем собственном блоге, как восстановлено Wayback Machine). Это действительно хорошее чтение.

Или вы можете прочитать мою собственную версию той же идеи ниже.

Перебазирование ветки на master:

  • дает неверное представление о том, как были созданы коммиты
  • загрязняет мастер кучей промежуточных коммитов, которые, возможно, не были хорошо протестированы
  • может фактически вводить разрывы сборки для этих промежуточных коммитов из-за изменений, которые были внесены в мастеринг между тем, когда была создана исходная ветка темы, и когда она была перебазирована.
  • затрудняет поиск хороших мест в мастерской для оформления заказа.
  • Заставляет метки времени коммитов не совпадать с их хронологическим порядком в дереве. Таким образом, вы увидите, что фиксация A предшествует фиксации B в мастере, но фиксация B была создана первой. (Какие?!)
  • Создает больше конфликтов, потому что каждый из отдельных коммитов в тематической ветке может включать конфликты слияния, которые необходимо разрешать индивидуально (более подробно рассказывается о том, что происходило в каждом коммите).
  • это переписывание истории. Если перебазируемая ветка была куда-то отправлена ​​(поделена с кем-либо, кроме вас), вы облажались со всеми, у кого есть эта ветка, так как вы переписали историю.

Напротив, объединение ветки темы в мастер:

  • сохраняет историю создания тематических веток, включая любые слияния из главной в тематическую ветку, чтобы поддерживать ее в актуальном состоянии. Вы действительно получаете точное представление о том, с каким кодом работал разработчик при создании.
  • master - это ветка, состоящая в основном из слияний, и каждая из этих коммитов слияния обычно является «хорошими моментами» в истории, которые можно безопасно проверить, потому что именно там ветка темы была готова к интеграции.
  • все отдельные коммиты тематической ветки сохраняются, в том числе тот факт, что они были в тематической ветке, поэтому изоляция этих изменений естественна, и вы можете детализировать их там, где это необходимо.
  • Конфликты слияния должны быть разрешены только один раз (в момент слияния), поэтому промежуточные изменения фиксации, сделанные в ветке темы, не должны разрешаться независимо.
  • можно делать несколько раз плавно. Если вы периодически интегрируете свою тематическую ветку в master, люди могут продолжать развивать тематическую ветку, и она может продолжать объединяться независимо.
person Andrew Arnott    schedule 03.02.2014
comment
Кроме того, в git merge есть опция --no-ff (без перемотки вперед), которая позволяет очень легко отменить все изменения, внесенные определенным слиянием. - person Bitcoin Cash - ADA enthusiast; 09.09.2014
comment
Просто сделайте это более ясным: вы ссылаетесь на ситуацию «всякий раз, когда вы уже подтолкнули» - это должно быть выделено жирным шрифтом. Между прочим, ссылка на пост Линуса отличная, - поясняет его. - person honzajde; 14.02.2015
comment
@ user271996 Второе предложение в моем ответе - это именно то место, и оно уже выделено жирным шрифтом. Чего еще вы просили? - person Andrew Arnott; 15.02.2015
comment
Извините за не очень четкий комментарий :). Пока я читал, не было очевидно, ссылаетесь ли вы на исходный более широкий вопрос или на ваше конкретное его сужение. Это все, на что я хотел указать. Хотя мое мнение может быть субъективным. Это все - person honzajde; 15.02.2015
comment
но разве не рекомендуется обновлять ветку темы из главной в ветку темы, прежде чем объединить ветку темы в главную через PR (чтобы разрешить конфликты в своей ветке, а не в главной)? Мы делаем это так, поэтому большинство тематических веток имеют в качестве последней фиксации мастер ветки слияния в тему -... но здесь это указано как функция перебазирования, и никто не упоминает ее для слияния ...? - person ProblemsOfSumit; 07.01.2016
comment
@Sumit: хотя вы, безусловно, можете это сделать, я бы не назвал это лучшей практикой, как тем, чего следует избегать. Большинство тематических веток должны иметь возможность без конфликтов сливаться с целевыми ветками (иначе вы можете делать что-то еще не так в своем рабочем процессе). Так что дополнительное слияние фиксации помимо слияния PR - это просто шум. Это также не гарантирует, что к моменту слияния PR не будет больше конфликтов из-за большего количества изменений в целевой ветви во время проверки вашего PR. Поэтому я отправляю слияние от мастера к теме только в том случае, если конфликты действительно обнаруживаются. - person Andrew Arnott; 08.01.2016
comment
@AndrewArnott Большинство тематических веток должны иметь возможность без конфликтов сливаться с целевыми ветками. Как это должно быть возможно, когда 20 разработчиков работают над 30 ветвями? Пока вы работаете над своим, будут слияния - так что, конечно, вам нужно обновить ветку темы от цели, прежде чем создавать PR ... нет? - person ProblemsOfSumit; 11.01.2016
comment
Не обычно, @Sumit. Git может легко объединить любое направление, даже если изменения были внесены в одну или обе ветки. Конфликты могут возникнуть только при изменении одних и тех же строк кода (или очень близких) в двух ветвях. Если это часто случается в какой-либо команде, команда должна переосмыслить то, как они распределяют работу, поскольку разрешение конфликтов - это налог и замедляет их. - person Andrew Arnott; 15.01.2016
comment
@AndrewArnott Если мастер изменился, я думаю, что дополнительное слияние, которое рекомендует Sumit, - хорошая идея. Вы правы, что код в любом случае будет одинаковым, но при этом не учитывается тот факт, что при изменении мастера я должен проверить, что моя функциональная ветка все еще работает. Самый простой способ сделать это - объединить мастер в ветку функции, протестировать и внести все необходимые изменения, пока ветка функции не станет правильной, а затем объединить ее с мастером. Конечно, это может сгенерировать дополнительную фиксацию слияния, но что с того? - person Marnen Laibow-Koser; 07.06.2018
comment
В общем, вам не следует бояться перезаписи удаленной удаленной ветки. Только не переписывайте историю давно работающих веток вашей команды, таких как master и dev. Нет ничего плохого в том, чтобы перенастроить какую-то случайную ветку функций. Или выполните локальную перестановку, а затем нажмите на новую ветку функций, которую вы затем используете для создания PR. - person pkamb; 24.04.2019
comment
Не могли бы вы объяснить некоторые моменты дальше? Каким образом перебазирование моей собственной ветки на мастере загрязняет мастер какой-либо фиксацией? - person Nico Haase; 20.05.2020
comment
@NicoHaase Перебазирование множественных коммитов на master было бы целесообразно, ИМО, только если вы проверили каждую фиксацию как хорошее, проверенное изменение. В противном случае вы загрязните master кучей коммитов, которые сами по себе не подходят. В этом случае предпочтительнее было бы использовать фиксацию слияния, поскольку она записывает через историю git, что ветка темы существовала, и предполагает, что она, вероятно, была полностью проверена только в ее окончательной форме (фиксация слияния). Если у вас есть только одна фиксация, которую вы переустанавливаете на вершину мастера, я не вижу в этом проблемы, особенно если вы делаете это до нажатия. - person Andrew Arnott; 22.05.2020
comment
Я не думаю, что кто-то говорил о ребасинге при слиянии INTO master. Таким образом, этот комментарий полностью отключен, и люди не поймут, что вы переустанавливаете ветки функций на master vs master на других ветках. - person rob2d; 16.04.2021

TL; DR: Это зависит от того, что наиболее важно - аккуратная история или точное представление последовательности развития

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

Если истинное представление последовательности является наиболее важным, вы должны выполнить слияние без перестановки.

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

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

Учитывая это, зачем вам переустанавливать? Просто чтобы история развития была ясна. Предположим, вы работаете над функцией X, и когда закончите, вы объединяете свои изменения. Теперь у места назначения будет одна фиксация, которая будет говорить что-то вроде «Добавленная функция X». Теперь, вместо слияния, если вы перебазируете, а затем слились, история разработки места назначения будет содержать все отдельные коммиты в единой логической последовательности. Это значительно упрощает последующий просмотр изменений. Представьте, как сложно было бы просматривать историю разработки, если бы 50 разработчиков постоянно объединяли различные функции.

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

Другой раз, когда вы можете захотеть перебазировать, - это когда вы хотите избавиться от коммитов из своей ветки перед тем, как продвигать вверх по течению. Например: коммиты, которые вводят некоторый код отладки на ранней стадии, а другие коммиты далее, очищают этот код. Единственный способ сделать это - выполнить интерактивную перебазировку: git rebase -i <branch/commit/tag>

ОБНОВЛЕНИЕ: вы также хотите использовать rebase, когда используете Git для взаимодействия с системой управления версиями, которая не поддерживает нелинейную историю (Subversion, например). При использовании моста git-svn очень важно, чтобы изменения, которые вы объединяете обратно в Subversion, представляли собой последовательный список изменений поверх самых последних изменений в стволе. Есть только два способа сделать это: (1) вручную воссоздать изменения и (2) использовать команду rebase, что намного быстрее.

ОБНОВЛЕНИЕ 2: Еще один способ думать о перебазировании - это то, что он позволяет своего рода сопоставление вашего стиля разработки со стилем, принятым в репозитории, который вы фиксируете. Допустим, вам нравится совершать коммит небольшими, крошечными порциями. У вас есть одна фиксация для исправления опечатки, одна фиксация для избавления от неиспользуемого кода и так далее. К тому времени, как вы закончите то, что вам нужно, у вас будет длинная серия коммитов. Теперь предположим, что репозиторий, который вы собираетесь использовать, поощряет большие коммиты, поэтому для работы, которую вы выполняете, можно ожидать одного или, может быть, двух коммитов. Как взять строку коммитов и сжать их до ожидаемого? Вы бы использовали интерактивную перестановку и разделили бы ваши крошечные коммиты на меньшее количество более крупных кусков. То же самое верно, если было необходимо обратное - если ваш стиль был несколькими большими коммитами, но репозиторий требовал длинных строк маленьких коммитов. Для этого вы также должны использовать rebase. Если бы вы вместо этого объединились, вы теперь перенесли свой стиль фиксации в основной репозиторий. Если разработчиков много, вы можете себе представить, как сложно будет проследить историю с несколькими разными стилями коммитов через некоторое время.

ОБНОВЛЕНИЕ 3: Does one still need to merge after a successful rebase? Да, это так. Причина в том, что перебазирование по существу включает в себя «смещение» коммитов. Как я уже сказал выше, эти коммиты рассчитываются, но если у вас было 14 коммитов с точки разветвления, то, если с вашим перебазированием ничего не пойдет не так, у вас будет 14 коммитов вперед (от точки, на которую вы перебазируете) после перебазирование выполнено. Перед перебазированием у вас была ветка. После у вас будет ветка такой же длины. Перед публикацией изменений вам все равно необходимо выполнить слияние. Другими словами, выполняйте перебазирование столько раз, сколько захотите (опять же, только если вы не отправили свои изменения вверх по течению). Слияние только после перебазирования.

person Carl    schedule 05.02.2012
comment
Слияние с мастером может привести к быстрой перемотке вперед. В функциональной ветке могут быть некоторые коммиты, которые содержат незначительные ошибки или даже не компилируются. Если вы проводите только модульное тестирование в функциональной ветке, некоторые ошибки в интеграции я проскользну. Перед объединением с мастером требуются интеграционные тесты, которые могут показать некоторые ошибки. Если они будут исправлены, функция может быть интегрирована. Поскольку вы не хотите фиксировать ошибочный код для мастеринга, кажется, что требуется перебазирование, чтобы предотвратить ускоренную перемотку всех коммитов. - person mbx; 15.07.2012
comment
@mbx git merge поддерживает параметр --no-ff, который заставляет его выполнять фиксацию слияния. - person Gavin S. Yancey; 09.06.2017

Хотя слияние определенно является самым простым и распространенным способом интеграции изменений, это не единственный способ: Rebase - альтернативный способ интеграции.

Лучшее понимание слияния

Когда Git выполняет слияние, он ищет три коммита:

  • (1) Фиксация общего предка. Если вы проследите историю двух веток в проекте, у них всегда есть хотя бы одна общая фиксация: в этот момент обе ветки имели одинаковое содержимое, а затем развивались по-разному.
  • (2) + (3) Конечные точки каждой ветви. Цель интеграции - объединить текущие состояния двух ветвей. Поэтому их соответствующие последние изменения представляют особый интерес. Объединение этих трех коммитов приведет к интеграции, к которой мы стремимся.

Быстрая перемотка вперед или фиксация слияния

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

Введите здесь описание изображения

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

Введите здесь описание изображения

Однако во многих случаях обе ветви развивались индивидуально.

Введите здесь описание изображения

Чтобы выполнить интеграцию, Git должен будет создать новую фиксацию, которая будет содержать различия между ними - фиксацию слияния.

Введите описание изображения здесь

Человеческие коммиты и слияния

Обычно коммит тщательно создается человеком. Это значимая единица, которая обертывает только связанные изменения и аннотирует их комментариями.

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

Интеграция с Rebase

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

Введите здесь описание изображения

Давайте шаг за шагом рассмотрим операцию перебазирования. Сценарий тот же, что и в предыдущих примерах: мы хотим интегрировать изменения из ветки B в ветку A, но теперь с помощью rebase.

Введите описание изображения здесь

Сделаем это в три шага

  1. git rebase branch-A // Synchronises the history with branch-A
  2. git checkout branch-A // Change the current branch to branch-A
  3. git merge branch-B // Merge/take the changes from branch-B to branch-A

Во-первых, Git «отменит» все коммиты на ветке A, которые произошли после того, как строки начали разветвляться (после коммита общего предка). Однако, конечно, он не будет их отбрасывать: вместо этого вы можете думать об этих коммитах как о «временно сохраненных».

Введите описание изображения здесь

Затем он применяет коммиты из ветки B, которые мы хотим интегрировать. На данный момент обе ветви выглядят одинаково.

Введите описание изображения здесь

На последнем этапе новые коммиты в ветке A теперь применяются повторно, но в новой позиции, поверх интегрированных коммитов из ветки B (они основаны заново).

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

Введите описание изображения здесь

Наконец, вы получаете чистую ветку branch-A без нежелательных и автоматически сгенерированных коммитов.

Примечание. Взято из замечательного сообщение от git-tower. Недостатки rebase также хорошо прочитаны в том же посте.

person Abdullah Khan    schedule 12.10.2017

Я только что своими словами создал FAQ для своей команды, который отвечает на этот вопрос. Разрешите поделиться:

Что такое merge?

Коммит, который объединяет все изменения другой ветки в текущую.

Что такое rebase?

Повторная фиксация всех коммитов текущей ветки на другую базовую фиксацию.

Каковы основные различия между merge и rebase?

  1. merge выполняет только одну новую фиксацию. rebase обычно выполняет несколько (количество коммитов в текущей ветке).
  2. merge создает новую сгенерированную фиксацию (так называемую фиксацию слияния). rebase перемещает только существующие коммиты.

В каких ситуациях мы должны использовать merge?

Используйте merge всякий раз, когда вы хотите добавить изменения разветвленной ветви назад в базовую ветвь.

Как правило, вы делаете это, нажимая кнопку «Объединить» в запросах на извлечение / объединение, например на GitHub.

В каких ситуациях мы должны использовать rebase?

Используйте rebase всякий раз, когда вы хотите добавить изменения базовой ветви обратно в разветвленную ветвь.

Обычно это делается в feature ветвях всякий раз, когда происходит изменение в main ветке.

Почему бы не использовать merge для объединения изменений из базовой ветки в функциональную ветку?

  1. История git будет включать множество ненужных коммитов слияния. Если в функциональной ветке требовалось несколько слияний, то эта функциональная ветка может даже содержать больше слияний, чем фактических!

  2. Это создает цикл, который разрушает ментальную модель, созданную Git, что вызывает проблемы при любой визуализации истории Git.

    Представьте себе реку (например, Нил). Вода течет в одном направлении (направление времени в истории Git). Время от времени представьте себе, что у реки есть ответвление, и предположите, что большинство этих ответвлений снова сливаются с рекой. Вот как может выглядеть естественное течение реки. Это имеет смысл.

    Но затем представьте, что у этой реки есть небольшой рукав. Затем по какой-то причине река сливается с ответвлением, а оттуда ответвление продолжается. Река сейчас технически исчезла, теперь она в рукаве. Но затем каким-то волшебным образом эта ветвь снова сливается с рекой. Какая река спросите вы? Я не знаю. Сейчас река должна быть в ответвлении, но каким-то образом она все еще продолжает существовать, и я могу снова слить ответвление с рекой. Итак, река в реке. Вроде не имеет смысла.

    Это именно то, что происходит, когда вы merge базовую ветвь в feature ветку, а затем, когда feature ветка завершена, вы снова объединяете ее обратно в базовую ветвь. Ментальная модель нарушена. И из-за этого вы получаете визуализацию ветки, которая не очень полезна.

Пример истории Git при использовании merge:

Пример истории Git при использовании слияния

Обратите внимание на множество коммитов, начинающихся с Merge branch 'main' into .... Их даже не существует, если вы перебазируете (там у вас будут только коммиты слияния запросов на вытягивание). Также много циклов слияния визуальных ветвей (main в feature в main).

Пример истории Git при использовании rebase:

Пример истории Git при использовании rebase

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

Есть ли недостатки / подводные камни с rebase?

Да:

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

Советы по уменьшению конфликтов слияния при использовании rebase:

  1. Часто перебазируйте. Обычно я рекомендую делать это хотя бы раз в день.
  2. Постарайтесь как можно больше втиснуть изменения в одной строке в одну фиксацию.
person Jeehut    schedule 12.10.2020
comment
очень хорошо написано палец вверх - person Chanwoo Park; 18.05.2021
comment
Я бы полностью удалил недостаток (2) из ​​вашего списка, потому что, как вы сказали, раздавливание - идеальное решение для (2), и оно всегда работает - person Nir O.; 18.05.2021

Перед слиянием / ребазом:

A <- B <- C    [master]
^
 \
  D <- E       [branch]

После git merge master:

A <- B <- C
^         ^
 \         \
  D <- E <- F

После git rebase master:

A <- B <- C <- D' <- E'

(A, B, C, D, E и F - коммиты)

Этот пример и более подробную информацию о Git можно найти в Git The Basics Tutorial.

person guybrush    schedule 07.06.2012

Этот ответ широко ориентирован на Git Flow. Таблицы были созданы с помощью прекрасного генератора таблиц ASCII, а деревья истории - с помощью этой замечательной команды. (псевдоним как git lg):

git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

Таблицы расположены в обратном хронологическом порядке, чтобы лучше соответствовать историческим деревьям. Также сначала обратите внимание на разницу между git merge и git merge --no-ff (обычно вы хотите использовать git merge --no-ff, так как это приближает вашу историю к реальности):

git merge

Команды:

Time          Branch "develop"             Branch "features/foo"
------- ------------------------------ -------------------------------
15:04   git merge features/foo
15:03                                  git commit -m "Third commit"
15:02                                  git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Результат:

* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
|           Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
|           Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge --no-ff

Команды:

Time           Branch "develop"              Branch "features/foo"
------- -------------------------------- -------------------------------
15:04   git merge --no-ff features/foo
15:03                                    git commit -m "Third commit"
15:02                                    git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Результат:

*   1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/            Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge vs git rebase

Первый момент: всегда объединяйте функции в разработку, никогда не переустанавливайте разработку на основе функций. Это следствие Золотого правила Ребазинг:

Золотое правило git rebase - никогда не использовать его в общедоступных ветках.

Другими словами:

Никогда не переустанавливайте то, что вы куда-то нажали.

Я бы лично добавил: если это не ветвь функций, И вы и ваша команда не знаете о последствиях.

Таким образом, вопрос git merge против git rebase применяется почти только к ветвям функций (в следующих примерах при слиянии всегда использовалось --no-ff). Обратите внимание: поскольку я не уверен, что есть одно лучшее решение (ведутся дебаты), я только расскажу, как ведут себя обе команды. В моем случае я предпочитаю использовать git rebase, так как он дает более красивое дерево истории :)

Между функциональными ветками

git merge

Команды:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Результат:

*   c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\            Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | |           Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | |           Fourth commit - Christophe
* | |   98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \            Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Команды:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git rebase features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Результат:

*   7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

От develop до функциональной ветки

git merge

Команды:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git merge --no-ff develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Результат:

*   9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\            Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* |   5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | |           Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ /            Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Команды:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git rebase develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Результат:

*   b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
*   856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\            Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

Боковые примечания

git cherry-pick

Если вам нужна только одна конкретная фиксация, git cherry-pick - хорошее решение (опция -x добавляет строку с надписью «(вишня, выбранная из фиксации ...)» в исходное тело сообщения фиксации, поэтому обычно хорошая идея использовать это - git log <commit_sha1> чтобы увидеть это):

Команды:

Time           Branch "develop"              Branch "features/foo"                Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git cherry-pick -x <second_commit_sha1>
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Результат:

*   50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| |           Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git pull --rebase

Я не уверен, что смогу объяснить это лучше, чем Дерек Гурли ... В основном, используйте git pull --rebase вместо git pull :) Однако в статье не хватает вы можете включить его по умолчанию:

git config --global pull.rebase true

git rerere

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

person sp00m    schedule 10.02.2017

Это предложение понимает это:

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

Источник: 3.6 Git Branch - Rebasing, Rebase vs. Merge

person Joaquin Sargiotto    schedule 05.01.2015

В книге Pro Git действительно хорошее объяснение страница изменения настроек.

Обычно слияние занимает два коммита и объединяет их.

Перебазирование перейдет к общему предку на обоих и постепенно применяет изменения друг к другу. Это делает историю более «чистой» и линейной.

Но когда вы перебазируете, вы отказываетесь от предыдущих коммитов и создаете новые. Поэтому никогда не следует переустанавливать публичный репозиторий. Другие люди, работающие с репозиторием, будут вас ненавидеть.

Только по этой причине я почти всегда сливаюсь. В 99% случаев мои ветки не сильно различаются, поэтому конфликты возникают только в одном или двух местах.

person xero    schedule 20.12.2012
comment
Слияния не объединяют коммиты - это переписывает историю. Rebase делает это. - person kellyfj; 30.03.2016
comment
Я не уверен, почему вы не можете переустановить ветку функции, а затем слить ее в общедоступной ветке. - person rob2d; 16.04.2021

  • Обычно используйте слияние
  • Если вы только один разработчик, вы можете использовать rebase, чтобы иметь четкую историю.
  • В общих проектах не используйте перебазирование, так как суммы кеширования изменены.
person yoAlex5    schedule 10.12.2019
comment
ИМО, это не отвечает на вопрос о когда использовать перебазирование или слияние - person Nico Haase; 04.02.2021
comment
В некоторых случаях вы хотите перебазировать свою функциональную ветку, чтобы получить коммиты от мастера. так что есть что рассмотреть. - person BluePie; 16.04.2021
comment
Для больших проектов полезно выполнить перебазирование, чтобы избежать пометки коммитов других людей как ваших при выполнении запроса на слияние. - person htafoya; 29.06.2021

Git rebase используется для того, чтобы сделать пути ветвления в очистке истории и структуру репозитория линейной.

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

После выполнения rebase мы также избавляемся от лишнего коммита, который мы использовали, чтобы проверить, выполняем ли мы обычное слияние.

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

person cvibha    schedule 24.09.2013

Некоторые практические примеры, отчасти связанные с крупномасштабной разработкой, где Gerrit используется для обзора и интеграция доставки:

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

git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature

Я сливаю, когда готовлю фиксацию доставки.

git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master

Я перебазирую, когда моя фиксация доставки не может интегрироваться по какой-либо причине, и мне нужно обновить ее до нового удаленного мастера.

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master
person Martin G    schedule 23.02.2016

Много раз объяснялось, что такое rebase и что такое слияние, но когда что использовать?

Когда следует использовать перебазирование?

Rebase отменяет ваши изменения и помещает все изменения перебазированной ветки в вашу текущую ветку, а затем помещает ваши изменения поверх нее. Следовательно, это изменяет историю вашей ветки.

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

Я сказал, что вы хотите видеть все свои изменения в одном месте, потому что иногда операция слияния объединяет все ваши изменения в одну фиксацию (сообщение some: merged from ...). Rebase заставляет ваше изменение выглядеть так, как будто вы делали свои коммиты все друг за другом, и никто другой не делал что-то между ними. Это упрощает просмотр того, что вы изменили для своей функции.

Однако убедитесь, что вы используете git merge feature-branch --ff-only, чтобы убедиться, что нет конфликтов, создающих одну фиксацию, когда вы объединяете свою функцию обратно в develop / master.

Когда следует использовать слияние?

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

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

person Jeremy Benks    schedule 07.02.2019
comment
Все наоборот. Если вы перебазируете, ваша история будет перезаписана, и поэтому некоторая информация будет потеряна. Слияние не изменяет и не теряет никакой истории, поэтому вы ошиблись. Самый важный момент, который вы упускаете, - это то, что перебазирование означает, что у вас есть линейная история. Ваш ответ упускает из виду точку ребазинга! - person Argeman; 06.10.2020

Когда мне использовать git rebase? Практически никогда, потому что он переписывает историю. git merge почти всегда является предпочтительным выбором, потому что он учитывает то, что на самом деле произошло в вашем проекте.

person Marnen Laibow-Koser    schedule 19.01.2018
comment
@benjaminhull Спасибо! - за исключением того, что я надеюсь, что мой ответ основан на фактах. ИМХО мнение не имеет места в подобных вещах: это факт, что потеря вашей реальной истории делает жизнь позже тяжелее. - person Marnen Laibow-Koser; 23.06.2019
comment
Согласен. Слияние никогда не приведет к повреждению истории и т. Д. (Когда вы переустанавливаете свои нажатые коммиты) - person surfrider; 14.08.2019
comment
Я не знаю, почему у этого ответа много отрицательных голосов. Мне пришлось проголосовать, чтобы минимизировать ущерб. РЖУ НЕ МОГУ. Я частично согласен, но я думаю, что мы можем перебазировать, если мы единственные, кто работает в ветке, чтобы все было чище. Основная проблема - если по какой-то неожиданной причине над этим начинают работать и другие. - person Maf; 30.03.2021