Pull to refresh

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

Reading time3 min
Views8.3K
Привет, Хабр!

Тем, кто работает с 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, который запускается ещё до создания сообщения коммита.
Tags:
Hubs:
Total votes 16: ↑15 and ↓1+14
Comments14

Articles