git pull --rebase --preserve-merges

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

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

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

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

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


person AturSams    schedule 26.01.2014    source источник
comment
Думаю, теперь я понял это благодаря этой ссылке. Насколько я понимаю, если я не сохраняю слияния, он просто добавляет код, который я написал, во все коммиты после самого последнего текущего коммита, игнорируя любые ветки, которые я создал и слил? Если я сохраняю слияния, он перемещает коммиты вперед, но сохраняет расходящиеся ветви.   -  person AturSams    schedule 26.01.2014
comment
Редактировал вопрос. Если я правильно понимаю, если вы фиксируете (локально), а затем извлекаете и объединяете, а затем извлекаете и переустанавливаете, это как-то дублирует ваши изменения?   -  person AturSams    schedule 26.01.2014
comment
Я использую git pull --rebase=merges   -  person marbel82    schedule 04.03.2020


Ответы (2)


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

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


Изменить: добавить рисунок

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

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

Обратите внимание, что ветка experiment также имеет внутри себя ветвление и слияние: база для этих двух ветвей - B, одна ветвь содержит фиксацию C, а другая ветвь содержит фиксацию D. Эти две (безымянные) ветки сжимаются до единого потока разработки при фиксации слияния E, а затем фиксация 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 скопировала все изменения, которые я сделал в коммитах B, C, D и F; они стали новыми коммитами B', C', D' и F'. (Фиксация 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 В любом случае неплохо узнать, «что делает rebase», что в 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 отдельные коммиты, если / по мере необходимости).

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

$ git rebase origin/strawberry

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

$ git merge origin/strawberry

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

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

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

person torek    schedule 26.01.2014
comment
Подробно и информативно, как всегда ... подождите, я написал это уже сегодня, не так ли? +1 в любом случае. Чтобы узнать, возможно ли перебазирование: stackoverflow.com/a/21362208/6309 - person VonC; 26.01.2014
comment
Я не уверен, что полностью понимаю, когда консервация нужна / полезна? Это просто вопрос мнения? Это та часть, которую я не уверен, я полностью понимаю То есть, если уместно вообще выполнить перебазирование, по крайней мере весьма вероятно, что история, которую нужно перебазировать, сама по себе является линейной, так что вопрос о сохранении-против-сглаживании является спорным. все равно .. Что именно сохраняет, а не сплющивает? Слияние, которое было сделано ранее специально между ветками? - person AturSams; 26.01.2014
comment
Параметр preserve сохраняет слияния. В целом невозможно описать их как между ветвями, не определив точно, что вы подразумеваете под ветвями, поскольку git оставляет сам термин неоднозначным. Я могу нарисовать то, что он делает, если это поможет ... - person torek; 26.01.2014
comment
Я предполагаю, что корень проблемы в том, что компания, в которой я работаю, и я сам не люблю бессмысленные коммиты слияния. Поскольку я в некоторой степени осторожный пользователь git и в основном использую stash для обработки нескольких изменений, которые еще не зафиксированы, я полагаю, что никогда не столкнусь с ситуацией, когда перебазирование будет иметь нежелательные результаты (и вместо этого будет достигать цели избежать ненужных пустые коммиты слияния). Думаю, теперь я знаю, как избежать ловушек с перебазированием. - person AturSams; 27.01.2014
comment
@ArthurWulfWhite: если вы избегаете создания пустых слияний, у вас обычно не будет никаких слияний в цепочке, которую вы собираетесь перебазировать, поэтому preserve / no-preserve - это переключатель, который ни на что не влияет. Если вы действительно случайно создали нежелательное пустое слияние, вы можете удалить его заново. - person torek; 27.01.2014
comment
@zehelvion, один из полезных случаев для этого - если вы работаете над продуктом с множеством разработчиков и множеством поддерживаемых версий, находящихся в дикой природе. В моей компании исправление для более ранней версии должно быть объединено с версиями текущей (каждая версия имеет свою собственную ветку выпуска, чтобы мы могли отслеживать это). Если вы выполняете такое слияние, и кто-то совершает коммит раньше вас, rebase --preserve-merges может быть весьма полезен, чтобы избежать повторного слияния. Конечно, вы всегда можете включить rerere и просто повторно выполнить слияние - лошади за курсами. - person MattJenko; 19.06.2015
comment
@torek, пожалуйста, посмотрите: stackoverflow.com/questions/58913987/ - person Number945; 19.11.2019

git pull --rebase то же самое, что git fetch, а затем git rebase, поэтому git pull --rebase --preserve-merges то же самое, что git fetch, а затем git rebase --preserve-merges. Вы можете получить хороший ответ о git rebase --preserve-merges здесь, Что именно делает git rebase --preserve-merges do (и почему?)

person Vlad Nikitin    schedule 26.01.2014
comment
AFAICT, git pull все равно не выберет вариант --preserve-merges. В статье, указанной в вопросе, говорится, что вам нужно fetch, а затем rebase - person Hasturkun; 26.01.2014
comment
@Hasturkun: есть новая опция --rebase=preserve (или вы можете установить branch.<name>.rebase на preserve). - person torek; 26.01.2014