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

1330

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

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

  • 2
    См .: stackoverflow.com/questions/457927/…
  • 0
    Это также может помочь с более широкой картиной: stackoverflow.com/a/9204499/631619
Показать ещё 6 комментариев
Теги:
git-merge
git-rebase
version-control

15 ответов

1068

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

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

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

Объединить

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

Rebase

  • Второй сценарий был бы, если бы вы начали делать некоторую разработку, а затем другой разработчик сделал несвязанное изменение. Вероятно, вы захотите вытащить, а затем rebase, чтобы основывать свои изменения на текущей версии из репо.
  • 92
    @Rob упомянул поддержание временных коммитов при слиянии. Я полагаю, что по умолчанию объединение ветви B (ветвь функций, над которой вы работали) с веткой M (основной веткой) создаст один коммит в M для каждого коммита, который был сделан в B, поскольку эти два разошлись. Но если вы объединяете, используя опцию --squash, все коммиты, сделанные в ветке B, будут «объединены» и объединены как один коммит в ветке M, сохраняя журнал в вашей главной ветке красивым и чистым. Сквош - это, вероятно, то, что вы хотите, если у вас есть множество разработчиков, работающих независимо и возвращающихся в мастер.
  • 1
    @Rob, ваше объяснение ребаз может использовать немного больше объяснений для людей, у которых нет понятия ребаз. Spaaarky21 просто добавил немного тонкости ко всему с помощью «раздавливания».
Показать ещё 5 комментариев
316

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

  • 16
    but this way a new dummy commit is added, if you want to avoid spaghetti-history - как это плохо?
  • 0
    Привет @Alex, это зависит от личных предпочтений и веток протоколов для вашего проекта. Если вы хотите сохранить эти коммиты в своей истории для дальнейшего использования (например, слили ли мы мастер-версию в выпуск перед развертыванием?), Используйте слияние, иначе, если вы предпочитаете более чистую историю, используйте rebase.
Показать ещё 7 комментариев
246

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

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

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


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

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

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

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

Изображение 7521

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

Изображение 7522

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

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

Итак:

  • "rebase vs. merge" можно рассматривать как два способа импорта работы, скажем, master.
  • Но "rebase then merge" может быть допустимым рабочим процессом, чтобы сначала разрешить конфликт изолированно, а затем вернуть вашу работу.
  • 0
    Судя по звуку вашего ответа, почти кажется, что нужно все-таки сливаться даже после перебазирования. Если у меня есть основная ветвь и ветвь с основными функциями. Я создам функцию в master-feature, затем хочу, чтобы эта функция была доступна в master. Если я сделаю ребаз, все скажут: N коммитов, которые я сделал в master-feature, и покажут историю в master. Какой смысл сливаться после ребазинга?
  • 16
    объединение после rebase - это тривиальное ускоренное выполнение без разрешения конфликтов.
Показать ещё 14 комментариев
169

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

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

Использовать merge - not rebase, если вы уже нажали.

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

Восстановление ветки на главном сервере:

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

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

  • сохраняет историю того, где были созданы ветки тем, включая любые слияния от ведущего к ветке темы, чтобы поддерживать его актуальность. Вы действительно получаете точное представление о том, с каким кодом работал разработчик, когда они строили.
  • master - это ветвь, состоящая в основном из слияний, и каждая из этих коммитов слияния обычно является "хорошими моментами" в истории, которые безопасны для проверки, потому что там, где ветвь темы была готова к интеграции.
  • сохраняются все индивидуальные фиксации ветки темы, в том числе тот факт, что они были в ветке темы, поэтому выделение этих изменений является естественным, и вы можете развернуть его там, где это необходимо.
  • конфликты слияния должны быть разрешены только один раз (в точке слияния), поэтому промежуточные изменения фиксации, сделанные в ветки темы, не должны быть разрешены независимо.
  • можно сделать несколько раз гладко. Если вы интегрируете ветвь темы для периодического обучения, люди могут продолжать строить ветку темы, и она может продолжать быть объединенной независимо.
  • 3
    Кроме того, в git merge есть опция --no-ff (без быстрой перемотки вперед), которая позволяет вам действительно легко отменить все изменения, внесенные определенным слиянием.
  • 3
    Просто сделайте это более ясным: вы ссылаетесь на ситуацию «всякий раз, когда вы уже нажали» - это должно быть смелым. Ссылка на пост Линуса отличная, кстати, проясняет ее.
Показать ещё 8 комментариев
150

TL; DR

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

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

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

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

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

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

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

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

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

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

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

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

Является ли сама ветка полезной информацией

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

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

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

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

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

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

Общие мифы

Объединить историю уничтожения (squashes commits)

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

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

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

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

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

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

Rebase более холодный/более сексуальный/более профессиональный

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

Мои два цента

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

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

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

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

  • 2
    Это требует еще много голосов.
  • 2
    Должен быть подтвержденный ответ.
Показать ещё 4 комментария
66

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

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

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

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

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

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

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

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

  • 1
    Слияние с мастером может привести к быстрой перемотке вперед. В ветви возможностей могут быть некоторые коммиты, которые имеют незначительные ошибки или даже не компилируются. Если вы выполняете только модульное тестирование в функциональной ветви, некоторые ошибки в интеграции могут ускользнуть. Перед слиянием с мастером требуются интеграционные тесты, которые могут показать некоторые ошибки. Если они исправлены, функция может быть интегрирована. Поскольку вы не хотите фиксировать код с ошибками в мастере, перебазирование кажется необходимым, чтобы предотвратить быструю пересылку всех коммитов.
  • 1
    @mbx git merge поддерживает параметр --no-ff который заставляет его выполнить коммит слияния.
56

перед слиянием /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/

38

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

Понимание слияния немного лучше

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

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

Быстрая перемотка или слияние

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

Изображение 7523

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

Изображение 7524

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

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

Изображение 7526

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

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

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

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

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

Изображение 7527

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

Изображение 7528

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

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

Изображение 7529

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

Изображение 7530

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

Изображение 7531

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

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

27

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

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

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

15

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

Первая точка: всегда объединяет функции в разработку, никогда не перестраивается из функций. Это является следствием Golden Rule of Rebasing:

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

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

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

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

Таким образом, вопрос о git merge vs 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

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

15

Книга pro git как действительно хорошее объяснение на странице .

В основном слияние будет принимать 2 коммита и объединить их.

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

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

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

  • 1
    Слияния не объединяют коммиты - это было бы переписыванием истории. Rebase делает это.
4

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

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

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

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

2

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

Я объединяюсь, когда я поднимаю ветвь своей функции на новый удаленный мастер. Это дает минимальную работу по поднятию и легко отслеживает историю разработки функции, например, 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
  • 0
    Хорошие примеры. +1
0

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

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

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

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

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

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

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

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

-8

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню