2016-12-27 8 views
1

Пытаясь понять, почему командаНе git merge --squash действительно git rebase -squash?

git merge --squash mybranch 

не называется

git rebase -squash mybranch 

Так как кажется, операция это делает гораздо больше похож на перебазирование, чем слияние. Я понимаю, что он ищет обратно в дереве фиксации до тех пор, пока не найдет общую фиксацию базы для текущей ветви и mybranch. Затем он повторно использует (перебазирует) все фиксации от этого базового узла до головки mybranch на голове текущей ветви. Но делает это в индекс/рабочее пространство, поэтому его можно применять как одно коммит. Когда сделано, нет никакого узла слияния, как есть в нормальном слиянии, показывающем две ветви, которые были объединены. У меня есть правильное понимание?

+0

'merge --squash' просто сжимает все коммиты из ветки в одну. Он не изменяет историю. –

+0

Что говорит Энди, так это то, что раздавливание происходит до слияния, поэтому с точки зрения целевой ветви он все еще только видит слияние. –

+0

Моя точка была после регулярного слияния, на графике показаны две ветви, которые были объединены, объединены в один узел слияния, но после слияния --squash & commit график показывает, что две ветви еще раздельны, но с изменениями от mybranch, на текущую ветвь. Для меня это больше похоже на перебазу, которая была сжата в одну фиксацию.Но реальный вопрос, который я задавал, заключался в том, что моя концепция того, что происходит слиянием --squash, была в корне корректной и из расширенного ответа от @torek. Я пришел к выводу, что, хотя есть еще более сложные детали, которые еще предстоит изучить. – JonN

ответ

2

Ну, слияние и перезагрузка - принципиально разные операции. Слияние-я имею в виду регулярные git merge что создает новое слияние совершить, действительно искать назад через фиксации графика для самого последнего общего фиксации:

...--o--*--o--o--A <-- mainbr 
     \ 
      B--C--D--E <-- sidebr 

Здесь самая последняя общая фиксация будет *. Затем процесс слияния сравнивается (как в git diff) с фиксацией * с фиксацией A, чтобы узнать, «что мы сделали», и diff * против фиксации E, чтобы узнать «что они сделали». Затем он делает новый сингл слияния коммит, с двумя родителями:

...--o--*--o--o--A---M <-- mainbr 
     \  /
      B--C--D--E <-- sidebr 

, который соединяет две истории, и сочетает в себе «что мы сделали», и «то, что они сделали», так что сравниваете * против M дает «один из каждое изменение ".

Обратите внимание, что здесь нет выбора базы слияния: Git вычисляет это, и все.

С другой стороны, с другой стороны может быть заявлено, что с другой стороны может быть заявлено, что для того, чтобы скопировать, и где их копировать, можно указать отдельно. Это правда, что по умолчанию он снова фиксирует * -but then копии оригинал фиксирует один за другим, используя git cherry-pick или его эквивалент; и, наконец, он перемещает ветвь метку, чтобы указать на последнюю скопированную фиксацию.

...--o--*--o--o--A <-- mainbr 
     \  \ 
      \  B'-C'-D'-E' <-- sidebr 
      \ 
      B--C--D--E 

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

Что git merge --squash делает это просто немного изменить процесс слияния: вместо того, чтобы объединить совершить M, Git идет через слияние машин как обычно, сравниваете на слияние базы-которой вы не можете выбрать, против текущая фиксация и другая фиксация и объединение изменений в индексе и дереве.Он тогда без всякой видимой причины -stops и заставляет вас работать git commit совершить результат, и когда вы делаете, это обычное, не слияние совершить, так что весь граф-фрагмент выглядит следующим образом:

...--o--*--o--o--A--F <-- mainbr 
     \ 
      B--C--D--E <-- sidebr 

Теперь, содержание коммиттерских F -The снимок дерева в результате слияния, так же, как содержание фиксации M когда мы делаем реальное слияние, и-вот реальный кикер-это также то же, что и содержание фиксации E', когда мы делаем rebase.

Кроме того, предположим, что только одна фиксация (B) на боковой ветке sidebr. Теперь все три из merge, merge --squash и rebase даст вам картину, которая заканчивается с чем-то мы могли бы просто сделать так:

...--o--*--o--o--A--B' <-- ??? 
     \   ? 
      B????????? <-- ??? 

и содержание нового фиксации B', т.е. конечный снимок, является то же самое во всех трех случаях. Однако для git merge новый фиксатор будет находиться на ветке mainbr и будет указывать на A и B, с sidebr, указывая на B; для git merge --squash, новая фиксация будет на mainbr и укажет только на A; и git rebase, новый будет совершать на sidebr, ни с чем не очевидным, указывающей B на всех, и мы должны сделать это так:

...--o--*--o--o--A <-- mainbr 
     \  \ 
      B  B' <-- sidebr 

так mainbr будет продолжать указывать на совершение A.

В конце концов, это выглядит немного больше похоже на слияние, чем на восстановление. (Тем не менее, я был бы счастлив, если бы не были названы «сквош слияние» на всех.)


Метод, с помощью которого Git находит * несколько иначе: на самом деле это не одно обязательство, а скорее как последний из (обычно) очень большой набор коммитов, а именно всех тех, которые достижимы из аргумента <upstream> до git rebase. (Смутно, слияние база также может быть множество фиксаций, но это гораздо более ограниченный набор. Лучше не погружаться в теорию графов еще. :-))

Если мы хотели это чтобы остановить, мы могли бы использовать --no-commit так же, как и для обычного, без сквоша git merge. Итак, почему делает автоматически останавливается?