Использование перехватчиков (hooks) в Git для блокирования правки опубликованных коммитов

от автора

Привет, Хабр!

Тем, кто работает с Git, хорошо знаком способ отредактировать последний коммит командой git commit --amend. Это удобно для мелких правок (изменить комментарий к коммиту, поправить строчку в коде и т.п.), потому что частенько хорошие мысли по поводу коммита приходят в голову уже после того, как этот коммит сделан.

Но с данным способом правки коммитов следует быть осторожным в случае, когда вы работаете с удалённым репозиторием и ещё более осторожным, когда вы работаете над исходным кодом в составе команды. Область безопасного использования опции --amend заканчивается там, где начинается область использования команды git push.

При коммите с опцией --amend создаётся новый коммит (с новым хешем), который заменяет собой предыдущий, а тот предыдущий коммит удаляется из истории.

Если вы отредактируете свой уже опубликованный коммит (отправленный в удалённый репозиторий командой git push), то в будущем создадите проблемы себе (когда попытаетесь опубликовать свой отредактированный коммит) и (самое важное!) создадите проблемы другим разработчикам, которые успели получить в свои локальные репозитории ваш старый коммит. В таком случае, и вам, и другим разработчикам обеспечена головная боль по чистке истории коммитов для исправления ситуации.

Большинство рекомендаций сводятся к совету: НЕ ПРАВЬТЕ ОПУБЛИКОВАННЫЕ КОММИТЫ!

И всё было бы отлично, если бы команда git commit --amend не была так хороша и удобна! Ею хочется пользоваться, не оглядываясь на историю коммитов в удалённом репозитории. Поэтому у меня возникло желание автоматизировать такие проверки.

Для подобной автоматизации в системе Git отлично послужат перехватчики (hooks), а именно те, которые работают на стороне клиента. Хранятся они в служебном каталоге .git/hooks и представляют собой файлы скриптов с предопределёнными именами, соответствующими их функциональному предназначению. Для моей задачи подойдёт перехватчик формирования сообщения для коммита prepare-commit-msg.

Суть идеи такова:

  1. Перехватываем команду git commit --amend;
  2. Получаем значение хеша последнего коммита в локальной ветке;
  3. Получаем значение хеша последнего коммита в удалённой ветке;
  4. Сравниваем их, и если они равны, то выводим предупреждение и комментируем содержательную часть сообщения коммита — для того, чтобы при выходе из редактирования коммит отменялся из-за пустого сообщения (“Aborting commit due to empty commit message”).

Содежимое prepare-commit-msg

#!/bin/bash   case "$2,$3" in 	commit,HEAD) 	# получаем short SHA-1 последнего коммита в локальной ветке 	sha1_local=$(git branch -vv | \ 		perl -lne 'print "$1" if /\*{1}\s+\S+\s+(\w+)\s+\[(\S+)\/(\S+).*\]\s+.*/')  	 	# получаем имя удалённой ветки (remote branch) 	remote_branch=$(git branch -vv | \ 		perl -lne 'print "$1/$2" if /\*{1}\s+\S+\s+\w+\s+\[(\S+)\/(\S+).*\]\s+.*/')  	# получаем short SHA-1 последнего коммита в удалённой ветке (remote branch) 	sha1_remote=$(git branch -rv | \ 		awk -v branch=$remote_branch '{ if ($1 == branch) print $2 }')  	if	[ -n "$sha1_local"  ] &&  		[ -n "$sha1_remote" ] &&  		[ "$sha1_local" = "$sha1_remote" ] 	then 		# Закомментируем сообщение коммита, чтобы выход из редактора приводил 		# к отмене коммита из-за отсутствия сообщения 		ci_comment=$(cat "$1" | grep -v '#' | perl -lne 'print "# $_"') 		ci_autogen=$(cat "$1" | grep '#') 		echo -e "$ci_comment" > "$1"  		# добавим текст предупреждения  		echo -e "# ВНИМАНИЕ! ВЫ ПЫТАЕТЕСЬ ОТРЕДАКТИРОВАТЬ УЖЕ ОПУБЛИКОВАННЫЙ КОММИТ!\n" >> "$1" 		echo -e "$ci_autogen" >> "$1"  	fi   ;;    *) ;; esac 

Пример работы данного хука приведён ниже:

Как видно, у пользователя остаётся возможность при желании произвести коммит — для этого ему следует раскомментировать сообщение коммита. Если стоит задача более жёсткого пресечения попыток правки опубликованных коммитов, то в системе Git имеется перехватчик pre-commit, который запускается ещё до создания сообщения коммита.

ссылка на оригинал статьи http://habrahabr.ru/post/221819/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *