Когда рекомендуется использовать Git rebase против Git merge?
Нужно ли объединять после успешной перезагрузки?
Когда рекомендуется использовать Git rebase против Git merge?
Нужно ли объединять после успешной перезагрузки?
Ответы:
Итак, когда вы используете один из них?
init
новым репо, add
файлом и commit
. Оформить заказ на новую ветвь функции ( checkout -b feature
.) Измените текстовый файл, подтвердите и повторите так, чтобы в ветви функции было две новые фиксации. Тогда checkout master
и merge feature
. В log
, я вижу мой первоначальный коммит на master, а затем два, которые были объединены из feature. Если вы merge --squash feature
, функция объединена с мастером, но не зафиксирована, поэтому единственным новым коммитом на мастере будет тот, который вы сделаете сами.
Это просто. С 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
- как это плохо?
Sean Schofield
добавляет это в комментарии: «Перебазировка также хороша, потому что как только вы в конечном итоге объединяете свои вещи обратно в master (что, как уже было описано, тривиально), вы сидите на вершине истории ваших коммитов. проекты, в которых функции могут быть написаны, но слиты через несколько недель, вы не хотите просто объединять их с мастером, потому что они «втыкаются» в мастер-путь назад в историю. Лично мне нравится иметь возможность делать git log и видеть эта недавняя функция прямо вверху. Обратите внимание, что даты фиксации сохраняются - ребаз не меняет эту информацию.
merge
, rebase
, fast-forward
и т.д.) , имеют в виду конкретных манипуляций направленного ациклического графа. Им становится легче рассуждать с этой мысленной моделью.
В дополнение к моему собственному ответу, упомянутому TSamper ,
Перед слиянием довольно часто хорошая идея сделать это, потому что идея заключается в том, что вы интегрируете в свою ветвь Y
работу ветви B
, в которую вы будете сливаться.
Но, опять же, перед объединением вы разрешаете любой конфликт в вашей ветви (то есть: «rebase», как в «воспроизвести мою работу в моей ветви, начиная с недавней точки из ветви B
).
Если все сделано правильно, последующее объединение из вашей ветви в ветка B
может быть перемотка вперед.
слияние напрямую влияет на целевую ветвь B
, что означает, что слияния лучше быть тривиальными, в противном случае эта ветвь B
может быть долгой, чтобы вернуться в стабильное состояние (время для разрешения всех конфликтов)
точка слияния после ребазирования?
В случае, который я описываю, я перехожу B
на свою ветку, просто чтобы иметь возможность воспроизвести мою работу с более поздней точки B
, но, оставаясь в моей ветке.
В этом случае, объединение все еще необходимо, чтобы перенести мою «переигранную» работу B
.
Другой сценарий ( описанный, например, в Git Ready ), заключается в непосредственном B
переносе вашей работы через ребаз (который сохраняет все ваши хорошие коммиты или даже дает вам возможность переупорядочить их через интерактивный ребаз).
В этом случае (когда вы перебазируете, находясь в ветке B), вы правы: дальнейшее слияние не требуется:
Git-дерево по умолчанию, когда мы не слили и не перебазировали
мы получаем путем ребазинга:
Этот второй сценарий полностью посвящен тому, как вернуть новую функцию в мастер.
Моя точка зрения, описывая первый сценарий перебазирования, состоит в том, чтобы напомнить всем, что перебазировка также может быть использована в качестве предварительного шага к этому (то есть, «получить новую функцию обратно в мастер»).
Вы можете использовать rebase, чтобы сначала ввести master "в" ветке новой функции: rebase будет воспроизводить коммиты новой функции из HEAD master
, но все еще в ветке новой функции, эффективно перемещая начальную точку ветки со старой основной фиксации в HEAD-master
.
Это позволяет вам разрешать любые конфликты в вашей ветви (то есть изолированно, в то же время позволяя master продолжать развиваться параллельно, если этап разрешения конфликтов занимает слишком много времени).
Затем вы можете переключиться на мастер и объединить new-feature
(или перебазировать new-feature
на, master
если вы хотите сохранить коммиты, сделанные в вашемnew-feature
филиал).
Так:
master
.Если у вас есть какие-либо сомнения, используйте слияние.
Единственные различия между ребазом и слиянием:
Таким образом, краткий ответ заключается в том, чтобы выбрать перебазирование или слияние в зависимости от того, как вы хотите, чтобы ваша история выглядела .
Есть несколько факторов, которые вы должны учитывать при выборе операции.
Если это так, не перебазируйте. Rebase уничтожает ветку, и эти разработчики будут иметь поврежденные / несовместимые репозитории, если они не используют git pull --rebase
. Это хороший способ быстро расстроить других разработчиков.
Ребаз это разрушительная операция. Это означает, что если вы не примените его правильно, вы можете потерять совершенную работу и / или нарушить целостность репозиториев других разработчиков.
Я работал в командах, в которых все разработчики пришли из того времени, когда компании могли позволить себе выделенный персонал, чтобы заниматься ветвлением и слиянием. Эти разработчики мало знают о Git и не хотят много знать. В этих командах я бы ни за что не рискнул порекомендовать перебазирование.
Некоторые команды используют модель ветвления на функцию, где каждая ветвь представляет функцию (или исправление, или подфункцию и т. Д.). В этой модели ветвь помогает идентифицировать наборы связанных коммитов. Например, можно быстро отменить функцию, отменив объединение этой ветви (если честно, это редкая операция). Или отличить особенность, сравнивая две ветви (чаще). Rebase уничтожит ветку, и это не будет простым.
Я также работал над командами, которые использовали модель ветвления на разработчика (мы все были там). В этом случае сама ветка не передает никакой дополнительной информации (в коммите уже есть автор). Там не будет никакого вреда в перебазировании.
Возврат (как при отмене) перебазирования является значительно трудным и / или невозможным (если у перебазирования были конфликты) по сравнению с возвратом слияния. Если вы считаете, что есть шанс вернуться, используйте merge.
Операции по перебазировке нужно подтягивать с соответствующими git pull --rebase
. Если вы работаете самостоятельно, вы можете вспомнить, какой из них следует использовать в соответствующее время. Если вы работаете в команде, это будет очень трудно координировать. Вот почему большинство рабочих процессов rebase рекомендуют использовать rebase для всех слияний (и git pull --rebase
для всех операций извлечения ).
Предполагая, что у вас есть следующее слияние:
B -- C
/ \
A--------D
Некоторые люди утверждают, что слияние «уничтожает» историю коммитов, потому что если бы вы смотрели журнал только основной ветки (A - D), вы пропустили бы важные сообщения коммитов, содержащиеся в B и C.
Если бы это было правдой, у нас не было бы таких вопросов . По сути, вы увидите B и C, если явно не попросите их не видеть (используя --first-parent). Это очень легко попробовать для себя.
Два подхода объединяются по-разному, но не ясно, что один всегда лучше другого, и это может зависеть от рабочего процесса разработчика. Например, если разработчик имеет тенденцию совершать регулярные коммиты (например, может быть, они совершают коммиты два раза в день, когда переходят с работы на дом), тогда для данной ветви может быть много коммитов. Многие из этих коммитов могут не выглядеть как конечный продукт (я склонен к рефакторингу своего подхода один или два раза для каждой функции). Если кто-то еще работает над соответствующей областью кода и пытается отменить мои изменения, это может быть довольно утомительной операцией.
Если вам нравится использовать псевдоним, rm
чтобы rm -rf
«сэкономить время», то, возможно, ребаз для вас.
Я всегда думаю, что когда-нибудь я столкнусь со сценарием, в котором Git rebase является отличным инструментом, который решает проблему. Как и я думаю, я столкнусь со сценарием, в котором Git reflog является отличным инструментом, который решает мою проблему. Я работал с Git более пяти лет. Этого не случилось.
Грязные истории никогда не были для меня проблемой. Я никогда не читаю историю коммитов, как захватывающий роман. Большую часть времени мне нужна история, я все равно собираюсь использовать вину Git или Git bisect. В этом случае наличие коммита слияния на самом деле полезно для меня, потому что, если слияние создало проблему, это значимая информация для меня.
Я чувствую себя обязанным упомянуть, что лично смягчил использование rebase, хотя мой общий совет остается в силе. Недавно я много общался с проектом Angular 2 Material . Они использовали rebase для сохранения очень чистой истории коммитов. Это позволило мне очень легко увидеть, что фиксация исправила данный дефект и была ли эта фиксация включена в релиз. Это служит хорошим примером правильного использования rebase.
Многие ответы здесь говорят, что слияние превращает все ваши коммиты в один, и поэтому предлагают использовать rebase для сохранения ваших коммитов. Это неверно И плохая идея, если вы уже выдвинули свои коммиты .
Слияние не стирает ваши коммиты. Слияние сохраняет историю! (просто посмотрите на gitk) Rebase переписывает историю, что является плохой вещью после того, как вы ее подтолкнули .
Используйте слияние - не делайте ребаз, когда вы уже нажали.
Вот что сделал Линус (автор Git) (теперь размещенный в моем блоге, найденный Wayback Machine ). Это действительно хорошее чтение.
Или вы можете прочитать мою собственную версию той же идеи ниже.
Перебазирование ветки на мастера:
Напротив, объединение ветки темы в мастер:
TLDR: это зависит от того, что является самым важным - аккуратная история или истинное представление последовательности развития
Если аккуратная история является наиболее важной, то сначала нужно выполнить перебазирование, а затем объединить свои изменения, чтобы точно было понятно, что представляет собой новый код. Если вы уже выдвинули свою ветку, не делайте ребаз, если не можете справиться с последствиями.
Если истинное представление последовательности является наиболее важным, вы бы слились без перебазирования.
Слияние означает: создать один новый коммит, который объединит мои изменения в место назначения. Примечание. У этого нового коммита будет два родителя - последний коммит из вашей строки коммитов и последний коммит другой ветви, которую вы объединяете.
Rebase означает: создать новую серию коммитов, используя мой текущий набор коммитов в качестве подсказок. Другими словами, посчитайте, как бы выглядели мои изменения, если бы я начал их вносить с того момента, к которому я перебираю. Таким образом, после перебазирования вам может потребоваться повторно протестировать ваши изменения, и во время перебазирования у вас может возникнуть несколько конфликтов.
Учитывая это, почему бы вам сделать ребаз? Просто чтобы история развития была понятной. Допустим, вы работаете над функцией X, и когда вы закончите, вы объедините свои изменения. У места назначения теперь будет один коммит, который будет что-то говорить в духе «Добавленной функции X». Теперь, вместо слияния, если вы перебазировали, а затем слили, целевая история разработки будет содержать все отдельные коммиты в одной логической последовательности. Это значительно упрощает последующее рассмотрение изменений. Представьте, как трудно было бы просмотреть историю разработки, если бы 50 разработчиков постоянно объединяли различные функции.
Тем не менее, если вы уже выдвинули ветку, над которой работаете, вам не нужно делать ребаз, а вместо этого объединять. Для веток, которые не были переданы вверх по потоку, выполните ребазирование, тестирование и объединение.
В другой раз вы, возможно, захотите сделать ребазинг, когда вы хотите избавиться от коммитов из вашей ветки, прежде чем переходить вверх по течению. Например: коммиты, которые вводят некоторый код отладки на ранних этапах, а другие коммиты в дальнейшем очищают этот код. Единственный способ сделать это - выполнить интерактивную ребазирование:git rebase -i <branch/commit/tag>
ОБНОВЛЕНИЕ: Вы также хотите использовать rebase, когда используете Git для взаимодействия с системой контроля версий, которая не поддерживает нелинейную историю ( например, Subversion ). При использовании моста git-svn очень важно, чтобы изменения, которые вы объединяете обратно в Subversion, представляли собой последовательный список изменений поверх самых последних изменений в транке. Есть только два способа сделать это: (1) заново создать изменения вручную и (2) использовать команду rebase, что намного быстрее.
ОБНОВЛЕНИЕ 2: Еще один способ думать о перебазировании заключается в том, что он обеспечивает своего рода сопоставление вашего стиля разработки со стилем, принятым в репозитории, к которому вы привязываетесь. Допустим, вам нравится совершать небольшие, маленькие кусочки. У вас есть один коммит, чтобы исправить опечатку, один коммит, чтобы избавиться от неиспользуемого кода и так далее. К тому времени, как вы закончите то, что вам нужно сделать, у вас будет длинный ряд коммитов. Теперь предположим, что репозиторий, который вы делаете, поощряет большие коммиты, поэтому для работы, которую вы делаете, можно ожидать один или, может быть, два коммита. Как вы берете свою строку коммитов и сжимаете их до ожидаемого? Вы бы использовали интерактивную перебазировку и разделили свои крошечные коммиты на меньшее количество больших кусков. То же самое верно, если нужно было обратное - если ваш стиль был несколькими большими коммитами, но хранилище требовало длинных цепочек небольших коммитов. Вы должны использовать ребаз для этого. Если вы вместо этого слились, то теперь вы перенесли свой стиль коммита в основной репозиторий. Если разработчиков много, вы можете себе представить, как трудно было бы проследить историю с несколькими различными стилями фиксации через некоторое время.
ОБНОВЛЕНИЕ 3: Does one still need to merge after a successful rebase?
Да, вы делаете. Причина в том, что ребаз по сути включает в себя «сдвиг» коммитов. Как я уже говорил выше, эти коммиты рассчитываются, но если у вас было 14 коммитов с точки ветвления, то при условии, что с вашей перебазировкой все в порядке, вы будете на 14 коммитов вперед (от точки, на которую вы перебазируете) после ребаз сделан. У вас была ветка перед ребазой. У вас будет ветвь такой же длины после. Вам все еще нужно объединиться, прежде чем публиковать свои изменения. Другими словами, сделайте ребаз столько раз, сколько хотите (опять же, только если вы не выдвинули свои изменения вверх по течению). Объединять только после того, как вы сделаете ребаз.
git merge
поддерживает --no-ff
опцию, которая заставляет его сделать коммит слияния.
Хотя слияние, безусловно, самый простой и распространенный способ интеграции изменений, он не единственный: Rebase является альтернативным средством интеграции.
Понимание, слить лучше
Когда Git выполняет слияние, он ищет три коммита:
Fast-Forward или Merge Commit
В очень простых случаях одна из двух ветвей не имеет никаких новых коммитов с тех пор, как произошло ветвление - ее последний коммит все еще остается общим предком.
В этом случае выполнить интеграцию очень просто: Git может просто добавить все коммиты другой ветки поверх коммитов общего предка. В Git эта простейшая форма интеграции называется слиянием «ускоренной перемотки вперед». Затем обе ветви имеют одинаковую историю.
Однако во многих случаях обе ветви двигались вперед по отдельности.
Чтобы сделать интеграцию, Git должен будет создать новый коммит, содержащий различия между ними - коммит слияния.
Human Commits & Merge Commits
Обычно коммит тщательно создается человеком. Это значимый модуль, который оборачивает только связанные изменения и комментирует их.
Коммит слияния немного отличается: вместо того, чтобы быть созданным разработчиком, он создается Git автоматически. И вместо того, чтобы оборачивать набор связанных изменений, его цель - соединить две ветви, как узел. Если вы хотите понять операцию слияния позже, вам нужно взглянуть на историю обеих ветвей и соответствующий граф коммитов.
Интеграция с Rebase
Некоторые люди предпочитают обходиться без таких автоматических коммитов слияния. Вместо этого они хотят, чтобы история проекта выглядела так, как если бы она развивалась по одной прямой линии. Не осталось никаких признаков того, что он был разделен на несколько веток в какой-то момент.
Давайте шаг за шагом пройдемся по операции 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, которые произошли после того, как линии начали ветвиться (после коммитов общего предка). Однако, конечно, он не откажется от них: вместо этого вы можете думать об этих коммитах как о «временно спасенных».
Затем он применяет коммиты из Branch-B, которые мы хотим интегрировать. На данный момент обе ветви выглядят одинаково.
На последнем шаге новые коммиты в ответвлении A теперь повторно применяются - но на новой позиции, поверх интегрированных коммитов из ответвления B (они переоснованы).
Результат выглядит так, будто развитие произошло по прямой линии. Вместо фиксации слияния, которая содержит все объединенные изменения, была сохранена исходная структура фиксации.
Наконец, вы получаете чистую ветвь A без нежелательных и автоматически генерируемых коммитов.
Примечание: Взято из удивительном поста по git-tower
. В минусы от rebase
также хорошо прочитать в той же должности.
Перед слиянием / перебазированием:
A <- B <- C [master]
^
\
D <- E [branch]
После git merge master
:
A <- B <- C
^ ^
\ \
D <- E <- F
После git rebase master
:
A <- B <- C <- D' <- E'
(A, B, C, D, E и F являются коммитами)
Этот пример и гораздо более наглядная информация о Git можно найти в 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
против git rebase
Первый момент: всегда объединяйте функции в разработке, никогда не изменяйте разработку из функций . Это следствие золотого правила перебазирования :
Золотое правило
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 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
Опять же, хорошо объяснил здесь . Проще говоря, если вы включите его, вам больше не придется разрешать один и тот же конфликт несколько раз.
Книга Pro Git имеет действительно хорошее объяснение на странице перебазирования .
По сути, слияние займет две фиксации и объединит их.
Ребаз будет идти к общему предку на двоих и постепенно применять изменения друг на друге. Это делает «чище» и более линейной истории.
Но когда вы делаете ребаз, вы отказываетесь от предыдущих коммитов и создаете новые. Поэтому вы никогда не должны перебазировать репозиторий, который является публичным. Другие люди, работающие над хранилищем, будут ненавидеть вас.
Только по этой причине я почти исключительно сливаюсь. В 99% случаев мои ветви мало чем отличаются, поэтому, если возникают конфликты, это только в одном или двух местах.
Git rebase используется для того, чтобы сделать пути ветвления в истории более чистыми и структуру хранилища линейной.
Он также используется для сохранения веток, созданных вами, в частном порядке, так как после удаления и отправки изменений на сервер, если вы удалите свою ветку, не будет никаких свидетельств того, что вы работали над веткой. Так что ваша ветвь теперь ваша местная забота.
После выполнения rebase мы также избавляемся от дополнительного коммита, который мы использовали, чтобы увидеть, выполняем ли мы нормальное слияние.
И да, по-прежнему необходимо выполнять слияние после успешной перебазировки, поскольку команда rebase просто помещает вашу работу поверх ветки, упомянутой вами при перебазировании, скажем, master, и делает первый коммит вашей ветки в качестве прямого потомка ветки master. , Это означает, что теперь мы можем выполнить ускоренное слияние для переноса изменений из этой ветви в основную ветку.
Некоторые практические примеры, в некоторой степени связанные с крупномасштабным развитием, где Геррит используется для обзора и интеграции доставки:
Я объединяюсь, когда поднимаю свою ветвь функций на новый удаленный мастер. Это дает минимальную работу по подъему и легко отслеживать историю развития функции, например, в 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?
Как Git rebase меняет историю. Поэтому вы не должны использовать его, когда кто-то еще работает над той же веткой / если вы нажали ее. Но если у вас есть локальная ветвь, вы можете выполнить мастер слияния ребаз до слияния своей ветки с главной, чтобы сохранить более чистую историю. При этом после слияния с основной ветвью не будет видно, что вы использовали ветку в основной ветке - история «чище», поскольку у вас нет автоматически сгенерированной «слитой ..», но все еще есть полная история в вашей основной ветке без автоматического создания коммитов "слил ..".
Тем не менее, убедитесь, что вы используете, git merge feature-branch --ff-only
чтобы убедиться, что нет конфликтов, создающих единый коммит при объединении вашей функции с основной.Это интересно, если вы используете ветви функций для каждой задачи, над которой работаете, когда вы получаете историю ветви функций, а не коммит "объединенный .."
Второй сценарий будет, если вы разветвляетесь из ветки и хотите знать, что изменилось в основной ветке. Rebase дает вам информацию, поскольку она включает в себя каждый коммит.
Когда вы должны использовать слияние?
Когда вам не нужно или вы хотите иметь всю историю ветви функций в вашей основной ветке или если другие работают в той же ветке / вы добавили ее. Если вы все еще хотите иметь историю, просто объедините мастер с веткой объектов, прежде чем объединять ветку объектов с мастером. Это приведет к быстрому слиянию, в котором у вас есть история ветви объектов в вашем мастере (включая коммит слияния, который был в вашей ветви функций, потому что вы слили мастер в него).
Когда я использую git rebase
? Почти никогда, потому что переписывает историю. git merge
почти всегда предпочтительный выбор, потому что он уважает то, что на самом деле произошло в вашем проекте.