Раздавить объединенную ветвь

Я хотел бы знать, как раздавить все коммиты слитой ветви следующим образом: feature | c3 - merge_master - c6 / / \ master | c1 - c2 - c5 ------------- merge_feature - c7

и я стремлюсь иметь это master | c1 - c2 - c5 - squash_c3_c6 - c7

я нашел это git rebase c6 c5 --onto c6 позвольте мне повторить c3 а также c6 иметь c3' а также c6' но у меня всегда есть c3 а также c6 в моей истории.

Я должен сделать это в скрипте, чтобы обработать большой репозиторий (более 6 000 веток!), Поэтому я не могу использовать git rebase -i

Любая идея?

3 ответа

Вы не можете (вполне) получить то, что вы хотите, но то, что вы получаете, вполне может быть хорошо.

В Git "сквош" означает "копировать". В частности, "сквошное слияние" использует механизм слияния Git, выполняя действие слияния (слово слияние как глагол), но затем делает обычный коммит. Предположим, например, у вас было это:

   C3--C6   <-- feature
  /
C1
  \
   C2--C5   <-- master

(обратите внимание, что название ветви, как master или же feature на самом деле указывает только на один коммит, а именно на коммит ветки; и некоторые коммиты находятся на обеих ветках, таких как C1). Бег git checkout master && git merge --squash feature тогда бы:

  • выбирать C1 как последнее место ветки разделили коммит;
  • выбирать C5 как верхушка текущей ветви master;
  • выбирать C6 как кончик feature,

Это входные данные для процесса слияния ("слияние" как глагол). Тогда Git будет:

  • сравнить C1 против C5это то, что мы изменили;
  • сравнить C1 против C6Это то, что они изменили.

Затем Git пытается объединить оба набора изменений. Если все идет хорошо, результат помещается в индекс и рабочее дерево ("готовится к фиксации"). Так как вы использовали --squash который автоматически включается --no-commitНа этом Git останавливается без коммита, но создает предварительно загруженное сообщение коммита.

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

   C3--C6   <-- feature
  /
C1
  \
   C2--C5--C7_which_is_3_plus_6   <-- master (HEAD)

На данный момент единственное разумное, что нужно сделать с именем ветви feature это удалить его, отказавшись от оригинальных коммитов C3 а также C6 (они будут в конечном итоге, или, возможно, даже очень скоро, будут собирать мусор: удаление ветки также удаляет те ветки, которые их защищают).

Лог-сообщение для C7 это все, что вы хотите, но в зависимости от того, как вы настроили Git, вы можете использовать его по умолчанию для объединения существующих сообщений журнала из C3 а также C6 (задавать merge.log в true или число, которое по крайней мере 2, или используйте --log).

Обратите внимание, что новый коммит C7 что берет то, что было сделано в оригинале C3 а также C6 (минус все, что уже продублировано C5). Это то, что, по-видимому, делает его безопасным для удаления feature совершенно так.

К сожалению, это не то, что у вас есть. У вас уже есть коммит слияния, достижимый с кончика feature, давая график, который вы нарисовали:

   C3--C4--C6   <-- feature
  /   /
C1--C2--C5   <-- master

База слияния feature а также master сейчас, а не совершать C1, а лучше совершить C2, Это потому, что когда мы начинаем с master и работать в обратном направлении, мы находим коммиты C5, затем C2, затем C1; и когда мы начнем с feature и работать в обратном направлении, мы находим C6, затем C4тогда оба C3 а также C2 одновременно; а потом C1,

Это означает, что C2не C1является "ближайшим" коммитом к обоим веткам, который находится в обеих ветках. С помощью git merge --squash когда на master будет затем:

  • разница C2 против C5: что мы изменили;
  • разница C2 против C6: что они изменили;
  • объединить изменения и остановить из-за подразумеваемых --no-commit

Затем вы можете сделать новый коммит C7:

   C3--C4--C6   <-- feature
  /   /
C1--C2--C5----C7   <-- master

Опять же, единственное разумное, что нужно сделать с feature это удалить его. Результатом будет последовательность ...--C1--C2--C5--C7, Это та же последовательность коммитов, что и раньше, и, что более важно, дерево (исходное содержимое), связанное с C7 должно быть таким же, как дерево, без которого вы получите C4, пока C4 само по себе не злое слияние и C6 не отменяет часть C4,

Причина этого в том, что C4 включает в себя изменения от C3и, очевидно, C6 включает в себя изменения от C6, Это означает, что когда Git побежал git diff C2 C6 он увидел изменения в обоих C3 а также C6: они являются частью одного из двух входов в процесс слияния как глагол. Отсюда и новый C7 содержит все изменения, которые вы хотите.

Чего нет в зависимости от --log а также merge.log настройки, это автоматическое заполнение сообщения журнала. Но вы можете редактировать сообщение журнала для C7 любым способом, включая поиск более раннего коммита C1 и используя git log --pretty=short --no-merges <hash>..feature,

Я думаю, что вы ищете git filter-brabch который удалил бы вторых родителей из коммитов слияния.

Что-то вроде (не проверено!)

git filter-branch --parent-filter 'read pkey pvalue rest && echo "$pkey $pvalue"' branch_first_commit..branch

(это может быть быстрее с помощью sed или awk, но я боюсь допустить ошибку при цитировании. Может произойти сбой при первом коммите)

Объяснение: Эта старая история может иметь конфликты, поэтому воспроизведение истории ветвей не может быть надежно выполнено автоматически. Но "раздавленное слияние" - это просто слияние с удаленными вторыми родителями. И у вас уже есть слияния, поэтому вам нужно только удалить родителей.

Так что я играл с git filter-branch --parent-filter Позволяет мне слить все коммиты слитых веток на моем мастере, полезно, но теряет правильное упорядочение коммитов => C1--C3--C2--C4--C5, Результат хороший, но история стала противоречивой, я ожидал чего-то вроде C1--C2--C3--C4--C5 и даже при этом осталось раздавить С3 и С4.

@torek спасибо за объяснения, я лучше понимаю, что C2 и C3 в равной степени являются предками C4 без понятия происхождения, такого как "commit from feature branch" или "commit from merge branch". И есть "коммит слияния", но просто коммит, который делает слияние.

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

Я буду держать вас в курсе

Другие вопросы по тегам