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

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

Нужно ли объединять после успешной перезагрузки?

21 ответ

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

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

Так когда вы используете один из них?

сливаться

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

Rebase

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

Это просто, с 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 и коммиты с новой функцией, слияние будет просто ускоренной перемоткой вперед.

TL;DR

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ты работаешь в команде? Если да, готовы ли вы использовать подход "все или ничего" в этой отрасли?

Операции по перебазировке нужно тянуть с соответствующей git pull --rebase, Если вы работаете самостоятельно, вы можете вспомнить, какой из них следует использовать в соответствующее время. Если вы работаете в команде, это будет очень трудно координировать. Вот почему большинство рабочих процессов rebase рекомендуют использовать 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.

Я просто своими словами создал 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:

https://stackru.com/images/bd654e1bbcaaa593e9f89d41b489c3382de1e3ba.png

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

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

https://stackru.com/images/fef04975f791b074d68c2c32fd0621410cbf7ffb.png

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

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

Да:

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

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

  1. Ребазируйте часто. Обычно я рекомендую делать это хотя бы раз в день.
  2. Постарайтесь максимально сжать изменения в одной строке в один коммит.

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

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

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


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

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

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

Git-дерево по умолчанию, когда мы не слили и не перебазировали

rebase1

мы получаем путем ребазинга:

rebase3

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

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

Так:

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

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

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

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

Вот Линус (автор git) возьмется за это. Это действительно хорошее чтение. Или вы можете прочитать мою собственную версию той же идеи ниже.

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

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

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

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

Слияние означает: создать один новый коммит, который объединит мои изменения в место назначения.

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

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

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

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

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

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

Update3: Does one still need to merge after a successful rebase? Да, вы делаете. Причина в том, что перебазирование, по сути, включает "сдвиг" коммитов. Как я уже говорил выше, эти коммиты рассчитываются, но если у вас было 14 коммитов с точки ветвления, то при условии, что с вашей перебазировкой все в порядке, вы будете на 14 коммитов вперед (от точки, на которую вы перебазируете) после ребаз сделан. У вас была ветка перед ребазой. У вас будет ветвь такой же длины после. Вам все еще нужно объединиться, прежде чем публиковать свои изменения. Другими словами, сделайте ребаз столько раз, сколько хотите (опять же, только если вы не выдвинули свои изменения вверх по течению). Объединять только после того, как вы сделаете ребаз.

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

Понимание, слить лучше

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

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

Fast-Forward или Merge Commit

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

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

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

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

Human Commits & Merge Commits

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

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

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

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

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

Мы сделаем это в три этапа

  1. git rebase branch-A // syncs 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, которые произошли после того, как линии начали разветвляться (после коммитов общего предка). Однако, конечно, он не откажется от них: вместо этого вы можете думать об этих коммитах как о "временно спасенных".

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

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

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

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

До слияния / ребазирования:

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 можно найти здесь: http://excess.org/article/2008/07/ogre-git-tutorial/

Этот ответ широко ориентирован на 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 против 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 development
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 development
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

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

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

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

Источник: http://www.git-scm.com/book/en/v2/Git-Branching-Rebasing

Книга про git как хорошее объяснение на странице перебазирования.

В основном слияние займет 2 коммита и объединит их.

Ребаз будет идти к общему предку 2 и постепенно применять изменения друг к другу. Это делает "чище" и более линейной истории.

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

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

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

https://i.stack.imgur.com/fb6L4.png

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

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

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

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

Некоторые практические примеры, в некоторой степени связанные с крупномасштабной разработкой, где 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

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

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

Когда использовать rebase?

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

Как git rebase меняет историю. Поэтому вы не должны использовать его, когда кто-то еще работает над той же веткой / если вы нажали ее. Но если у вас есть локальная ветвь, вы можете выполнить мастер слияния ребаз перед слиянием своей ветки с главной, чтобы сохранить более чистую историю. Делая это, после слияния с основной ветвью не будет видно, что вы использовали ветку в основной ветке - история "чище", поскольку у вас нет автоматически сгенерированной "слитой..", но все еще есть полная история в вашей главной ветке без автоматической генерации коммитов "merged..". Убедитесь, что вы используете git merge feature-branch --ff-only чтобы не было конфликтов при создании единого коммита при слиянии вашей функции с основной.

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

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

  • когда вы нажали на ветку / другие тоже работают над этим
  • вам не нужна полная история
  • достаточно просто слияния

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

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

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

Инфографика всегда поможет :)

Слияние: наложение одной ветки на другую

      Assume the following history exists and the current branch is "master":

                     A---B---C topic
                    /
               D---E---F---G master

       Then "git merge topic" will replay the changes made on the topic branch since it
       diverged from master (i.e., E) until its current commit (C) on top of master, and
       record the result in a new commit along with the names of the two parent commits
       and a log message from the user describing the changes.

                     A---B---C topic
                    /         \
               D---E---F---G---H master

Rebase: перемещение изменения одной ветки в конец другой.

      Assume the following history exists and the current branch is "topic":

                     A---B---C topic
                    /
               D---E---F---G master

       From this point, the result of either of the following commands:

           git rebase master
           git rebase master topic

       would be:

                             A'--B'--C' topic
                            /
               D---E---F---G master

       NOTE: The latter form is just a short-hand of git checkout topic followed by git
       rebase master. When rebase exits topic will remain the checked-out branch.

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

Кредит: страница помощиgit merge --helpиgit rebase --help

  • Если несколько разработчиков переходят на 1 ветку , я используюgit merge --no-ffчтобы увидеть после слияния ветки релиза, кто написал эту строку.
  • Еще я используюgit rebaseсо сжатием, чтобы иметь четкую историю небольших коммитов git. В случае конфликтов после разрешения используюgit push --force-with-lease

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

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

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

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

Другие вопросы по тегам