Как мы проверяем сообщения фиксации для push-уведомлений?

Исходя из CVS, у нас есть политика, согласно которой сообщения фиксации должны быть помечены номером ошибки (простой суффикс "... [9999]"). Сценарий CVS проверяет это во время фиксации и отклоняет фиксацию, если сообщение не соответствует требованиям.

Коммит-msg git hook делает это на стороне разработчика, но мы считаем полезным, чтобы автоматические системы проверяли и напоминали нам об этом.

Во время git push commit-msg не запускается. Есть ли другой хук во время отправки, который может проверять сообщения фиксации?

Как мы проверяем сообщения фиксации во время git push?


person Dale Forester    schedule 26.03.2010    source источник


Ответы (5)


Использование хука обновления

Вы знаете о хуках - пожалуйста, прочтите документация о них! Хук, который вам, вероятно, нужен, это update, который запускается один раз для каждой ссылки. (Хук pre-receive запускается один раз для всего нажатия) На SO уже есть множество вопросов и ответов об этих хуках; в зависимости от того, что вы хотите сделать, вы, вероятно, можете найти руководство о том, как написать хук, если вам это нужно.

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

Этот хук можно использовать для предотвращения принудительного обновления определенных ссылок, убедившись, что имя объекта является объектом фиксации, который является потомком объекта фиксации, названного старым именем объекта. То есть, чтобы применить политику только быстрой перемотки вперед.

Его также можно использовать для регистрации старого.. нового статуса.

И конкретика:

Хук выполняется один раз для каждой обновляемой ссылки и принимает три параметра:

  • имя обновляемой ссылки,
  • старое имя объекта, хранящееся в ссылке,
  • и новое имя объекта, которое будет сохранено в ref.

Так, например, если вы хотите убедиться, что ни одна из тем фиксации не длиннее 80 символов, очень примитивной реализацией будет:

#!/bin/bash
long_subject=$(git log --pretty=%s $2..$3 | egrep -m 1 '.{81}')
if [ -n "$long_subject" ]; then
    echo "error: commit subject over 80 characters:"
    echo "    $long_subject"
    exit 1
fi

Конечно, это игрушечный пример; в общем случае вы должны использовать вывод журнала, содержащий полное сообщение фиксации, разделить его для каждой фиксации и вызывать свой проверочный код для каждого отдельного сообщения фиксации.

Почему вам нужен хук обновления

Это обсуждалось/уточнялось в комментариях; вот резюме.

Хук обновления запускается один раз для каждой ссылки. Ссылка — это указатель на объект; в данном случае мы говорим о ветках и тегах, и, как правило, просто о ветках (люди не часто вставляют теги, так как они обычно используются только для маркировки версий).

Теперь, если пользователь отправляет обновления в две ветки, основную и экспериментальную:

o - o - o (origin/master) - o - X - o - o (master)
 \
  o - o (origin/experimental) - o - o (experimental)

Предположим, что X — плохой коммит, т. е. тот, который не сможет выполнить хук commit-msg. Ясно, что мы не хотим принимать толчок к мастерству. Таким образом, хук обновления отклоняет это. Но в коммитах на экспериментальных нет ничего плохого! Хук обновления принимает его. Таким образом, origin/master остается неизменным, а origin/experimental обновляется:

o - o - o (origin/master) - o - X - o - o (master)
 \
  o - o - o - o (origin/experimental, experimental)

Хук pre-receive запускается только один раз, непосредственно перед началом обновления ссылок (до первого запуска хука обновления). Если бы вы использовали его, вам пришлось бы вызвать сбой всей отправки, тем самым говоря, что, поскольку на мастере было плохое сообщение о коммите, вы почему-то больше не верите, что коммиты на экспериментальном сервере хорошие, даже если их сообщения в порядке!

person Cascabel    schedule 26.03.2010
comment
Я думаю, что крючок, который ищет OP, - это предварительное получение, поскольку он / она хочет отклонить весь толчок в зависимости от сообщения фиксации. Однако, насколько мне известно, ни предварительное получение, ни обновление не получают сообщение фиксации в качестве входных данных. Поэтому использование commit-msg, вероятно, будет лучшим решением. - person Can Berk Güder; 26.03.2010
comment
@Can: я почти уверен, что ОП хочет обновления, а не предварительного получения. Весь толчок означает толчок для всех ветвей. Если пользователь пытается отправить обновления в три ветки, и только одна из них содержит недопустимые сообщения фиксации, две другие все равно должны быть приняты! - person Cascabel; 26.03.2010
comment
@Can: И нет, сообщение фиксации не является частью ввода, а старое и новое имена объектов (фиксации) (SHA1). Обратите внимание, что хук обновления выполняется непосредственно перед обновлением ссылок (после получения объектов фиксации). Таким образом, хук может использовать git log для проверки всего, что он хочет, о коммитах между старым и новым, включая их сообщения коммитов. - person Cascabel; 26.03.2010
comment
@Jefromi » Я не уверен, что согласен, но я думаю, что эта часть субъективна. ИМО, я бы рассматривал это как транзакцию: если какая-то часть того, что вы сделали, плоха, остановите все это, чтобы вы могли исправить ошибки. - person John Feminella; 26.03.2010
comment
@John: Это было бы самым простым и желательным. Все это должно потерпеть неудачу, если какая-либо часть недействительна. - person Dale Forester; 26.03.2010
comment
@John: Ну, ты можешь сделать свой собственный выбор. Но вот моя общая мысль. Это согласуется с общей философией веток в git, рассматривая каждую из них как транзакцию. Вы останавливаете отправку этой отдельной ветки, если в ней есть один плохой коммит, даже если на нем 500 новых коммитов. Но две разные ветки — это две разные вещи — разные темы, разные возможности. Если вы работаете над двумя вещами и делаете ошибку в одной, это не должно влиять на другую. - person Cascabel; 26.03.2010
comment
@shovas: Но какое отношение недопустимое сообщение фиксации X в ветке A имеет к ветке B? B не может содержать X, иначе он тоже не сработает. Итак, B — это серия коммитов, и все они в порядке. Почему в проталкивании B должно быть отказано только потому, что разработчик тоже сделал что-то не так на A? Если они отправят ветки по отдельности (git push A, git push B), A потерпит неудачу, а B преуспеет. - person Cascabel; 26.03.2010
comment
@shovas: я согласен с тем, что нажатие всего должно завершиться неудачно, если одна часть недействительна, но правильное определение в этом контексте состоит в том, что целое - это ветвь, а одна часть - это отдельная фиксация. Контент в одной ветке не может сделать недействительным контент в другой ветке. - person Cascabel; 26.03.2010
comment
@Jefromi: Вы делаете хорошее замечание. Я предполагаю, что моя неуверенность заключается в том, является ли идея один раз за ссылку в документах. Означает ли это, что все коммиты в ветке являются одной ссылкой? - person Dale Forester; 26.03.2010
comment
@shovas: ref - это указатель на объект, обычно фиксируемый. Примерами ссылок являются теги и ветки. Поэтому, когда в документах говорится, что один раз для каждой ссылки обновляется в push-уведомлении, они имеют в виду один раз для каждой ветки/тега. Если вы сделали 50 коммитов в ветке с момента отправки, git загружает все 50, а затем обновляет ссылку. Старая позиция ref находится непосредственно перед отправкой первого коммита; новый - это последний нажатый коммит. Хук запускается непосредственно перед обновлением ссылки; вы можете просмотреть все 50 коммитов. Если хук не сработает, рефери вообще не будет двигаться. - person Cascabel; 26.03.2010
comment
но git log --pretty=%s $2..$3 не работает при отправке новой ветки на удаленный сервер. Сообщение об ошибке: Invalid revision range 0000000000000000000000000000000000000000..d480cc0993800cadd6c23d00c608fe5272300896 - person Zarathustra; 18.05.2016

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

#! /usr/bin/perl

my $errors = 0;
while (<>) {
  chomp;
  next unless my($old,$new) =
    m[ ^ ([0-9a-f]+) \s+   # old SHA-1
         ([0-9a-f]+) \s+   # new SHA-1
         refs/heads/master # ref
       \s* $ ]x;

  chomp(my @commits = `git rev-list $old..$new`);
  if ($?) {
    warn "git rev-list $old..$new failed\n";
    ++$errors, next;
  }

  foreach my $sha1 (@commits) {
    my $msg = `git cat-file commit $sha1`;
    if ($?) {
      warn "git cat-file commit $sha1 failed";
      ++$errors, next;
    }

    $msg =~ s/\A.+? ^$ \s+//smx;
    unless ($msg =~ /\[\d+\]/) {
      warn "No bug number in $sha1:\n\n" . $msg . "\n";
      ++$errors, next;
    }
  }
}

exit $errors == 0 ? 0 : 1;

Это требует, чтобы все коммиты в push-уведомлениях имели номер ошибки где-то в соответствующих сообщениях коммитов, а не только в подсказке. Например:

$ git log --pretty=oneline origin/master..HEAD
354d783efd7b99ad8666db45d33e30930e4c8bb7 second [123]
aeb73d00456fc73f5e33129fb0dcb16718536489 no bug number

$ git push origin master
Counting objects: 6, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (5/5), 489 bytes, done.
Total 5 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (5/5), done.
No bug number in aeb73d00456fc73f5e33129fb0dcb16718536489:

no bug number

To file:///tmp/bare.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'file:///tmp/bare.git'

Скажем, мы исправим проблему, объединив два коммита вместе и отправив результат:

$ git rebase -i origin/master
[...]

$ git log --pretty=oneline origin/master..HEAD
74980036dbac95c97f5c6bfd64a1faa4c01dd754 second [123]

$ git push origin master
Counting objects: 4, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 279 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To file:///tmp/bare.git
   8388e88..7498003  master -> master
person Greg Bacon    schedule 27.03.2010

Это версия pre-receive для Python, на завершение которой у меня ушло некоторое время, надеюсь, она поможет другим. В основном я использую его с Trac, но его можно легко модифицировать для других целей.

Я также написал инструкции по изменению исторического сообщения коммита, что немного сложнее, чем я думал.

#!/usr/bin/env python
import subprocess

import sys 
import re

def main():
    input  = sys.stdin.read()
    oldrev, newrev, refname = input.split(" ")
    separator = "----****----"


    proc = subprocess.Popen(["git", "log", "--format=%H%n%ci%n%s%b%n" + separator, oldrev + ".." +  newrev], stdout=subprocess.PIPE)
    message = proc.stdout.read()
    commit_list = message.strip().split(separator)[:-1] #discard the last line

    is_valid = True

    print "Parsing message:"
    print message

    for commit in commit_list:
        line_list = commit.strip().split("\n")
        hash = line_list[0]
        date = line_list[1]
        content = " ".join(line_list[2:])
        if not re.findall("refs *#[0-9]+", content): #check for keyword
            is_valid = False

    if not is_valid:
        print "Please hook a trac ticket when commiting the source code!!!" 
        print "Use this command to change commit message (one commit at a time): "
        print "1. run: git rebase --interactive " + oldrev + "^" 
        print "2. In the default editor, modify 'pick' to 'edit' in the line whose commit you want to modify"
        print "3. run: git commit --amend"
        print "4. modify the commit message"
        print "5. run: git rebase --continue"
        print "6. remember to add the ticket number next time!"
        print "reference: http://stackoverflow.com/questions/1186535/how-to-modify-a-specified-commit"

        sys.exit(1)

main()
person Walty Yeung    schedule 06.03.2014

Вам нужно сделать скрипт на предварительном приеме.

В этом скрипте вы получаете старую и новую ревизию. Вы можете проверить все фиксации и вернуть false, если что-то из этого плохо.

person shingara    schedule 26.03.2010

Вы не указали, какой у вас баг-трекер, но если это JIRA, то надстройка с именем Политика фиксации может сделать это без какого-либо программирования.

Вы можете настроить условие фиксации, которое требует, чтобы сообщение фиксации соответствовало регулярному выражению. Если это не так, отправка отклоняется, и разработчик должен изменить (исправить) сообщение фиксации, а затем отправить еще раз.

person Ferenc Kiss    schedule 15.12.2015