Когда рекомендуется использовать Git rebase или Git merge?
Мне все еще нужно слияние после успешной перебазировки?
Когда рекомендуется использовать Git rebase или Git merge?
Мне все еще нужно слияние после успешной перебазировки?
Итак, когда вы используете любой из них?
init
новым репо, add
файлом и commit
. Оформить новую ветку функции (checkout -b feature
.) Измените текстовый файл, сделайте фиксацию и повторите так, чтобы в ветке функции было две новых фиксации. Потом checkout master
и merge feature
. В log
я вижу свою первоначальную фиксацию на мастере, а затем две, которые были объединены из функции. Если вы merge --squash feature
, функция объединяется в мастер, но не фиксируется, поэтому единственной новой фиксацией в мастере будет то, что вы сделаете сами.
- person spaaarky21; 28.05.2013
foo
, добавил одну фиксацию в master
и несколько коммитов в foo
, затем git merge foo
. Если я просматриваю журнал, он показывает все коммиты от foo
. Это противоречит тому, как вы сказали, что git не будет включать все отдельные коммиты из ветки B, если они будут объединены с M ...
- person Fahmi; 11.03.2020
Это просто. С помощью 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, плюс коммиты с новой функцией, слияние будет просто быстрой перемоткой вперед.
but this way a new dummy commit is added, if you want to avoid spaghetti-history
- как там плохо?
- person アレックス; 19.05.2014
Sean Schofield
добавляет это в комментарий: Rebase также хорош, потому что, как только вы в конечном итоге объедините ur-материал обратно в master (что тривиально, как уже описано), вы увидите, что он находится в верхней части истории ваших коммитов. В более крупных проектах, где функции могут быть написаны, но объединены через несколько недель, вы не хотите просто объединять их в мастер, потому что они вставляются в мастер еще в истории. Лично мне нравится иметь возможность вести журнал git и видеть эту недавнюю функцию прямо вверху. Обратите внимание, что даты фиксации сохранены - перебазирование не меняет эту информацию.
- person Adrien Be; 01.03.2016
merge
, rebase
, fast-forward
и т.д.) относятся к конкретным манипуляциям с направленным ациклическим графом. Их становится легче рассуждать, имея в виду эту ментальную модель.
- person Roy Tinker; 27.01.2017
Если у вас есть сомнения, используйте слияние.
Единственные различия между перебазированием и слиянием:
Итак, краткий ответ - выбрать перебазирование или слияние в зависимости от того, как вы хотите, чтобы ваша история выглядела.
При выборе операции следует учитывать несколько факторов.
Если да, не переустанавливайте. Rebase уничтожает ветку, и у этих разработчиков будут поврежденные / несогласованные репозитории, если они не используют git pull --rebase
. Это хороший способ быстро расстроить других разработчиков.
Rebase - это деструктивная операция. Это означает, что если вы не примените его правильно, вы можете потерять выполненную работу и / или нарушить целостность репозиториев других разработчиков.
Я работал в командах, где все разработчики были из тех времен, когда компании могли позволить себе выделенный персонал для разветвления и слияния. Эти разработчики мало что знают о Git и не хотят много знать. В этих командах я бы ни по какой причине не рискнул рекомендовать ребазинг.
Некоторые команды используют модель ветвления на каждую функцию, где каждая ветвь представляет функцию (или исправление ошибки, или подфункцию и т. Д.). В этой модели ветвь помогает идентифицировать наборы связанных коммитов. Например, можно быстро отменить функцию, отменив слияние этой ветви (честно говоря, это редкая операция). Или различите функцию, сравнив две ветви (чаще всего). Rebase уничтожит ветку, и это будет непросто.
Я также работал с командами, которые использовали модель «ветвь на разработчика» (все мы были в ней). В этом случае сама ветка не несет никакой дополнительной информации (у коммита уже есть автор). Перебазирование не повредит.
Отмена (как и отмена) перебазирования значительно труднее и / или невозможно (если перебазирование имело конфликты) по сравнению с откатом слияния. Если вы думаете, что есть шанс вернуться, используйте слияние.
Операции Rebase необходимо подтянуть с соответствующим git pull --rebase
. Если вы работаете в одиночку, вы можете вспомнить, какие из них следует использовать в подходящее время. Если вы работаете в команде, это будет очень сложно координировать. Вот почему большинство рабочих процессов перебазирования рекомендуют использовать перебазирование для всех слияний (и git pull --rebase
для всех вытягиваний).
Предполагая, что у вас есть следующее слияние:
B -- C
/ \
A--------D
Некоторые люди скажут, что слияние «уничтожает» историю коммитов, потому что если вы посмотрите журнал только главной ветки (A - D), вы пропустите важные сообщения фиксации, содержащиеся в B и C.
Если бы это было правдой, у нас не было бы подобные вопросы. По сути, вы увидите B и C, если явно не попросите их не видеть (с помощью --first-parent). Это очень легко попробовать на себе.
Эти два подхода объединяются по-разному, но неясно, всегда ли один лучше другого, и это может зависеть от рабочего процесса разработчика. Например, если разработчик имеет тенденцию делать коммиты регулярно (например, может быть, он совершает коммит дважды в день при переходе с работы на дом), то для данной ветки может быть много коммитов. Многие из этих коммитов могут не иметь ничего общего с конечным продуктом (я стараюсь реорганизовать свой подход один или два раза для каждой функции). Если кто-то другой работал над связанной областью кода и пытался перебазировать мои изменения, это могло быть довольно утомительной операцией.
Если вам нравится использовать псевдоним от rm
до rm -rf
, чтобы «сэкономить время», то, возможно, перебазирование подойдет вам.
Я всегда думаю, что когда-нибудь я наткнусь на сценарий, в котором Git rebase - отличный инструмент, решающий проблему. Как и я, думаю, я столкнусь со сценарием, в котором Git reflog - отличный инструмент, решающий мою проблему. Я работаю с Git более пяти лет. Этого не произошло.
Беспорядочные истории никогда не были для меня проблемой. Я никогда не читаю историю своих коммитов, как увлекательный роман. В большинстве случаев, когда мне нужна история, я все равно буду использовать Git blame или Git bisect. В этом случае наличие фиксации слияния действительно полезно для меня, потому что, если слияние вызвало проблему, для меня это значимая информация.
Я считаю своим долгом упомянуть, что я лично смягчился в отношении использования rebase, хотя мой общий совет все еще в силе. В последнее время я много взаимодействую с проектом Angular 2 Material. Они использовали rebase, чтобы сохранить очень чистую историю коммитов. Это позволило мне очень легко увидеть, какая фиксация исправила данный дефект и была ли эта фиксация включена в выпуск. Это отличный пример правильного использования rebase.
git pull
поведение, включив по умолчанию флаг --rebase
. Это означает, что выполнение перебазирования веток, используемых несколькими разработчиками, немного менее опасно. Человек, вносящий ваши изменения, может быть удивлен тем, что во время такой операции необходимо разрешить некоторые конфликты, но катастрофы не будет.
- person pkubik; 31.07.2020
В дополнение к моему собственному ответу упоминается от TSamper,
довольно часто перед слиянием рекомендуется выполнить перебазирование, потому что идея состоит в том, что вы интегрируете в свою ветку Y
работу той ветки, B
на которой вы будете выполнять слияние.
Но опять же, перед слиянием вы разрешаете любой конфликт в своей ветке (например, «перебазировать», как в «воспроизвести мою работу в моей ветке, начиная с недавней точки ветки B
). .
Если все сделано правильно, последующее слияние из вашей ветки в ветку B
может быть перемотано вперед.
слияние напрямую влияет на целевую ветвь B
, что означает, что слияние лучше быть тривиальным, иначе эта ветка B
может быть долгой, чтобы вернуться в стабильное состояние (пора решить все конфликты)
точка слияния после ребаза?
В случае, который я описываю, я переставляю B
в свою ветку, просто чтобы иметь возможность воспроизвести мою работу из более позднего момента из B
, но оставаясь в моей ветке.
В этом случае все еще необходимо слияние, чтобы перенести мою "воспроизведенную" работу на B
.
Другой сценарий (описан в Git Ready для instance), заключается в том, чтобы перенести вашу работу прямо в B
через перебазирование (которое сохраняет все ваши хорошие коммиты или даже дает вам возможность изменить их порядок с помощью интерактивного перебазирования).
В этом случае (когда вы перебазируете, находясь в ветке B), вы правы: дальнейшее слияние не требуется:
Дерево Git по умолчанию, если мы не выполняли слияние и не перебазировали
получаем путем перебазирования:
Этот второй сценарий посвящен тому, как мне вернуть новую функцию в мастер.
Я хочу сказать, описывая первый сценарий перебазирования, чтобы напомнить всем, что перебазирование также можно использовать как предварительный шаг к этому (то есть «вернуть новую функцию в мастер»).
Вы можете использовать rebase, чтобы сначала перенести master "в" ветку новой функции: rebase будет воспроизводить коммиты новой функции из HEAD master
, но все еще в ветке новой функции, эффективно перемещая начальную точку вашей ветки из старый мастер фиксирует HEAD-master
.
Это позволяет разрешать любые конфликты в вашей ветке (то есть изолированно, позволяя мастеру продолжать развиваться параллельно, если этап разрешения конфликта занимает слишком много времени).
Затем вы можете переключиться на master и объединить new-feature
(или переустановить new-feature
на master
, если вы хотите сохранить коммиты, сделанные в вашей ветке new-feature
).
So:
master
.git rebase
странице руководства подробно описывается возможное решение в этом редком случае.
- person VonC; 22.08.2011
master
), а не объединять мастер и, возможно, вносить некоторые исправления в мою ветку функций, прежде чем объединять ее обратно в master
: история чище.
- person VonC; 24.01.2013
Многие ответы здесь говорят, что слияние превращает все ваши коммиты в один, и поэтому предлагается использовать rebase для сохранения ваших коммитов. Это неверно. И это плохая идея, если вы уже отправили свои коммиты.
Слияние не стирает ваши коммиты. Слияние сохраняет историю! (просто посмотрите на gitk) Rebase переписывает историю, что является плохим моментом после того, как вы ее нажали.
Используйте слияние, а не перебазирование всякий раз, когда вы уже нажали.
Вот Линус (автор Git) возьми на себя (теперь размещен в моем собственном блоге, как восстановлено Wayback Machine). Это действительно хорошее чтение.
Или вы можете прочитать мою собственную версию той же идеи ниже.
Перебазирование ветки на master:
Напротив, объединение ветки темы в мастер:
master
и dev
. Нет ничего плохого в том, чтобы перенастроить какую-то случайную ветку функций. Или выполните локальную перестановку, а затем нажмите на новую ветку функций, которую вы затем используете для создания PR.
- person pkamb; 24.04.2019
TL; DR: Это зависит от того, что наиболее важно - аккуратная история или точное представление последовательности развития
Если аккуратная история является наиболее важной, тогда вы должны сначала переустановить, а затем объединить свои изменения, чтобы было ясно, что именно представляет собой новый код. Если вы уже отправили ветку, не перемещайте ее, пока не сможете справиться с последствиями.
Если истинное представление последовательности является наиболее важным, вы должны выполнить слияние без перестановки.
Слияние означает: создать одну новую фиксацию, которая объединяет мои изменения в место назначения. Примечание. У этого нового коммита будет два родительских элемента - последний коммит из вашей строки коммитов и последний коммит другой ветви, которую вы объединяете.
Rebase означает: создать новую серию коммитов, используя мой текущий набор коммитов в качестве подсказок. Другими словами, посчитайте, как бы выглядели мои изменения, если бы я начал вносить их с той точки, на которую я выполняю перебазирование. Поэтому после перебазирования вам может потребоваться повторное тестирование ваших изменений, и во время перебазирования у вас может возникнуть несколько конфликтов.
Учитывая это, зачем вам переустанавливать? Просто чтобы история развития была ясна. Предположим, вы работаете над функцией X, и когда закончите, вы объединяете свои изменения. Теперь у места назначения будет одна фиксация, которая будет говорить что-то вроде «Добавленная функция X». Теперь, вместо слияния, если вы перебазируете, а затем слились, история разработки места назначения будет содержать все отдельные коммиты в единой логической последовательности. Это значительно упрощает последующий просмотр изменений. Представьте, как сложно было бы просматривать историю разработки, если бы 50 разработчиков постоянно объединяли различные функции.
Тем не менее, если вы уже отправили ветку, над которой работаете, вверх по течению, вам не следует выполнять перебазирование, а вместо этого выполнить слияние. Для веток, которые не были отправлены вверх по течению, выполните перебазирование, тестирование и слияние.
Другой раз, когда вы можете захотеть перебазировать, - это когда вы хотите избавиться от коммитов из своей ветки перед тем, как продвигать вверх по течению. Например: коммиты, которые вводят некоторый код отладки на ранней стадии, а другие коммиты далее, очищают этот код. Единственный способ сделать это - выполнить интерактивную перебазировку: git rebase -i <branch/commit/tag>
ОБНОВЛЕНИЕ: вы также хотите использовать rebase, когда используете Git для взаимодействия с системой управления версиями, которая не поддерживает нелинейную историю (Subversion, например). При использовании моста git-svn очень важно, чтобы изменения, которые вы объединяете обратно в Subversion, представляли собой последовательный список изменений поверх самых последних изменений в стволе. Есть только два способа сделать это: (1) вручную воссоздать изменения и (2) использовать команду rebase, что намного быстрее.
ОБНОВЛЕНИЕ 2: Еще один способ думать о перебазировании - это то, что он позволяет своего рода сопоставление вашего стиля разработки со стилем, принятым в репозитории, который вы фиксируете. Допустим, вам нравится совершать коммит небольшими, крошечными порциями. У вас есть одна фиксация для исправления опечатки, одна фиксация для избавления от неиспользуемого кода и так далее. К тому времени, как вы закончите то, что вам нужно, у вас будет длинная серия коммитов. Теперь предположим, что репозиторий, который вы собираетесь использовать, поощряет большие коммиты, поэтому для работы, которую вы выполняете, можно ожидать одного или, может быть, двух коммитов. Как взять строку коммитов и сжать их до ожидаемого? Вы бы использовали интерактивную перестановку и разделили бы ваши крошечные коммиты на меньшее количество более крупных кусков. То же самое верно, если было необходимо обратное - если ваш стиль был несколькими большими коммитами, но репозиторий требовал длинных строк маленьких коммитов. Для этого вы также должны использовать rebase. Если бы вы вместо этого объединились, вы теперь перенесли свой стиль фиксации в основной репозиторий. Если разработчиков много, вы можете себе представить, как сложно будет проследить историю с несколькими разными стилями коммитов через некоторое время.
ОБНОВЛЕНИЕ 3: Does one still need to merge after a successful rebase?
Да, это так. Причина в том, что перебазирование по существу включает в себя «смещение» коммитов. Как я уже сказал выше, эти коммиты рассчитываются, но если у вас было 14 коммитов с точки разветвления, то, если с вашим перебазированием ничего не пойдет не так, у вас будет 14 коммитов вперед (от точки, на которую вы перебазируете) после перебазирование выполнено. Перед перебазированием у вас была ветка. После у вас будет ветка такой же длины. Перед публикацией изменений вам все равно необходимо выполнить слияние. Другими словами, выполняйте перебазирование столько раз, сколько захотите (опять же, только если вы не отправили свои изменения вверх по течению). Слияние только после перебазирования.
git merge
поддерживает параметр --no-ff
, который заставляет его выполнять фиксацию слияния.
- person Gavin S. Yancey; 09.06.2017
Хотя слияние определенно является самым простым и распространенным способом интеграции изменений, это не единственный способ: Rebase - альтернативный способ интеграции.
Лучшее понимание слияния
Когда Git выполняет слияние, он ищет три коммита:
Быстрая перемотка вперед или фиксация слияния
В очень простых случаях одна из двух веток не имеет новых коммитов после того, как произошло ветвление - ее последняя фиксация по-прежнему является общим предком.
В этом случае выполнить интеграцию очень просто: Git может просто добавить все коммиты другой ветки поверх коммита общего предка. В Git эта простейшая форма интеграции называется слиянием с перемоткой вперед. Обе ветви имеют одинаковую историю.
Однако во многих случаях обе ветви развивались индивидуально.
Чтобы выполнить интеграцию, Git должен будет создать новую фиксацию, которая будет содержать различия между ними - фиксацию слияния.
Человеческие коммиты и слияния
Обычно коммит тщательно создается человеком. Это значимая единица, которая обертывает только связанные изменения и аннотирует их комментариями.
Коммит слияния немного отличается: вместо того, чтобы быть созданным разработчиком, он создается автоматически Git. И вместо того, чтобы оборачивать набор связанных изменений, его цель - соединить две ветви, как узел. Если вы хотите понять операцию слияния позже, вам нужно взглянуть на историю обеих веток и соответствующий график фиксации.
Интеграция с Rebase
Некоторые люди предпочитают обходиться без таких автоматических коммитов слияния. Вместо этого они хотят, чтобы история проекта выглядела так, как если бы она развивалась в виде единой прямой линии. Не осталось никаких указаний на то, что в какой-то момент она была разделена на несколько ветвей.
Давайте шаг за шагом рассмотрим операцию перебазирования. Сценарий тот же, что и в предыдущих примерах: мы хотим интегрировать изменения из ветки B в ветку A, но теперь с помощью rebase.
Сделаем это в три шага
git rebase branch-A // Synchronises the history with branch-A
git checkout branch-A // Change the current branch to branch-A
git merge branch-B // Merge/take the changes from branch-B to branch-A
Во-первых, Git «отменит» все коммиты на ветке A, которые произошли после того, как строки начали разветвляться (после коммита общего предка). Однако, конечно, он не будет их отбрасывать: вместо этого вы можете думать об этих коммитах как о «временно сохраненных».
Затем он применяет коммиты из ветки B, которые мы хотим интегрировать. На данный момент обе ветви выглядят одинаково.
На последнем этапе новые коммиты в ветке A теперь применяются повторно, но в новой позиции, поверх интегрированных коммитов из ветки B (они основаны заново).
Результат выглядит так, как будто развитие происходило по прямой линии. Вместо фиксации слияния, содержащей все объединенные изменения, была сохранена исходная структура фиксации.
Наконец, вы получаете чистую ветку branch-A без нежелательных и автоматически сгенерированных коммитов.
Примечание. Взято из замечательного сообщение от git-tower
. Недостатки rebase
также хорошо прочитаны в том же посте.
Я только что своими словами создал FAQ для своей команды, который отвечает на этот вопрос. Разрешите поделиться:
merge
?Коммит, который объединяет все изменения другой ветки в текущую.
rebase
?Повторная фиксация всех коммитов текущей ветки на другую базовую фиксацию.
merge
и rebase
?merge
выполняет только одну новую фиксацию. rebase
обычно выполняет несколько (количество коммитов в текущей ветке).merge
создает новую сгенерированную фиксацию (так называемую фиксацию слияния). rebase
перемещает только существующие коммиты.merge
?Используйте merge
всякий раз, когда вы хотите добавить изменения разветвленной ветви назад в базовую ветвь.
Как правило, вы делаете это, нажимая кнопку «Объединить» в запросах на извлечение / объединение, например на GitHub.
rebase
?Используйте rebase
всякий раз, когда вы хотите добавить изменения базовой ветви обратно в разветвленную ветвь.
Обычно это делается в feature
ветвях всякий раз, когда происходит изменение в main
ветке.
merge
для объединения изменений из базовой ветки в функциональную ветку?История git будет включать множество ненужных коммитов слияния. Если в функциональной ветке требовалось несколько слияний, то эта функциональная ветка может даже содержать больше слияний, чем фактических!
Это создает цикл, который разрушает ментальную модель, созданную Git, что вызывает проблемы при любой визуализации истории Git.
Представьте себе реку (например, Нил). Вода течет в одном направлении (направление времени в истории Git). Время от времени представьте себе, что у реки есть ответвление, и предположите, что большинство этих ответвлений снова сливаются с рекой. Вот как может выглядеть естественное течение реки. Это имеет смысл.
Но затем представьте, что у этой реки есть небольшой рукав. Затем по какой-то причине река сливается с ответвлением, а оттуда ответвление продолжается. Река сейчас технически исчезла, теперь она в рукаве. Но затем каким-то волшебным образом эта ветвь снова сливается с рекой. Какая река спросите вы? Я не знаю. Сейчас река должна быть в ответвлении, но каким-то образом она все еще продолжает существовать, и я могу снова слить ответвление с рекой. Итак, река в реке. Вроде не имеет смысла.
Это именно то, что происходит, когда вы merge
базовую ветвь в feature
ветку, а затем, когда feature
ветка завершена, вы снова объединяете ее обратно в базовую ветвь. Ментальная модель нарушена. И из-за этого вы получаете визуализацию ветки, которая не очень полезна.
merge
:Обратите внимание на множество коммитов, начинающихся с Merge branch 'main' into ...
. Их даже не существует, если вы перебазируете (там у вас будут только коммиты слияния запросов на вытягивание). Также много циклов слияния визуальных ветвей (main
в feature
в main
).
rebase
:Более чистая история Git с гораздо меньшим количеством коммитов слияния и без каких-либо загроможденных визуальных циклов слияния ветвей.
rebase
?Да:
rebase
перемещений совершает фиксацию (технически повторно выполняет их), дата фиксации всех перемещенных коммитов будет временем перебазирования, а история git теряет время первоначальной фиксации. Итак, если по какой-то причине требуется точная дата фиксации, то merge
- лучший вариант. Но обычно чистая история git намного полезнее, чем точные даты фиксации.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 можно найти в Git The Basics 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
vs git rebase
Первый момент: всегда объединяйте функции в разработку, никогда не переустанавливайте разработку на основе функций. Это следствие Золотого правила Ребазинг:
Золотое правило
git rebase
- никогда не использовать его в общедоступных ветках.
Никогда не переустанавливайте то, что вы куда-то нажали.
Я бы лично добавил: если это не ветвь функций, И вы и ваша команда не знаете о последствиях.
Таким образом, вопрос git merge
против git rebase
применяется почти только к ветвям функций (в следующих примерах при слиянии всегда использовалось --no-ff
). Обратите внимание: поскольку я не уверен, что есть одно лучшее решение (ведутся дебаты), я только расскажу, как ведут себя обе команды. В моем случае я предпочитаю использовать git rebase
, так как он дает более красивое дерево истории :)
git merge
Команды:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Результат:
* c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\ Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | | Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | | Fourth commit - Christophe
* | | 98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \ Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
Команды:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git rebase features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Результат:
* 7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
develop
до функциональной веткиgit merge
Команды:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m "Sixth commit"
15:08 git merge --no-ff develop
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Результат:
* 9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\ Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* | 5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | | Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ / Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
Команды:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m "Sixth commit"
15:08 git rebase develop
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Результат:
* b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git cherry-pick
Если вам нужна только одна конкретная фиксация, git cherry-pick
- хорошее решение (опция -x
добавляет строку с надписью «(вишня, выбранная из фиксации ...)» в исходное тело сообщения фиксации, поэтому обычно хорошая идея использовать это - git log <commit_sha1>
чтобы увидеть это):
Команды:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git cherry-pick -x <second_commit_sha1>
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Результат:
* 50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| | Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git pull --rebase
Я не уверен, что смогу объяснить это лучше, чем Дерек Гурли ... В основном, используйте git pull --rebase
вместо git pull
:) Однако в статье не хватает вы можете включить его по умолчанию:
git config --global pull.rebase true
git rerere
Опять же, хорошо объяснено здесь. Но, проще говоря, если вы включите его, вам больше не придется разрешать один и тот же конфликт несколько раз.
Это предложение понимает это:
В общем, способ получить лучшее из обоих миров - это перебазировать локальные изменения, которые вы сделали, но еще не поделились, прежде чем отправлять их, чтобы очистить свою историю, но никогда не переустанавливайте все, что вы куда-то отправили. .
Источник: 3.6 Git Branch - Rebasing, Rebase vs. Merge
В книге Pro Git действительно хорошее объяснение страница изменения настроек.
Обычно слияние занимает два коммита и объединяет их.
Перебазирование перейдет к общему предку на обоих и постепенно применяет изменения друг к другу. Это делает историю более «чистой» и линейной.
Но когда вы перебазируете, вы отказываетесь от предыдущих коммитов и создаете новые. Поэтому никогда не следует переустанавливать публичный репозиторий. Другие люди, работающие с репозиторием, будут вас ненавидеть.
Только по этой причине я почти всегда сливаюсь. В 99% случаев мои ветки не сильно различаются, поэтому конфликты возникают только в одном или двух местах.
Git rebase используется для того, чтобы сделать пути ветвления в очистке истории и структуру репозитория линейной.
Он также используется для сохранения конфиденциальности созданных вами веток, так как после перезагрузки и отправки изменений на сервер, если вы удалите свою ветку, не будет никаких свидетельств ветки, над которой вы работали. Так что теперь ваш филиал - это ваша местная забота.
После выполнения rebase мы также избавляемся от лишнего коммита, который мы использовали, чтобы проверить, выполняем ли мы обычное слияние.
И да, после успешной перебазировки все еще нужно выполнить слияние, поскольку команда rebase просто помещает вашу работу поверх ветки, которую вы упомянули во время перебазирования, скажем, 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
Много раз объяснялось, что такое rebase и что такое слияние, но когда что использовать?
Когда следует использовать перебазирование?
Rebase отменяет ваши изменения и помещает все изменения перебазированной ветки в вашу текущую ветку, а затем помещает ваши изменения поверх нее. Следовательно, это изменяет историю вашей ветки.
Я сказал, что вы хотите видеть все свои изменения в одном месте, потому что иногда операция слияния объединяет все ваши изменения в одну фиксацию (сообщение some: merged from ...). Rebase заставляет ваше изменение выглядеть так, как будто вы делали свои коммиты все друг за другом, и никто другой не делал что-то между ними. Это упрощает просмотр того, что вы изменили для своей функции.
Однако убедитесь, что вы используете git merge feature-branch --ff-only, чтобы убедиться, что нет конфликтов, создающих одну фиксацию, когда вы объединяете свою функцию обратно в develop / master.
Когда следует использовать слияние?
(*) вы можете избежать того, что ваша функция будет объединена только с одним ... коммитом, сначала объединяя ветку разработки с вашей функцией, а затем объединяя вашу функцию обратно в develeop. Это по-прежнему дает вам объединенный .. коммит, но по крайней мере все коммиты вашей функции все еще видны.
Когда мне использовать git rebase
? Практически никогда, потому что он переписывает историю. git merge
почти всегда является предпочтительным выбором, потому что он учитывает то, что на самом деле произошло в вашем проекте.