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

<p>когда рекомендуется использовать <code>git rebase</code> против <code>git merge</code>?</p><p>нужно ли объединять после успешной перезагрузки?</p>

<h2>укороченная версия</h2><ul> <li>слияние принимает все изменения в одной ветви и объединяет их в другую ветку за один коммит.</li> <li>ребаз говорит, что я хочу, чтобы точка, в которой я разветвился, переместилась в новую отправную точку.</li> </ul><p>так когда вы используете один из них?</p><h3>сливаться</h3><ul> <li>допустим, вы создали ветку с целью разработки одной функции. если вы хотите вернуть эти изменения в master, вы, вероятно, захотите <strong>объединить</strong> (вам не нужно поддерживать все временные коммиты).</li> </ul><h3>rebase</h3><ul> <li>второй сценарий: если вы начали заниматься какой-то разработкой, а другой разработчик внес несвязанные изменения. вы, вероятно, хотите вытащить и затем <strong>rebase</strong>, чтобы основать свои изменения из текущей версии из репозитория.</li> </ul>

<p>все просто, с rebase вы говорите использовать другую ветку как новую <strong>base</strong> для вашей работы.</p><p>если у вас есть, например, ветвь <code>master</code> и вы создаете ветвь для реализации новой функции, скажем, вы называете ее <code>cool-feature</code>, конечно, основная ветвь является базой для вашей новой функции.</p><p>теперь в определенный момент вы хотите добавить новую функцию, которую вы реализовали в ветке <code>master</code>. вы можете просто переключиться на <code>master</code> и объединить ветвь <code>cool-feature</code>:</p><pre><code>$ git checkout master $ git merge cool-feature </code></pre><p>но таким образом добавляется новый фиктивный коммит, если вы хотите избежать истории спагетти, вы можете <strong>rebase</strong>:</p><pre><code>$ git checkout cool-feature $ git rebase master </code></pre><p>а затем объединить его в <code>master</code>:</p><pre><code>$ git checkout master $ git merge cool-feature </code></pre><p>на этот раз, поскольку ветка темы имеет те же коммиты master и коммиты с новой функцией, слияние будет просто ускоренной перемоткой вперед.</p>

<p>чтобы дополнить <a href="https://stackoverflow.com/questions/457927/git-workflow-and-rebase-vs-merge-questions">мой собственный ответ</a> упомянуто <a href="https://stackoverflow.com/questions/804115/git-rebase-vs-git-merge/804119#804119">tsamper</a> ,</p><ul> <li> <p>перед слиянием довольно часто хорошая идея сделать перебазирование, потому что идея заключается в том, что вы интегрируете в свою ветвь <code>y</code> работу ветви <code>b</code>, в которую вы будете сливаться.<br> но опять же, перед объединением вы разрешаете любой конфликт в <em> вашей </em> ветке (то есть: "rebase", как в "воспроизвести мою работу в моей ветке, начиная с недавнего точка из ветки <code>b</code>)<br> если все сделано правильно, последующее слияние из вашей ветви в ветку <code>b</code> может быть ускорено.</p> </li> <li> <p>влияние слияния непосредственно на целевую ветвь <code>b</code>, что означает, что слияния лучше быть тривиальными, в противном случае эта ветвь <code>b</code> может быть долгой, чтобы вернуться в стабильное состояние (время для разрешения всех конфликтов)</p> </li> </ul><hr><blockquote> <p>точка слияния после ребазирования?</p> </blockquote><p>в случае, который я описываю, я перебазирую <code>b</code> в свою ветку, просто чтобы иметь возможность воспроизвести мою работу с более поздней точки из <code>b</code>, но оставаясь в моей ветви.<br> в этом случае все еще необходимо объединение, чтобы перенести мою "воспроизведенную" работу на <code>b</code>.</p><p>другой сценарий ( <a href="http://www.gitready.com/intermediate/2009/01/31/intro-to-rebase.html">описанный, например, в git ready</a> ) состоит в том, чтобы перенести вашу работу непосредственно в <code>b</code> посредством перебазирования (которое сохраняет все ваши коммиты nice или даже дает вам возможность переупорядочить их через интерактивный ребаз).<br> в этом случае (когда вы перебазируете, находясь в ветке b), вы правы: дальнейшее слияние не требуется:</p><p><strong> git-дерево по умолчанию, когда мы не слили и не перебазировали </strong></p><p><img src="https://i.stack.imgur.com/7vsgz.png" alt="rebase1"></p><p><strong> мы получаем перебазировкой: </strong></p><p><img src="https://i.stack.imgur.com/ycxoo.png" alt="rebase3"></p><p>этот второй сценарий полностью посвящен тому, как вернуть новую функцию в мастер.</p><p>моя точка зрения, описывая первый сценарий перебазирования, состоит в том, чтобы напомнить всем, что перебазировка также может быть использована в качестве предварительного шага к этому (то есть, "получить новую функцию обратно в мастер").<br> вы можете использовать rebase, чтобы сначала привести master "в" ветку новой функции: rebase будет воспроизводить коммиты новой функции из <code>head master</code>, но все еще в ветке новой функции, эффективно перемещая начальную точку ветки из старый мастер коммит <code>head-master</code>.<br> это позволяет вам разрешать любые конфликты в <em> вашей </em> ветви (то есть изолированно, в то же время позволяя мастеру продолжать развиваться параллельно, если этап разрешения конфликта занимает слишком много времени). ).<br> затем вы можете переключиться на master и объединить <code>new-feature</code> (или перебазировать <code>new-feature</code> в <code>master</code>, если вы хотите сохранить коммиты, сделанные в вашей ветви <code>new-feature</code>).</p><p>так:</p><ul> <li>"rebase vs. merge" можно рассматривать как два способа импорта работы, скажем, <code>master</code>.</li> <li>но "ребаз, а затем слияние" может быть допустимым рабочим процессом, чтобы сначала разрешить конфликт изолированно, а затем вернуть свою работу.</li> </ul>

<h1>tl; dr</h1><p>если у вас есть какие-либо сомнения, используйте слияние.</p><h2>короткий ответ</h2><p>единственные различия между ребазом и слиянием:</p><ul> <li>результирующая древовидная структура истории (обычно заметная только при просмотре графа коммитов) отличается (одна будет иметь ветви, а другая - нет).</li> <li>слияние обычно создает дополнительный коммит (например, узел в дереве).</li> <li>слияние и перебазировка будут обрабатывать конфликты по-разному. rebase будет представлять конфликты по одному коммиту за раз, когда слияние представляет их все одновременно.</li> </ul><p>таким образом, короткий ответ - <em> выбрать перебазирование или слияние в зависимости от того, как вы хотите, чтобы ваша история выглядела </em>.</p><h2>длинный ответ</h2><p>есть несколько факторов, которые вы должны учитывать при выборе операции.</p><h3>разделяется ли ветка, от которой вы получаете изменения, с другими разработчиками за пределами вашей команды (например, с открытым исходным кодом, общедоступным)?</h3><p>если это так, не перебазируйте. rebase уничтожает ветку, и эти разработчики будут иметь поврежденные/несовместимые репозитории, если они не используют <code>git pull --rebase</code>. это хороший способ быстро расстроить других разработчиков.</p><h3>насколько опытна ваша команда разработчиков?</h3><p>ребаз это разрушительная операция. это означает, что, если вы не примените это правильно, <strong> вы можете потерять совершенную работу и/или нарушить целостность репозиториев других разработчиков. </strong></p><p>я работал в командах, в которых все разработчики пришли из того времени, когда компании могли позволить себе выделенный персонал, чтобы заниматься ветвлением и слиянием. эти разработчики мало знают о git и не хотят много знать. в этих командах я бы ни за что не рискнул порекомендовать перебазирование.</p><h3>представляет ли сама ветка полезную информацию</h3><p>некоторые команды используют модель ветвления на функцию, где каждая ветвь представляет функцию (или исправление, или подфункцию и т.д.). в этой модели ветвь помогает идентифицировать наборы связанных коммитов. например, можно быстро восстановить функцию, отменив объединение этой ветви (если честно, это редкая операция). или отличить особенность, сравнивая две ветви (чаще). rebase уничтожит ветку, и это не будет простым.</p><p>я также работал над командами, которые использовали модель ветвления на разработчика (мы все были там). в этом случае сама ветка не передает никакой дополнительной информации (у коммита уже есть автор). там не будет никакого вреда в перебазировании.</p><h3>можете ли вы отменить слияние по какой-либо причине?</h3><p>возврат (как при отмене) перебазирования является значительно трудным и/или невозможным (если у перебазирования были конфликты) по сравнению с возвратом слияния. если вы считаете, что есть шанс вернуться, используйте merge.</p><h3>ты работаешь в команде? если да, готовы ли вы использовать подход "все или ничего" в этой отрасли?</h3><p>операции перебазирования должны быть извлечены с соответствующим <code>git pull --rebase</code>. если вы работаете самостоятельно, вы можете вспомнить, какой из них следует использовать в соответствующее время. если вы работаете в команде, это будет очень трудно координировать. вот почему большинство рабочих процессов rebase рекомендуют использовать rebase для всех слияний (и <code>git pull --rebase</code> для всех извлечений).</p><h2>общие мифы</h2><h3>слияние уничтожает историю</h3><p>предполагая, что у вас есть следующее слияние:</p><pre><code> b -- c / \ a--------d </code></pre><p>некоторые люди утверждают, что слияние "уничтожает" историю коммитов, потому что если бы вы смотрели журнал только основной ветки (a - d), вы пропустили бы важные сообщения коммитов, содержащиеся в b и c.</p><p>если бы это было правдой, у нас не было бы <a href="https://stackoverflow.com/questions/15875253/git-log-to-return-only-the-commits-made-to-the-master-branch">такие вопросы</a> . по сути, вы увидите b и c, если явно не попросите их не видеть (используя --first-parent). это очень легко попробовать для себя.</p><h3>rebase позволяет более безопасные/простые слияния</h3><p>два подхода объединяются по-разному, но не ясно, что один всегда лучше другого, и это может зависеть от рабочего процесса разработчика. например, если разработчик стремится к регулярной фиксации (например, может быть, он фиксирует дважды в день при переходе с работы на дом), тогда для данной ветви может быть много коммитов. многие из этих коммитов могут не выглядеть как конечный продукт (я склонен к рефакторингу своего подхода один или два раза для каждой функции). если кто-то еще работает над соответствующей областью кода и пытается отменить мои изменения, это может быть довольно утомительной операцией.</p><h3>rebase круче/сексуальнее/профессиональнее</h3><p>если вы хотите использовать псевдоним <code>rm</code> для <code>rm -rf</code>, чтобы "сэкономить время", то, возможно, ребаз для вас.</p><h1>мои два цента</h1><p>я всегда думаю, что когда-нибудь я столкнусь со сценарием, в котором git rebase является отличным инструментом, который решает проблему. как и я думаю, я столкнусь со сценарием, в котором git reflog - это замечательный инструмент, который решает мою проблему. я работал с git более пяти лет. этого не случилось.</p><p>грязные истории никогда не были для меня проблемой. я никогда не читаю историю коммитов, как захватывающий роман. большую часть времени мне нужна история, я в любом случае собираюсь использовать git blame или git bisect. в этом случае наличие коммита слияния на самом деле полезно для меня, потому что, если слияние привело к проблеме, которая является важной информацией для меня.</p><h1>обновление (4/2017)</h1><p>я чувствую себя обязанным упомянуть, что лично смягчил использование rebase, хотя мой общий совет остается в силе. недавно я много взаимодействовал с проектом <a href="https://github.com/angular/material2">angular 2 material</a> . они использовали rebase, чтобы сохранить очень чистую историю коммитов. это позволило мне очень легко увидеть, что фиксация исправила данный дефект и была ли эта фиксация включена в релиз. это служит хорошим примером правильного использования rebase.</p>

<p>многие ответы здесь говорят, что слияние превращает все ваши коммиты в один, и поэтому предлагают использовать rebase для сохранения ваших коммитов. <strong> это неверно. и плохая идея, если вы уже выдвинули свои коммиты </strong>.</p><p>слияние <em> не </em> стирает ваши коммиты. слияние сохраняет историю! (просто посмотрите на gitk) rebase переписывает историю, что является плохой вещью после того, как вы <em> нажали </em> ее.</p><p><strong> используйте merge, а не rebase </strong> всякий раз, когда вы уже нажали.</p><p> <a href="http://thread.gmane.org/gmane.comp.video.dri.devel/34739/focus=34744">вот линус '(автор git) возьми это на себя</a> . это действительно хорошее чтение. или вы можете прочитать мою собственную версию той же идеи ниже.</p><p>перебазирование ветки на мастера:</p><ul> <li>дает неверное представление о том, как были созданы коммиты</li> <li>загрязняет хозяина кучей промежуточных коммитов, которые, возможно, не были хорошо протестированы</li> <li>может на самом деле вводить разрывы сборки для этих промежуточных коммитов из-за изменений, которые были внесены в мастерскую между тем, когда исходная ветка темы была создана и когда она была перебазирована.</li> <li>затрудняет поиск хороших мест в мастере для оформления заказа.</li> <li>заставляет временные метки коммитов не выравниваться с их хронологическим порядком в дереве. таким образом, вы увидите, что коммит a предшествует коммиту b в master, но коммит b был создан первым. (какие?!)</li> <li>создает больше конфликтов, потому что отдельные коммиты в ветке темы могут включать конфликты слияния, которые должны быть разрешены индивидуально (дальнейшая история о том, что происходило в каждом коммите).</li> <li>это переписать историю. если перебазируемая ветвь была перенесена куда-либо (предоставлена кому-либо, кроме вас), то вы облажались с каждым, у кого есть эта ветка, с тех пор, как вы переписали историю.</li> </ul><p>напротив, объединение ветки темы в мастер:</p><ul> <li>сохраняет историю создания веток темы, включая любые слияния от главной ветки к теме, чтобы поддерживать ее актуальность. вы действительно получите точное представление о том, с каким кодом работал разработчик, когда создавал.</li> <li>master - это ветвь, состоящая в основном из слияний, и каждый из этих коммитов слияния, как правило, является "хорошей точкой" в истории, которую можно безопасно проверить, потому что именно там ветка темы была готова для интеграции.</li> <li>все отдельные коммиты ветки темы сохраняются, в том числе тот факт, что они были в ветке темы, поэтому изоляция этих изменений является естественной, и вы можете углубиться в нее при необходимости.</li> <li>конфликты слияния должны быть разрешены только один раз (в точке слияния), поэтому изменения промежуточной фиксации, сделанные в ветке темы, не должны решаться независимо.</li> <li>можно сделать несколько раз плавно. если вы периодически интегрируете ветку своей темы в мастеринг, люди могут продолжать строить ветку тем и объединять ее независимо.</li> </ul>

<p>слияние означает: создать один новый коммит, который объединит мои изменения в место назначения.</p><p>rebase означает: создать новую серию коммитов, используя мой текущий набор коммитов в качестве подсказок. другими словами, посчитайте, как бы выглядели мои изменения, если бы я начал их вносить с того момента, к которому я перебираю. таким образом, после перебазирования вам может потребоваться повторно протестировать ваши изменения, и во время перебазирования у вас может возникнуть несколько конфликтов.</p><p>учитывая это, почему бы вам сделать ребаз? просто чтобы история развития была понятной. допустим, вы работаете над функцией x, и когда вы закончите, вы объедините свои изменения. у места назначения теперь будет один коммит, который будет что-то говорить в духе "добавленной функции x". теперь, вместо слияния, если вы перебазировали, а затем слили, целевая история разработки будет содержать все отдельные коммиты в одной логической последовательности. это значительно упрощает последующее рассмотрение изменений. представьте, как трудно было бы просмотреть историю разработки, если бы 50 разработчиков постоянно объединяли различные функции.</p><p>тем не менее, если вы уже выдвинули ветку, над которой работаете, вам не нужно делать ребаз, а вместо этого объединять. для веток, которые не были переданы вверх по потоку, выполните ребазирование, тестирование и объединение.</p><p>в другой раз вы, возможно, захотите сделать ребазинг, когда вы хотите избавиться от коммитов из вашей ветки, прежде чем переходить вверх по течению. например: коммиты, которые вводят некоторый код отладки на ранних этапах, а другие коммиты в дальнейшем очищают этот код. единственный способ сделать это - выполнить интерактивную перебазировку: <code>git rebase -i <branch/commit/tag></code></p><p>обновление: вы также хотите использовать rebase, когда используете git для взаимодействия с системой контроля версий, которая не поддерживает нелинейную историю (например, subversion). при использовании моста git-svn очень важно, чтобы изменения, которые вы объединяете обратно в subversion, представляли собой последовательный список изменений поверх самых последних изменений в транке. есть только два способа сделать это: (1) заново создать изменения вручную и (2) использовать команду rebase, что намного быстрее.</p><p>обновление 2: еще один способ думать о перебазировании заключается в том, что он обеспечивает своего рода сопоставление вашего стиля разработки со стилем, принятым в репозитории, к которому вы привязываетесь. допустим, вам нравится совершать небольшие, маленькие кусочки. у вас есть один коммит, чтобы исправить опечатку, один коммит, чтобы избавиться от неиспользуемого кода и так далее. к тому времени, как вы закончите то, что вам нужно сделать, у вас будет длинный ряд коммитов. теперь давайте предположим, что репозиторий, который вы делаете, поощряет большие коммиты, поэтому для работы, которую вы делаете, можно ожидать один или, может быть, два коммита. как вы берете свою строку коммитов и сжимаете их до ожидаемого? вы бы использовали интерактивную перебазировку и разделили свои крошечные коммиты на меньшее количество больших кусков. то же самое верно, если нужно было обратное - если ваш стиль был несколькими большими коммитами, но репо требовало длинных строк небольших коммитов. вы должны использовать ребаз для этого. если вы слились вместо этого, вы теперь перенесли свой стиль коммитов в основной репозиторий. если разработчиков много, вы можете себе представить, как трудно было бы проследить историю с несколькими различными стилями фиксации через некоторое время.</p><p>обновление3: <code>does one still need to merge after a successful rebase?</code> да, вы делаете. причина в том, что ребаз по сути включает в себя "сдвиг" коммитов. как я уже говорил выше, эти коммиты рассчитываются, но если у вас было 14 коммитов с точки ветвления, то при условии, что с вашей перебазировкой все в порядке, вы будете на 14 коммитов вперед (от точки, на которую вы перебазируете) после ребаз сделан. у вас была ветка перед ребазой. у вас будет ветвь такой же длины после. вам все еще нужно объединиться, прежде чем публиковать свои изменения. другими словами, сделайте ребаз столько раз, сколько хотите (опять же, только если вы не выдвинули свои изменения вверх по течению). объединять только после того, как вы сделаете ребаз.</p>

<p>до слияния/ребазирования:</p><pre><code>a <- b <- c [master] ^ \ d <- e [branch] </code></pre><p>после <code>git merge master</code>:</p><pre><code>a <- b <- c ^ ^ \ \ d <- e <- f </code></pre><p>после <code>git rebase master</code>:</p><pre><code>a <- b <- c <- d' <- e' </code></pre><p>(a, b, c, d, e и f являются коммитами)</p><p>этот пример и гораздо более хорошо иллюстрированная информация о git можно найти здесь: <a href="http://excess.org/article/2008/07/ogre-git-tutorial/">http://excess.org/article/2008/07/ogre-git-tutorial/</a> </p>

<p>хотя объединение, безусловно, самый простой и распространенный способ интеграции изменений, он не единственный: <strong> <em> rebase </em> </strong> является альтернативным средством интеграции.</p><p><strong> понимание слияния лучше </strong></p><p>когда git выполняет слияние, он ищет три коммита:</p><ul> <li>(1) общий предковый коммит если вы следите за историей двух ветвей в проекте, у них всегда есть хотя бы одна общая фиксация: на данный момент обе ветви имели одинаковое содержание, а затем развивались по-разному.</li> <li>(2) + (3) конечные точки каждой ветви цель интеграции - объединить текущие состояния двух ветвей. поэтому их соответствующие последние редакции представляют особый интерес. объединение этих трех коммитов приведет к интеграции, к которой мы стремимся.</li> </ul><p><strong> fast forward или merge commit </strong></p><p>в очень простых случаях одна из двух ветвей не имеет никаких новых коммитов с тех пор, как произошло ветвление - ее последний коммит все еще остается общим предком.</p><p> <a href="https://i.stack.imgur.com/57wt0.gif"><img src="https://i.stack.imgur.com/57wt0.gif" alt="enter image description here"></a> </p><p>в этом случае выполнить интеграцию очень просто: git может просто добавить все коммиты другой ветки поверх коммитов общего предка. в git эта простейшая форма интеграции называется слиянием "ускоренной перемотки вперед". затем обе ветви имеют одинаковую историю.</p><p> <a href="https://i.stack.imgur.com/f1vtv.gif"><img src="https://i.stack.imgur.com/f1vtv.gif" alt="enter image description here"></a> </p><p>однако во многих случаях обе ветви двигались вперед по отдельности. <a href="https://i.stack.imgur.com/glagx.gif"><img src="https://i.stack.imgur.com/glagx.gif" alt="enter image description here"></a> </p><p>чтобы сделать интеграцию, git должен будет создать новый коммит, содержащий различия между ними - коммит слияния.</p><p> <a href="https://i.stack.imgur.com/e3xje.gif"><img src="https://i.stack.imgur.com/e3xje.gif" alt="enter image description here"></a> </p><p><strong> human commits и merge commits </strong></p><p>обычно коммит тщательно создается человеком. это значимый модуль, который оборачивает только связанные изменения и комментирует их.</p><p>коммит слияния немного отличается: вместо того, чтобы быть созданным разработчиком, он создается git автоматически. и вместо того, чтобы оборачивать набор связанных изменений, его цель - соединить две ветви, как узел. если вы хотите понять операцию слияния позже, вам нужно взглянуть на историю обеих ветвей и соответствующий граф коммитов.</p><p><strong> интеграция с rebase </strong></p><p><em> некоторые люди предпочитают обходиться без таких автоматических коммитов слияния. вместо этого они хотят, чтобы история проекта выглядела так, как будто он развивался по одной прямой линии. </em> не осталось никаких признаков того, что он был разделен на несколько веток в какой-то момент.</p><p> <a href="https://i.stack.imgur.com/b1nud.gif"><img src="https://i.stack.imgur.com/b1nud.gif" alt="enter image description here"></a> </p><p>давайте шаг за шагом пройдемся по операции rebase. сценарий такой же, как и в предыдущих примерах: мы хотим интегрировать изменения из ветви b в ветку a, но теперь с помощью rebase.</p><p> <a href="https://i.stack.imgur.com/az5lo.gif"><img src="https://i.stack.imgur.com/az5lo.gif" alt="enter image description here"></a> </p><p>мы сделаем это в три этапа</p><ol> <li><code>git rebase branch-a // syncs the history with branch-a</code></li> <li><code>git checkout branch-a // change the current branch to branch-a</code></li> <li><code>git merge branch-b // merge/take the changes from branch-b to branch-a</code></li> </ol><p>во-первых, git "отменит" все коммиты на ветви-a, которые произошли после того, как линии начали ветвиться (после коммитов общего предка). однако, конечно, он не откажется от них: вместо этого вы можете думать об этих коммитах как о "временно спасенных".</p><p> <a href="https://i.stack.imgur.com/to3iq.gif"><img src="https://i.stack.imgur.com/to3iq.gif" alt="enter image description here"></a> </p><p>затем он применяет коммиты из branch-b, которые мы хотим интегрировать. на данный момент обе ветви выглядят одинаково.</p><p> <a href="https://i.stack.imgur.com/vrfmo.gif"><img src="https://i.stack.imgur.com/vrfmo.gif" alt="enter image description here"></a> </p><p>на последнем шаге новые коммиты в ответвлении a теперь повторно применяются - но на новой позиции, поверх интегрированных коммитов из ответвления b (они переоснованы). <strong> результат выглядит так, как будто развитие произошло по прямой линии. вместо фиксации слияния, содержащей все объединенные изменения, была сохранена исходная структура фиксации. </strong></p><p> <a href="https://i.stack.imgur.com/4ikl0.gif"><img src="https://i.stack.imgur.com/4ikl0.gif" alt="enter image description here"></a> </p><p>наконец, вы получаете чистую ветвь <strong> branch-a </strong> без нежелательных и автоматически генерируемых коммитов.</p><p><strong> примечание: </strong> взято с удивительного <a href="https://www.git-tower.com/learn/git/ebook/en/desktop-gui/advanced-topics/rebase">post</a> <a href="https://www.git-tower.com/windows/"><code>git-tower</code></a> . <em> недостатки </em> <code>rebase</code> также хорошо читаются в том же посте.</p>

<p>это предложение получает это:</p><blockquote> <p>в общем, способ получить лучшее из обоих миров состоит в том, чтобы перебазировать локальные изменения, которые вы внесли, но еще не поделились, прежде чем нажать на них, чтобы очистить свою историю, но никогда не перебрасывайте то, что вы где-то выдвинули.</p> </blockquote><p>источник: <a href="http://www.git-scm.com/book/en/v2/git-branching-rebasing#rebase-vs.-merge">http://www.git-scm.com/book/en/v2/git-branching-rebasing#rebase-vs.-merge</a> </p>

<p>этот ответ широко ориентирован на <a href="http://nvie.com/posts/a-successful-git-branching-model/">git flow</a> . таблицы были сгенерированы с помощью nice <a href="https://ozh.github.io/ascii-tables/">ascii table generator</a> и деревьев истории с помощью этой замечательной команды ( <a href="https://githowto.com/aliases">с псевдонимом</a> as <code>git lg</code>):</p><pre class="lang-none prettyprint-override"><code>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)' </code></pre><p>таблицы расположены в обратном хронологическом порядке, чтобы быть более совместимыми с деревьями истории. сначала посмотрите также разницу между <code>git merge</code> и <code>git merge --no-ff</code> (обычно вы хотите использовать <code>git merge --no-ff</code>, так как ваша история выглядит ближе к реальности):</p><h3><code>git merge</code></h3><p>команды:</p><pre class="lang-none prettyprint-override"><code>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" </code></pre><p>результат:</p><pre class="lang-none prettyprint-override"><code>* 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 </code></pre><h3><code>git merge --no-ff</code></h3><p>команды:</p><pre class="lang-none prettyprint-override"><code>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" </code></pre><p>результат:</p><pre class="lang-none prettyprint-override"><code>* 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 </code></pre><hr><h1><code>git merge</code> vs <code>git rebase</code></h1><p>первое замечание: <strong> всегда объединяйте функции в разработке, никогда не перебирайте разработки из функций </strong>. это является следствием <a href="https://www.atlassian.com/git/tutorials/merging-vs-rebasing#the-golden-rule-of-rebasing">золотое правило перебазирования</a> :</p><blockquote> <p>золотое правило <code>git rebase</code> - никогда не использовать его в <em> публичных </em> ветвях.</p> </blockquote><p> <a href="https://git-scm.com/book/en/v2/git-branching-rebasing">другими словами</a> :</p><blockquote> <p>никогда не опровергайте ничего, что вы куда-то толкнули.</p> </blockquote><p>я бы лично добавил: <em>, если это не ветка функций, а вы и ваша команда не знаете о последствиях </em>.</p><p>таким образом, вопрос <code>git merge</code> vs <code>git rebase</code> применим почти только к ветвям объектов (в следующих примерах <code>--no-ff</code> всегда использовался при объединении). обратите внимание, что, поскольку я не уверен, что есть одно лучшее решение ( <a href="https://www.atlassian.com/git/articles/git-team-workflows-merge-or-rebase">дискуссия существует</a> ), я лишь расскажу, как ведут себя обе команды. в моем случае я предпочитаю использовать <code>git rebase</code>, поскольку он создает более красивое дерево истории :)</p><h2>между функциональными ветвями</h2><h3><code>git merge</code></h3><p>команды:</p><pre class="lang-none prettyprint-override"><code>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" </code></pre><p>результат:</p><pre class="lang-none prettyprint-override"><code>* 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 </code></pre><h3><code>git rebase</code></h3><p>команды:</p><pre class="lang-none prettyprint-override"><code>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" </code></pre><p>результат:</p><pre class="lang-none prettyprint-override"><code>* 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 </code></pre><h2>от <code>develop</code> к функциональной ветви</h2><h3><code>git merge</code></h3><p>команды:</p><pre class="lang-none prettyprint-override"><code>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" </code></pre><p>результат:</p><pre class="lang-none prettyprint-override"><code>* 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 </code></pre><h3><code>git rebase</code></h3><p>команды:</p><pre class="lang-none prettyprint-override"><code>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" </code></pre><p>результат:</p><pre class="lang-none prettyprint-override"><code>* 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 </code></pre><hr><h1>примечания стороны</h1><h3><code>git cherry-pick</code></h3><p>когда вам нужен только один конкретный коммит, <code>git cherry-pick</code> - это решение nice (опция <code>-x</code> добавляет строку с надписью "<em> (вишня выбрана из коммита ...) </em>" к исходному сообщению коммита body, поэтому, как правило, неплохо использовать его - <code>git log <commit\_sha1></code>, чтобы увидеть его):</p><p>команды:</p><pre class="lang-none prettyprint-override"><code>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" </code></pre><p>результат:</p><pre class="lang-none prettyprint-override"><code>* 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 </code></pre><h3><code>git pull --rebase</code></h3><p>не уверен, что могу объяснить это лучше, чем <a href="https://www.derekgourlay.com/blog/git-when-to-merge-vs-when-to-rebase/">дерек гурлэй</a> ... по сути, используйте <code>git pull --rebase</code> вместо <code>git pull</code> :) однако в статье не хватает того, что <a href="https://coderwall.com/p/tnoiug/rebase-by-default-when-doing-git-pull">вы можете включить его по умолчанию</a> знак равно</p><pre class="lang-none prettyprint-override"><code>git config --global pull.rebase true </code></pre><h3><code>git rerere</code></h3><p>опять же, хорошо объяснил <a href="https://git-scm.com/blog/2010/03/08/rerere.html">здесь</a> . но попросту говоря, если вы включите его, вам больше не придется разрешать один и тот же конфликт несколько раз.</p>

<p>книга про git в качестве действительно хорошего объяснения на <a href="http://git-scm.com/book/en/git-branching-rebasing">страница перебазирования</a> .</p><p>в основном слияние займет 2 коммита и объединит их.</p><p>ребаз будет идти к общему предку на 2 и постепенно применять изменения друг на друге. это делает "чище" и более линейной истории.</p><p>но когда вы делаете ребазинг, вы отказываетесь от предыдущих коммитов и создаете новые. таким образом, вы никогда не должны отказываться от публичного репо. другие люди, работающие в репо, будут ненавидеть вас.</p><p>только по этой причине я почти исключительно сливаюсь. в 99% случаев мои ветви не сильно отличаются друг от друга, поэтому, если возникают конфликты, это только в одном или двух местах.</p>

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

<p>некоторые практические примеры, в некоторой степени связанные с крупномасштабной разработкой, где gerrit используется для обзора и интеграции поставки.</p><p>я сливаюсь, когда поднимаю свою ветвь функций на новый удаленный мастер. это дает минимальную работу по подъему и легко отслеживать историю развития функции, например, в gitk.</p><pre><code>git fetch git checkout origin/my\_feature git merge origin/master git commit git push origin head:refs/for/my\_feature </code></pre><p>я объединяюсь, когда готовлю коммит доставки.</p><pre><code>git fetch git checkout origin/master git merge --squash origin/my\_feature git commit git push origin head:refs/for/master </code></pre><p>я перезагружаюсь, когда моя фиксация доставки не проходит интеграцию по любой причине, и мне нужно обновить ее до нового удаленного мастера.</p><pre><code>git fetch git fetch <gerrit link> git checkout fetch\_head git rebase origin/master git push origin head:refs/for/master </code></pre>

<p>много раз объяснялось, что такое ребаз и что такое слияние, но когда и что использовать?</p><p><strong>когда использовать rebase?</strong></p><ul> <li>когда вы не нажали ветку/над ней никто больше не работает</li> <li>ты хочешь полную историю</li> <li>вы хотите избежать всех автоматически сгенерированных сообщений "слияния .."</li> </ul><p>как git rebase меняет историю. поэтому вы не должны использовать его, когда кто-то еще работает над той же веткой/если вы нажали ее. но если у вас есть локальная ветвь, вы можете выполнить мастер слияния ребаз перед слиянием своей ветки с главной, чтобы сохранить более чистую историю. делая это, после слияния с мастер-веткой не будет видно, что вы использовали ветку в мастер-ветке - история "чище", так как у вас нет автоматически сгенерированного "слияния ..", но все еще есть полный история в вашей главной ветке без автоматической генерации коммитов "merged ..". тем не менее, убедитесь, что вы используете <code>git merge feature-branch --ff-only</code>, чтобы избежать конфликтов при создании одного коммита при слиянии вашей функции с основной. <strong>это интересно, если вы используете ветви функций для каждой задачи, над которой работаете, так как вы получаете историю ветви функций, но не коммит "объединено .."</strong></p><p>второй сценарий будет, если вы разветвляетесь из ветки и хотите знать, что изменилось в основной ветке. rebase дает вам информацию, поскольку она включает в себя каждый коммит.</p><p><strong>когда использовать слияние?</strong></p><ul> <li>когда вы нажали на ветку/другие тоже работают над этим</li> <li>вам не нужна полная история</li> <li>достаточно просто слияния</li> </ul><p>когда вам не нужно или вы хотите иметь всю историю ветви функций в вашей основной ветке или если другие работают в той же ветке/вы добавили ее. если вы все еще хотите иметь историю, просто объедините мастер с ветвью объектов, прежде чем объединять ветвь объектов с мастером. это приведет к ускоренному слиянию, при котором у вас будет история ветви объектов в вашем мастере (включая коммит слияния, который был в вашей ветви функций, потому что вы слили мастер в него).</p>