Question

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
Was it helpful?

Solution

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:

OTHER TIPS

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

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top