Git pull --rebase --preserve-merges

Краткая версия: Нужно ли сохранять-слияния, только если вы явно слились после локального коммита? Что именно происходит в противном случае? Применяет ли он ваш совершенный код к объединенной ветке?

Пожалуйста, объясните, когда это полезно для git pull --rebase --preserve-merges по сравнению с обычным git pull --rebase? Я прочитал о проблеме с git pull --rebase здесь: http://notes.envato.com/developers/rebasing-merge-commits-in-git/ Это может привести к дублированию изменений кода.

Я читаю здесь: Когда будет` git pull --rebase` заставьте меня беспокоиться?

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

Поэтому я не уверен, что понимаю, когда мне понадобится git pull --rebase --preserve-merges, и если это когда-нибудь будет плохое использование vs. git pull --rebase.

Ответ 1

Технически - и я утверждаю, что это немного глупо от git, pull script (это оболочка script) должна просто сделать это для вас - вам нужно запустить git pull --rebase=preserve, а не пытаться использовать git pull --rebase --preserve-merges. (Или, как я отметил в комментарий на ответе Влада Никитина, вы можете установить branch.name.rebase на preserve, чтобы получить тот же эффект автоматически.)

Другими словами, вы никогда не должны запускать git pull --rebase --preserve-merges, поскольку он (неправильно) передает --preserve-merges на шаг fetch, а не на шаг merge -or- rebase. Однако вы можете запустить git pull --rebase=preserve.

Вопрос о том, когда (и следует ли) использовать какой-либо пересмотр, будь то сохранение слияния или нет, скорее является вопросом мнения. Это означает, что на самом деле это не так хорошо работает в stackoverflow.: -)

Тем не менее, я приведу здесь одно утверждение: вам нужно только переустановить, если вы знаете (в каком-то общем смысле), что вы делаете, 1 и если вы знаете, что делаете, вы, скорее всего, предпочтете сохранение баланса слиянием как общее правило, хотя к тому времени, когда вы решили, что перезагрузка является хорошей идеей, вы, вероятно, обнаружите, что история, в которой есть свои встроенные точки ветвления и слияния, не обязательно правильная "окончательная перезаписанная история".

То есть, если уместно вообще выполнить переустановку, по крайней мере, вполне вероятно, что история, которую нужно переустановить, сама линейна, так что вопрос сохранения-vs-flatten в любом случае является спорным.


Изменить: добавить чертеж

Вот чертеж части графика фиксации, показывающий две названные ветки, mainline и experiment. Общей базой для mainline и experiment является commit node A, а mainline имеет commit G, который не находится в ветки experiment:

...--o--A-------------G   <-- mainline
         \
          \ .-C-.
           B     E--F     <-- experiment
            \_D_/

Обратите внимание, что ветвь experiment также имеет ветвь-и-merge внутри нее: база для этих двух ветвей B, одна ветка содержит commit C, а другая ветка имеет фиксацию D. Эти две (неназванные) ветки сокращаются до одного потока разработки при объединении commit E, а затем commit F сидит на вершине слияния и является концом ветки experiment.

Вот что произойдет, если вы находитесь на experiment и запустите git rebase mainline:

$ git rebase mainline
First, rewinding head to replay your work on top of it...
Applying: B
Applying: C
Applying: D
Applying: F

Вот что теперь в графе фиксации:

...--o--A--G               <-- mainline
            \
             B'-C'-D'-F'   <-- experiment

"Структурная ветвь", которая когда-то была на ветке experiment, исчезла. Операция rebase скопировала все изменения, которые я сделал в commits B, C, D и F; они стали новыми коммитами B', C', D' и F'. (Commit E был чистым слиянием без изменений и не требовал копирования. Я не проверял, что произойдет, если я переупакую слияние со встроенными изменениями, либо для разрешения конфликтов, либо, как некоторые называют его, "злое слияние". )

С другой стороны, если я это сделаю:

$ git rebase --preserve-merges mainline
[git grinds away doing the rebase; this takes a bit longer
than the "flattening" rebase, and there is a progress indicator]
Successfully rebased and updated refs/heads/experiment.

Я получаю этот график вместо:

...--o--A--G               <-- mainline
            \
             \ .-C'.
              B'    E'-F'  <-- experiment
               \_D'/

Это сохранило слияние и, следовательно, "внутреннюю ветвь", experiment. Это хорошо? Плохо? В различных? Прочитайте (очень длинную) сноску!


1 Это хорошая идея, чтобы узнать, "что такое перестановка" в любом случае, что в git (увы!) в значительной степени требует изучения "как это делается", по крайней мере, на средний уровень. В основном, rebase делает копии (изменения от ваших более ранних) коммитов, которые вы затем применяете к (вашей или чужой), позднее совершает, делая это "похоже", что вы выполняли работу в каком-то другом порядке. Простой пример: два разработчика, скажем, Алиса и Боб, работают в одной ветке. Скажем, что маркетинг попросил функцию с кодовым названием Strawberry, и Алиса и Боб выполняют некоторую работу по реализации strawberry, как на ветке с именем strawberry.

Алиса и Боб оба запускают git fetch, чтобы передать strawberry из origin.

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

Боб пишет описание новой функции, которая меняет файл README, но не имеет другого эффекта. Боб совершает свое изменение и толкает.

Затем Алиса обновляет файл feat, чтобы предоставить реальную функцию. Она пишет и совершает (отдельно) это, и теперь готова подтолкнуть. Но, о нет, Боб избил ее:

$ git push origin strawberry
...
! [rejected]        strawberry -> strawberry (non-fast-forward)

Затем Алиса должна получить изменения и посмотреть на них (не только слепое слияние или переадресация):

$ git fetch
...
$ git log origin/strawberry

(или используя gitk или что-то еще - я обычно использую git lola сам, а git show выполняет отдельные команды, если /as необходимо).

Она может видеть из этого, что Боб изменил только README, поэтому ее изменения определенно не затронуты ни в одном из способов. На этом этапе она может сказать, что безопасно переустанавливать изменения на origin/strawberry:

$ git rebase origin/strawberry

(обратите внимание, что для сохранения не существует слияний), что заставляет его смотреть (в терминах git history), так как она сначала ждала, пока Боб обновит документацию, и только тогда фактически начал реализовывать изменения, которые все же разделились на две отдельные коммиты, чтобы было легче сказать, позже, изменилось ли изменение файла abc что-то еще. Эти две отдельные коммиты теперь смежны, поэтому теперь легко сказать, что точкой перехода на abc было включение изменения в файл feat. И так как на первый взгляд происходит переход на README, еще более ясно, что это была точка изменения на abc. Не то чтобы было трудно сказать, даже если бы Алиса только что сделала:

$ git merge origin/strawberry

вместо этого, хотя это создает коммит-коммит, единственная точка которого, похоже, заключается в том, чтобы сказать: "Алиса начала в abc до того, как Боб закончил обновление README и закончил feat после", что не очень полезно.

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

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