Раздавить объединенную ветвь
Я хотел бы знать, как раздавить все коммиты слитой ветви следующим образом:
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, я собираюсь написать это.
Я буду держать вас в курсе