Когда вы используете git rebase вместо git merge? - PullRequest
1395 голосов
/ 30 апреля 2009

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

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

Ответы [ 15 ]

1055 голосов
/ 30 апреля 2009

Короткая версия

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

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

Слияние

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

Rebase

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

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

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

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

$ git checkout master
$ git merge cool-feature

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

$ git checkout cool-feature
$ git rebase master

, а затем объединить его в master:

$ git checkout master
$ git merge cool-feature

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

260 голосов
/ 30 апреля 2009

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

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

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


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

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

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

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

rebase1

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

rebase3

Второй сценарий - как вернуть новую функцию в мастер.

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

Итак:

  • "rebase vs. merge" можно рассматривать как два способа импорта работы, скажем, master.
  • Но «ребаз, а затем слияние» может быть допустимым рабочим процессом, который сначала разрешает конфликт в изоляции, а затем возвращает вашу работу.
174 голосов
/ 13 апреля 2016

TL; DR

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

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

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

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

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

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

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

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

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

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

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.

171 голосов
/ 04 февраля 2014

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

60 голосов
/ 07 июня 2012

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

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/

52 голосов
/ 12 октября 2017

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

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

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

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

Fast-Forward или Merge Commit

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

enter image description here

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

enter image description here

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

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

enter image description here

Human Commits & Merge Commits

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

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

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

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

enter image description here

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

enter image description here

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

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

enter image description here

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

enter image description here

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

enter image description here

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

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

27 голосов
/ 05 января 2015

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

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

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

19 голосов
/ 10 февраля 2017

Этот ответ широко ориентирован на Git Flow . Таблицы были сгенерированы с помощью симпатичного ASCII Table Generator и деревьев истории с помощью этой замечательной команды ( aliased as 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 добавляет строку с надписью " (cherry selected from commit ...) " к исходному коммиту тело сообщения, поэтому обычно рекомендуется использовать его - 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

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

...