Действительно расплющить мерзавец слияние

В 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. Итак, процесс будет:

  1. Как обычно, объедините ветви A и B из разных репо в один репо.
  2. Перебазировать (неинтерактивно) одну ветку на другую. Подумайте, какая ветвь должна быть перебазирована на какую, чтобы иметь первоначальное право коммита (которое не может быть легко переписано с помощью rebase).
  3. Начните git rebase -i
  4. В другой консоли перейдите в $REPO/.git/rebase-merge/
  5. Бежать: 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
  6. Это кажется правильным местом для изменения порядка коммитов: sort -k3 git-rebase-todo >git-rebase-todo.new; mv git-rebase-todo.new git-rebase-todo
  7. Переключитесь на исходную консоль и перезагрузите файл git-rebase-todo в редакторе, затем выйдите из редактора.

Вуаля! На самом деле, это может быть полностью написано, если git rebase -i мог работать в "неинтерактивном" режиме, я представил Как мне запустить git rebase --interactive неинтерактивным способом? для этого.

На самом деле, если я правильно понимаю, вы можете легко добиться этого с помощью git-stitch-repo.

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