Технически - и я утверждаю, что это немного глупо с точки зрения 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
git pull --rebase=merges
- person marbel82   schedule 04.03.2020