Git: почему я не могу удалить свою ветку после слияния сквоша?

У меня есть git repo с mainline (эквивалентно master) и некоторые локальные ветки функций. Например:

$ git branch
* mainline
  feature1
  feature2
  feature3

Когда я делаю следующее, я могу squash объединить все мои изменения в ветки функции в одну фиксацию на mainline:

$ git checkout mainline
$ git pull
$ git checkout feature1
$ git rebase mainline
$ git checkout mainline
$ git merge --squash feature1
$ git commit
$ git push

Мой вопрос в том, что в этот момент, когда я пытаюсь удалить ветвь feature1, он говорит мне, что он не полностью слит:

$ git branch -d feature1
error: The branch 'feature1' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature1'.

Что вызывает эту ошибку? Я думал, что git merge --squash feature1 слит feature1 в mainline.

Ответ 1

Это происходит из-за того, что Git не знает, что слияние сквоша эквивалентно "различным конкретным ветвям". Вы должны принудительно удалить ветвь с git branch -D вместо git branch -D.

(Остальное - это только то, почему это так).

Нарисуйте граф фиксации

Пусть нарисовать (часть) графа фиксации (этот шаг подходит для многих вещей в Git...). На самом деле, отпустите еще один шаг, чтобы мы начали с вашего git rebase, с чем-то вроде этого:

...--o--o--o     <-- mainline
      \
       A--B--C   <-- feature1

Название ветки, например mainline или feature1, указывает только на одно конкретное коммит. Эта фиксация указывает назад (влево) на предыдущую фиксацию и т.д., И это эти обратные указатели, которые формируют фактические ветки.

Верхняя строка коммитов, всего лишь называемая o здесь, немного скучна, поэтому мы не дали им буквенных названий. Нижняя строка commits, A-B-C, находится только на ветке feature1. C - самая новая такая фиксация; он возвращается обратно к B, что возвращается к A, что приводит к одному из скучных o коммитов. (В стороне: крайняя левая o фиксация вместе со всеми более ранними фиксациями в разделе ... находится в обеих ветвях.)

Когда вы запустили git rebase, три комманды A-B-C были скопированы в новые коммиты, добавленные к кончику mainline, давая нам:

...--o--o--o            <-- mainline
      \     \
       \     A'-B'-C'   <-- feature1
        \
         A--B--C       [old feature1, now abandoned]

Новые комманды A'-B'-C' в основном такие же, как и исходные три, но они перемещаются на графике. (Обратите внимание, что все три бурения o совершаются на обеих ветвях сейчас.) Отказ от оригинала три означает, что Git обычно не нужно сравнивать копии с оригиналами. (Если оригиналы были доступны другому имени - ветвь, добавленная к старому feature1, например, Git, может понять это, по крайней мере, в большинстве случаев. Точные сведения о том, как Git показывает это здесь нет особого значения.)

В любом случае, теперь вы запускаете git checkout mainline; git merge --squash feature1. Это делает одно новое коммитирование, которое является "сквош-копией" трех или даже многих коммитов, находящихся на feature1. Я прекращу рисовать старые заброшенные и вызовет новый squash-commit S для Squash:

...--o--o--o--S         <-- mainline
            \
             A'-B'-C'   <-- feature1

"Удалить безопасность" полностью определяется историей фиксации

Когда вы запрашиваете Git для удаления feature1, он выполняет проверку безопасности: "feature1 объединено в mainline?" Это "сливается в" тест основан исключительно на связности графа. Имя mainline указывает на фиксацию S; commit S указывает на первый расточный o фиксация, которая возвращает к более скучной o фиксации. Commit C', кончик feature1 недоступен из S: нам не разрешено двигаться вправо, только влево.

Сравните это с созданием "нормального" слияния M:

...--o--o--o---------M   <-- mainline
            \       /
             A'-B'-C'    <-- feature1

С помощью одного и того же теста: "является ли фиксация наконечника feature1 достижимой от фиксации наконечника mainline?" - ответ теперь да, потому что commit M имеет ссылку вниз и левую для фиксации C'. (Commit C' есть в Git внутреннем выражении, второй родитель слияния commit M.)

Так как слияния сквоша фактически не сливаются, но нет связи от S назад к C'.

Опять же, Git даже не пытается увидеть, является ли S "таким же, как" A', B' или C'. Однако, если бы это было так, то было бы сказано "не то же самое", потому что S совпадает с суммой всех трех коммитов. Единственный способ получить S для соответствия раздавленным коммитам - это иметь только одну такую ​​фиксацию (и в этом случае нет необходимости раздавить в первую очередь).