Git Сквош по автору - весь автор совершает одно фиксирование

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

Случай:

Допустим, у меня есть ветвь с именем feature-a, в этой ветки у меня много коммитов для многих авторов. Как я могу скворовать все коммиты от автора (например, по электронной почте) в один коммит. Я хочу сделать это, чтобы объединить все авторские коммиты в мастер.

Любая помощь здесь?

Заранее спасибо

Ответ 1

Будьте осторожны, переписывая историю

Конечный результат, который вы хотите, может быть возможен, если вы создаете ветки для каждого автора, cherry-pick коммиты каждого автора в правильную ветвь, а затем раздавить эти изменения. Тем не менее, я не думаю, что это сработает, если эти обязательства будут значимо зависеть друг от друга.

Если у вас есть серия коммитов:

            Author1                Author2                Author1
version1 ---commit---> version2 ---commit---> version3 ---commit--->...

Если вы попытаетесь извлечь изменения из Author2 и применить их к версии 1, то есть хороший шанс, что это не будет иметь смысла (например, если Author2 изменяет код, созданный Author1).

Ответ 2

С учетом Kenkron оговорок вы можете сделать следующее:

SORTED_GIT_LOGS=$(git log --pretty="format:%an %H" master..feature_a | sort -g | cut -d' ' -f2); \
IFS=$(echo -en "\n\b"); for LOG in $SORTED_GIT_LOGS; do \
    git cherry-pick $LOG; \
done | less

git log --pretty="format:%an %H" master..feature_a | sort -g будет сортировать журналы feature_a commits (а не те из master из-за синтаксиса master..feature_a)

Вам все равно нужно сделать интерактивную переадресацию для сквоша (теперь упорядоченного по автору) совершает на master.

Ответ 3

Мне нужно было сделать подобный переписывание на ненужном большом хранилище, в то время как репо было отключено. Подход, который я использовал, заключался в автоматическом "интерактивном" переадресации с использованием GIT_SEQUENCE_EDITOR, который рассматривается в этом ответе @james-foucar и @pfalcon.

Чтобы это хорошо работало, мне было лучше сначала удалить слияния из раздела переписываемой истории. Для моего собственного случая это было сделано с помощью большого количества git rebase --onto, которое подробно рассматривается в других вопросах в StackOverflow.

Я создал small script generate-similiar-commit-squashes.sh для создания команд pick и squash, чтобы последовательные подобные коммиты быть раздавленным. Я использовал author-date-and-shortlog для сопоставления с подобными коммитами, но вам нужен только автор (у моего gist есть комментарий о том, как сделать его совпадающим только с автором).

$ generate-similiar-commit-squashes.sh > /tmp/git-rebase-todo-list

Выход выглядит как

...
pick aaff1c556004539a54a7a33ce2fb859af0c4238c [email protected]
squash aa190ea2323ece42f1cd212041bf61b94d751d5c [email protected]
pick aab8c98981a8d824d2bc0d5278d59bc1a22cc7b0 [email protected]_config.yml

Репозиторий также был полон самовосстановления с тем же сообщением "Обновить xyz". Когда раздавили, они привели к пустым коммитам.

У коммитов, которые были слияния, были одинаковые сообщения фиксации. git rebase -i предлагает пересмотренное сообщение фиксации со всеми сжатыми сообщениями о фиксации, которые были бы повторяющимися. Чтобы решить эту проблему, я использовал небольшой perl script из этого ответа, чтобы удалить повторяющиеся строки из сообщения фиксации, предлагаемого git rebase. Это лучше в файле, так как оно будет использоваться в переменной оболочки.

$ echo 'print if ! $x{$_}++' > /tmp/strip-seen-lines.pl

Теперь для последнего шага:

$ GIT_EDITOR='perl -i -n -f /tmp/strip-seen-lines.pl ' \
  GIT_SEQUENCE_EDITOR='cat /tmp/git-rebase-todo-list >' \
  git rebase --keep-empty -i $(git rev-list --max-parents=0 HEAD)

Несмотря на использование --keep-empty, git несколько раз жаловался на этот процесс о пустых коммитах. Это вывело бы меня на консоль с неполным git rebase. Чтобы пропустить пустую транзакцию фиксации и возобновления, необходимы две команды (довольно часто в моем случае).

$ git reset HEAD^
$ GIT_EDITOR='perl -i -n -f /tmp/strip-seen-lines.pl ' git rebase --continue

Снова, несмотря на --keep-empty, я обнаружил, что не имел пустых коммитов в последней истории git, поэтому сброс выше удалил их все. Я предполагаю, что что-то не так с моей версией git, версии 2.14.1. Обработка ~ 10000 таких как это заняла чуть более 10 минут на дерьмовом ноутбуке.