问题
I'm trying to write git pre-commit hook script, it should write date of commit at the begining of modified files. My problem is that i can't add modified files to previous commit. When i trying invoke git commit again it runs recursive. How i can write script, which append 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
回答1:
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:
- generating a separate file with the information you want in it (and then commit).
See for instance "Expanding Git SHA1 information into a checkin without archiving?". - storing that information in git note (that are separates from commits and don't change the SHA1).
See "Adding Git notes to a blob".
回答2:
May be git attribute with smudge/clean operation is what you are looking for:
Related: use git smudge/clean to replace file contents
回答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:
Use the following pre-commit hook:
#!/bin/sh git diff --cached --name-only -z --diff-filter=ACM | xargs -r0 .filters/myfilter
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.
来源:https://stackoverflow.com/questions/14640306/git-hook-modify-commit-files