Блог переехал. Актуальная версия поста находится по адресу: http://aakinshin.net/ru/blog/git/rewrite-commiter-list/.
Иногда возникает потребность переписать коммитеров в Git-репозитории. Задача достаточно редкая, но иногда всё-таки приходится ей заниматься. Давайте разберёмся в ситуации подробней. Прежде всего, взглянем на текущий список коммитеров:
$ git log --pretty=format:"%an <%aE>" | sort -u
Допустим, мы получили следующий список:
Ivan <ivan@gmail.com> Ivan <ivan.ivanov@gmail.com> Ivan <ivan-ivan@gmail.com> Ivan Ivanov <ivan.ivanov@gmail.com> Vanya Ivanov <ivan.ivanov@gmail.com> Vanya <ivan.ivanov@gmail.com>
Наблюдаем следующую проблему: некий Иван Иванов делал коммиты, указывая каждый раз разную информацию об имени пользователя и почтовом адресе. Для начала нужно дать по рукам Ивану и сказать, чтобы больше так не делал. Лучше всего использовать для всех коммитов одинаковую учётную информацию (например, Ivan Ivanov <ivan.ivanov@gmail.com>
). Проблема может встать особенно остро, если в проекте используются дополнительные сервисы, которые работают с репозиторием (code review system, build server и т.п.). Ну, а пока Иван размышляет над своим поведением, мы займёмся переписыванием истории.
На текущий момент в репозитории имеется ряд коммитеров с именем Ivan
. Давайте их все объединим! А поможет нам в этом замечательная команда git filter-branch:
$ git filter-branch --commit-filter ' if [ "$GIT_AUTHOR_NAME" = "Ivan" ]; then GIT_AUTHOR_NAME="Ivan Ivanov"; GIT_AUTHOR_EMAIL="ivan.ivanov@gmail.com"; git commit-tree "$@"; else git commit-tree "$@"; fi' HEAD
Теперь все Иваны ушли из нашего репозитория, список коммитеров выглядит следующим образом:
Ivan Ivanov <ivan.ivanov@gmail.com> Vanya Ivanov <ivan.ivanov@gmail.com> Vanya <ivan.ivanov@gmail.com>
После перезаписи истории git сохраняет оригинальные указатели на ветки в .git/refs/original
. Для выполнения следующей перезаписи истории (давайте объединим пользователей по почтовому адресу) нам необходимо либо удалить эту папку, либо выполнить команду с ключом -f
:
$ git filter-branch -f --commit-filter ' if [ "$GIT_AUTHOR_EMAIL" = "ivan.ivanov@gmail.com" ]; then GIT_AUTHOR_NAME="Ivan Ivanov"; GIT_AUTHOR_EMAIL="ivan.ivanov@gmail.com"; git commit-tree "$@"; else git commit-tree "$@"; fi' HEAD
Ура! У нас остался единственный коммитер:
Ivan Ivanov <ivan.ivanov@gmail.com>
Проверив правильность изменений, можно с чистой совестью удалить папку .git/refs/original
с бэкапом данных. После этого в репозитории будет находится много мусора. Не помешает явно избавиться от него c помощью git gc:
$ git gc
Далее наступает ответственный этап: отправка изменённой истории на сервер:
$ git push --all -f origin
После этого можно порекомендовать вашим коллегам по репозиторию снести свою локальную копию и скачать все данные с нуля (думаю, это наиболее безболезненный способ перехода на новое дерево коммитов).
Важно!
- Помните, что коммиты содержат SHA-1 своих родителей, поэтому будут переписаны SHA-1 не только целевых коммитов, но и всех их потомков. Соответственно, если была привязка сторонних сервисов к коммитам вашего репозитория по SHA-1, то она «погибнет» после перезаписи истории. Да и остальные разработчики в вашей команде будут безмерно удивлены полностью переписанной истории на сервере. Поэтому не используйте перезапись истории на сервере, если у вас нет действительно веских причин для этого.
- Процесс перезаписи истории быстрым не назовёшь, время работы прямо пропорционально общему количеству коммитов. Если ваш репозиторий достаточно большой, то придётся запастись терпением.
Комментариев нет:
Отправить комментарий