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