Git hook, изменить файлы коммитов

Я пытаюсь написать скрипт git pre-commit hook, он должен записывать дату коммита в начале измененных файлов. Моя проблема в том, что я не могу добавить измененные файлы в предыдущий коммит. Когда я пытаюсь снова вызвать git commit, он запускается рекурсивно. Как я могу написать скрипт, который добавляет время модификации в конце измененных файлов?

Мой код:

#!/bin/bash

files_modified=`git diff-index --name-only HEAD`

for f in $files_modified; do
    if [[ $f == *.groovy ]]; then
        $line = $(head -1 f)
        if [[ $line == "/%%*" ]];
        then
            sed -i 1d
        fi
        echo "/%% " + $(date +"%m_%d_%Y") + " %%\\" >> f
        git add f
    fi
done 
git commit --amend #recursive
exit

3 ответа

Решение

Вы не можете изменить коммит в хуке предварительной фиксации.
И то, что вы делаете, похоже на механизм раскрытия ключевых слов, который не является наилучшей практикой в ​​Git (или любой DVCS), как описано в разделе " Чтобы поставить префикс" ?<revision-number> на коды Git / Svn ".

Другие подходы включают в себя:

Глядя на ваш хук до фиксации, вы почти что-то работали. Вот минимальные изменения, которые я считаю необходимыми:

    #!/bin/bash
    files_modified=`git diff --cache --name-only --diff-filter=ACM`
            ### fix: use current branch; cached; and only files
    for f in $files_modified; do ### broken: if space in filename(s)
        if [[ $f == *.groovy ]]; then
            line=$(head -1 $f) ### fix: forgot a $ before f
            if [[ $line == "/%%*" ]];
            then
                sed -i 1d "$f" ### fix: forgot file argument
            fi
            echo "/%% " + $(date +"%m_%d_%Y") + " %%\\" >> $f
                    ### fix: forgot a $ before f
            git add -u $f ### fix: forgot a $ before f
        fi
    done
    ### undesired ### git commit --amend #recursive
    ### unneeded ### exit

Тем не менее, я заметил несколько проблем с вашей реализацией. Вы удалите строку, соответствующую "/%%*", в верхней части файла и добавите новую в конец. Каждый раз, когда вы запускаете это, вы всегда будете добавлять новые /%% mm_dd_YYYY %%\ строка в конец файла. Это может быть не то, что вы хотите (1000 коммитов позже, ранее пустой файл будет иметь 1000 строк). Я думаю, что вы хотели сделать, это заменить существующую линию. В этом случае будет работать перевод sed для замены совпадающих строк.

Вот рецепт, который, я думаю, приближается к тому, что вы хотели:

    #!/bin/sh
    TMPFILE="/tmp/${0##*/}.$$"
    for f in $( git diff --cached --name-only --diff-filter=ACM ); do
            # XXX broken: if space in filename(s)
            case "$f" in
            *.groovy) : fall through ;;
            *) continue
            esac
            cp "$f" "$TMPFILE" || continue
            awk -v new="/%% $(date +%m_%d_%Y) %%\\" \
                    'NR==1{sub("^/%% .* %%\\\\$",new)}1' \
                    < "$TMPFILE" > "$f"
            git add -u -- "$f"
    done

Если первая строка файла совпадает /%% ... %%\ затем он обновляется с текущей датой / временем (во время ловушки перед фиксацией).

Однако это было просто для того, чтобы проиллюстрировать, как это можно сделать просто. Это на самом деле не полное решение. Вышеприведенный скрипт не будет работать с файлами, в имени которых есть пробелы, двойные кавычки, обратная косая черта, вкладки и т. Д.

Для полного решения:

  1. Используйте следующий хук pre-commit:

    #!/bin/sh
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 .filters/myfilter
    
  2. Создайте ".filters / myfilter" со следующим содержимым:

    #!/bin/sh
    TMPFILE="/tmp/${0##*/}.$$"
    for f in "$@"; do ### the only difference from above recipe
            case "$f" in
            *.groovy) : fall through ;;
            *) continue
            esac
            cp "$f" "$TMPFILE" || continue
            awk -v new="/%% $(date +%m_%d_%Y) %%\\" \
                    'NR==1{sub("^/%% .* %%\\\\$",new)}1' \
                    < "$TMPFILE" > "$f"
            git add -u -- "$f"
    done
    

Вышеприведенная реализация может обрабатывать любое имя файла, которое вы кидаете.

Другие вопросы по тегам