11

I'm trying to write a Git pre-commit hook script. It should write the date of commit at the beginning of modified files.

My problem is that I can't add modified files to the previous commit. When I am trying invoke to a Git commit again, it runs recursive. How can I write the script, which appends the time of modification at the end of modified files?

My code:

#!/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 Answers 3

4

You cannot amend a commit in a pre commit hook.
And what you are doing is similar to the keyword expansion mechanism, which is not a best practice with Git (or any DVCS), as explained in "To put the prefix ?<revision-number> to codes by Git/Svn".

Other approaches include:

Sign up to request clarification or add additional context in comments.

Comments

3

Looking at your pre-commit hook, you almost had something that sorta-worked. Here's the minimal changes that I see being required:

    #!/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

However, I notice several issues with your implementation. You will remove a line matching "/%%*" from the top of the file and append a new one to the bottom. Each time you run this, you'll be forever appending a new /%% mm_dd_YYYY %%\ line to the end of the file. That may not be what you want (1000 commits later, a previously-empty file will have 1000 lines). I think what you meant to do was replace the existing line. In which case a sed translation to replace matching lines would work.

Here's a recipe that I think gets closer to what you wanted:

    #!/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

If the first line of the file matches /%% ... %%\ then it is updated with the current date/time (at the time of pre-commit hook).

However, that was just to illustrate how it could be done simply. That is actually not a complete solution. The above script won't work with files that contain spaces in their name, double-quotes, backslashes, tabs, etc.

For a complete solution:

  1. Use the following pre-commit hook:

    #!/bin/sh
    git diff --cached --name-only -z --diff-filter=ACM |
            xargs -r0 .filters/myfilter
    
  2. Create ".filters/myfilter" with the following content:

    #!/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
    

The above implementation can handle any filename you throw at it.

Comments

2

May be git attribute with smudge/clean operation is what you are looking for:

enter image description here

enter image description here

Related: use git smudge/clean to replace file contents

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.