Как сгруппировать все коммиты одного автора подряд? - PullRequest
0 голосов
/ 01 апреля 2019

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

  • commit 09 - автор BBBB
  • commit 08 - автор BBBB
  • commit 07 - автор AAAA
  • commit 06 - автор AAAA
  • commit 05 - Автор AAAA
  • commit 04 - Автор CCCC
  • commit 03 - Автор CCCC
  • commit 02 - Автор AAAA
  • commit01 - Автор BBBB

Это закончилось как:

  • commit 05 - Автор BBBB
  • commit 04 - Автор AAAA
  • commit 03 - Автор CCCC
  • commit 02 - Автор AAAA
  • commit 01 - Автор BBBB

Как написать скрипт с помощью git?

Ответы [ 2 ]

2 голосов
/ 01 апреля 2019

Нет встроенного способа сделать это.

Как nologin эффективно отметил в комментарии , если вы достигнете желаемого набора коммитов, у вас будет new история, несовместимая с оригинальной историей.Если все в порядке, есть процесс - не встроенный, но не чрезвычайно трудный - с помощью которого вы можете достичь желаемого набора коммитов.Однако сначала убедитесь, что вы хотите.

Вы описываете коммиты как линейные, и они могут на самом деле быть линейными, но это не так.Они будут линейными в некоторых областях.Но коммиты образуют направленный ациклический граф или DAG.Этот график является историей в хранилище.В тех частях, где он линейный, он довольно прост:

... <-F <-G <-H   <-- master

Здесь имя ветви master идентифицирует или указывает на , фиксирует H.Точнее, имя master хранит хэш-идентификатор commit H.Коммит H, тем временем, сохраняет хэш-идентификатор родительского коммита H G, в котором хранится хэш-идентификатор его родительского F и т. Д.Начиная с конца и работая в обратном направлении, git log показывает вам эти коммиты, а является историей.

Однако некоторые коммиты являются merge коммитами.Такой коммит имеет двух (или более, но обычно только двух) родителей.Мы можем нарисовать их следующим образом:

       I--J
      /    \
...--H      M   <-- dev
      \    /
       K--L

Здесь имя ветви dev указывает на фиксацию M, но M указывает на оба J и L.J указывает на I;L указывает на KI и K оба указывают на коммит, из которого сформировались две дочерние ветви внутри ветви, а именно на коммит H (на который предположительно указывает имя master: коммиты H и более ранние включены и master и dev).

Если коммиты I, L и M все сделаны автором BBBB, ноJ и K принадлежат автору AAAA, что вы собираетесь здесь делать?Если вы сохраняете M (BBBB) и сохраняете J, потому что он принадлежит другому автору AAAA, вы также должны оставить L, даже если это BBBB.Однако, если все I-J и K-L и M имеют значение AAAA, вы можете свернуть их все в один коммит, родитель которого равен H:

...--H--M'  <-- dev

ваша задача - определить, какие коммиты вы хотите сохранить, и что вы хотите сделать с коммитами слияния.Вы должны сохранять коммиты слияния, если вам нужно сохранить структуру (форк-энд-слияние в H и M).Если вы хотите исключить структуру ветвления и слияния, вы должны отменить коммиты слияния, но затем вы должны выяснить, что делать со странными коммитами, такими как I и L если они принадлежат другому автору.

Что бы вы ни решили, когда вы, наконец, сделали, способ достичь желаемого результата:

  • Началосо списком всех коммитов (по хеш-идентификатору), которые вы хотите сохранить, и / или всех коммитов, которые вы хотите отменить.(Достаточно и того, и другого, поскольку мы предполагаем, что вы будете поддерживать устойчивый юниверс Всех Коммитов, пока вы делаете это, то есть не добавляйте new коммитов в репозиторий, пока вы вычисляете эти списки ивнесение изменений в хранилище.)

  • Затем запустите git filter-branch.Выберите хотя бы --commit-filter.Вам могут потребоваться дополнительные фильтры, в зависимости от того, какие другие исторические данные вы намереваетесь удалить здесь.(Например, у каждого коммита есть сообщение журнала: хотите ли вы объединить все сообщения журнала или отбросить те из коммитов, снимок которых вы выбросили? Что равно что вы делаете: вы создаете вымышленную историю. Вы можете составить столько, сколько захотите, оставив только то, что вам нравится, из исходной истории, отбросив все остальное. Что вы держите и что отбрасываете доВы. Ваш новый репозиторий несовместим со старыми репозиториями: изменение хотя бы одного бита в любом месте истории делает оставшуюся историю недействительной и несовместимой. Таким образом, вы можете пойти так далеко, как захотите: это действительно все или ничего!)

    В вашем фильтре фиксации - прочитайте документацию git filter-branch для деталей - используйте skip_commit, чтобы пропустить коммиты, которые вы не хотите и git commit-tree "$@", чтобы сделатькоммиты, которые вы хотите сохранить.Чтобы решить, просто посмотрите, есть ли $GIT_COMMIT в списке хранения или отмены.

Команда filter-branch позаботится о перечислении каждого коммита, по одному, в правильномСделайте так, чтобы вы могли создавать или исключать коммит из истории, которую вы создаете, на ходу.После того, как ваш фильтр фиксации будет вызываться для каждого такого коммита, он запишет идентификатор хэша последнего скопированного коммита в имя хэша.Оригинальная история теперь фактически исчезла (но все еще доступна через имя refs/original/refs/head/<em>branch</em>; это имя не будет в каких-либо новых клонах, и вы можете отказаться от него, когда будете готовы; снова посмотритедокументация).

0 голосов
/ 02 апреля 2019

На основании этого ответа https://stackoverflow.com/a/46403701/926064, Я закончил с этим. Это действительно работает как шарм:

$ GIT_EDITOR='cat' \
GIT_SEQUENCE_EDITOR='todofile=$1; awk '"'"'{if ($1 != "#" && $1 != "") { author=$3; if (lastauthor != author) { lastauthor=author; printf "pick %s %s\n", $2, $3 } else { printf "squash %s %s\n", $2, $3 }}}'"'"' $todofile>$todofile.temp; mv -f $todofile.temp $todofile; cat $todofile' \
git -c "rebase.instructionFormat=%ae" rebase -i $(git log --oneline --reverse --pretty=format:%H  | head -n1)

Примечания:

Первое, GIT_EDITOR гарантирует, что сообщение фиксации сквоша будет сохранено, как и сообщения git squash по умолчанию, не касаясь их - они будут объединенными сообщениями.

Второй, GIT_SEQUENCE_EDITOR будет выполнять желаемую работу, фильтр, сообщая, какой коммит будет подавлен в зависимости от автора. Но это зависит от электронной почты автора, поэтому, когда мы вызываем git rebase, мы должны отформатировать «инструкции по перебазированию» с просьбой git добавить электронную почту автора в список.

Третий и последний - git rebase, но мы должны отформатировать «инструкцию по перебазированию», чтобы поместить в них всю информацию, которая нам понадобится при обработке (редактировании) списка инструкций по перебазировке.

Для удобства ниже отформатированный скрипт awk вставлен в GIT_SEQUENCE_EDITOR переменную:

{ 
    if ($1 != "#" && $1 != "") { 
        author=$3; 
        if (lastauthor != author) { 
            lastauthor=author; 
            printf "pick %s %s\n", $2, $3 
        } else {
            printf "squash %s %s\n", $2, $3
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...