Действительно расплющить мерзавец слияние
В Stackru мало вопросов о "сплющивающем слиянии", а ответом обычно является "git rebase". Эти ответы, тем не менее, упускают один важный момент - порядок фиксации.
Предположим, что есть ветвь A с коммитами 1 июня и 1 августа и ветвь B с коммитом 1 июля (ОБНОВЛЕНИЕ, чтобы восстановить описанный ниже сценарий использования: ветки полностью независимы и не имеют общего происхождения, например, поступают из 2 разных репозиториев).). При объединении B в A будет следующая история (для журнала git):
Merged branch 'B'
Aug 1
Jul 1
Jun 1
Теперь я ищу способ получить тот же результат, но без коммитов слияния (и, следовательно, с лежащей в основе линейной историей в таком порядке, и да, это означает переопределение коммитов). git rebase здесь не помогает, так как с ним вы получите следующие истории:
Jul 1
Aug 1
Jun 1
или же
Aug 1
Jun 1
Jul 1
Другими словами, git rebase всегда накладывает одну ветку поверх другой, в то время как я ищу решение, которое будет разбирать коммиты, отсортированные по дате принятия автора.
По-видимому, для простых случаев необходимое расположение может быть достигнуто путем ручной постобработки git rebase с помощью git rebase -i, но это не практично для больших историй, поэтому я бы искал автоматизированную команду / скрипт.
USECASE? Если A и B представляют разные части одного и того же проекта, который оказался в разных репозиториях, и пришло время исправить это, объединив их вместе, то естественно, что линейная история разворачивается в фактическом порядке разработки.
4 ответа
Подумав немного, я разобрался, как это сделать. Как мне запустить git rebase - интерактивно неинтерактивным способом?, который также предоставляет полностью скриптовое решение для этого вопроса.
1. Принесите 2 ветки из разных репозиториев в один репозиторий (git remote add + git fetch)
2. Перебазируйте (не интерактивно) одну ветвь поверх другой (порядок имеет значение, рассмотрите первую фиксацию той ветки, которую вы хотели бы иметь в качестве первой фиксации консолидированной ветки).
3. Подготовьте следующий скрипт (rebase-reoder-by-date
):
#!/bin/sh
awk '
/^pick/ {
printf "%s %s ", $1, $2;
system("echo -n `git show --format='%ai' -s " $2 "`");
for (i = 3; i <= NF; i++) printf " %s", $i; printf "\n";
}
' $1 | sort -k3 > $1.tmp
mv $1.tmp $1
4. Запустите: GIT_SEQUENCE_EDITOR=./rebase-reoder-by-date git rebase -i <initial commit>
Отказ от ответственности: все эти операции должны происходить с копиями оригинальных репозиториев, проверять / проверять / тестировать объединенную ветку, чтобы убедиться, что это то, что вы ожидали и содержат то, что вы ожидаете, держать резервные копии под рукой.
В чем проблема с тем, чтобы оставить отдельные разработки в отдельных строках до их объединения? Если они были отдельными, то они были отдельными.
Есть много способов просмотреть историю в хронологическом порядке, не взламывая историю, как вы пытаетесь. Ты пытался git log --pretty --date-order
?
[Смотрите мой другой ответ для полностью автоматизированного решения. Я бы оставил это в качестве примера пути, который привел к окончательному решению, на случай, если кто-то столкнется с подобной не столь очевидной для решения задачи.]
Хорошо, это не реальный ответ на вопрос (полностью сценарий, автоматизированное решение), но размышление и пример того, как обработка (на основе интерактивной ребазировки) может быть автоматизирована.
Ну, во-первых, для окончательного решения git filter-branch --parent-filter
выглядит именно то, что нужно. За исключением того, что мой git-fu не позволяет мне писать, 1-, 2- или 3-строчный с ним, и подход к написанию автономного скрипта для анализа всех ревизий не классный и более трудоемкий, чем rebase -i.
Таким образом, rebase -i можно было бы эффективно использовать, если бы были видны даты коммитов автора. Моей первой мыслью было временно исправить патч сообщения, чтобы начать с даты автора, используя git filter-branch --msg-filter
, выполните rebase -i, затем распакуйте сообщения обратно.
Вторая мысль заключалась в следующем: зачем беспокоиться, лучше исправлять список изменений rebase, используемый rebase -i. Итак, процесс будет:
- Как обычно, объедините ветви A и B из разных репо в один репо.
- Перебазировать (неинтерактивно) одну ветку на другую. Подумайте, какая ветвь должна быть перебазирована на какую, чтобы иметь первоначальное право коммита (которое не может быть легко переписано с помощью rebase).
- Начните
git rebase -i
- В другой консоли перейдите в $REPO/.git/rebase-merge/
- Бежать:
awk '/^pick/ {printf "%s %s ", $1, $2; system("echo -n
git show --format='%ai' -s " $2 "
"); для (i = 3; i <= NF; i++) printf " %s", $i; printf "\n"; }' git-rebase-todo > git-rebase-todo.new; mv git-rebase-todo.new git-rebase-todo - Это кажется правильным местом для изменения порядка коммитов:
sort -k3 git-rebase-todo >git-rebase-todo.new; mv git-rebase-todo.new git-rebase-todo
- Переключитесь на исходную консоль и перезагрузите файл git-rebase-todo в редакторе, затем выйдите из редактора.
Вуаля! На самом деле, это может быть полностью написано, если git rebase -i
мог работать в "неинтерактивном" режиме, я представил Как мне запустить git rebase --interactive неинтерактивным способом? для этого.
На самом деле, если я правильно понимаю, вы можете легко добиться этого с помощью git-stitch-repo.