Created
May 9, 2014 18:53
-
-
Save elmart/8d6dc48ebdc8d04ae5cb to your computer and use it in GitHub Desktop.
Git prepare-commit-msg hook to automatically add task context headings
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# Called by "git commit" with the name of the file that has the | |
# commit message, followed by the description of the commit | |
# message's source. The hook's purpose is to edit the commit | |
# message file. If the hook fails with a non-zero status, | |
# the commit is aborted. | |
# | |
# This hook automatically inserts Task/Subtask headings into commit messages. | |
# Each line in .git/task file gets added as a level. | |
# This is a prepare-commit time hook, so headings are already there | |
# when the commit message is presented to the user after issuing 'git commit'. | |
# That means headings (full message content in fact) can still be edited | |
# if desired before really committing, which will occur when saving and quitting. | |
# Quitting with an empty message will abort the commit, as usual. | |
# | |
# For example, if contents of .git/task file is | |
# Simplify memory management | |
# Use safe functions | |
# Then, when you issue 'git commit', the message presented to you | |
# will already include: | |
# Simplify memory management: Use safe functions:. | |
# So that you only have to fill up the last (Step) part. | |
# As you can see above, it also adds a dot at the end, so that you can | |
# quickly type '$i', fill up the Step, and be sure there's a dot at the end | |
# withouth actually having to type it. | |
# | |
# You can manually edit .git/task file if you want. | |
# But, to populate it more comfortably, you can add this alias to git config: | |
# [alias] | |
# task = !vi .git/task | |
# That way, issuing 'git task' at the command line will open an editor in which you | |
# can change current task/subtasks. | |
# If you remove .git/task file, or leave it empty, this does nothing. | |
# | |
# This also works when the message was already populated. For example, after | |
# issuing a git rebase -i that chose to rename some commit. | |
# In that case, it checks due headings are there, and adds them before the | |
# existing message if not. | |
# If the existing message starts with "LOCAL", then it doesn't add the headings. | |
# That allows you having some local commits, that you never publish, but that | |
# you systematically rebase on top of your branches for something to work | |
# different locally just for you. | |
# All these changes to existing message apply only to the first line. Other existing | |
# lines are untouched. | |
# | |
# As a last thing, it can optionally add the project's name before the headings. | |
# This is disabled by default. | |
# | |
# @author elmart<[email protected]> | |
hook_dir="$(dirname "$0")" | |
project_dir="$(cd "${hook_dir}/../.." && pwd)" | |
project_name="$(basename "$project_dir")" | |
project_name_separator=" - " | |
add_project_name=0 | |
task_file="$hook_dir/../task" | |
task_separator=": " | |
temp_file=`mktemp /tmp/gitpreparecommithook.XXXXXX` | |
awk -v project_name="$project_name" \ | |
-v project_name_separator="$project_name_separator" \ | |
-v add_project_name="$add_project_name" \ | |
-v task_file="$task_file" \ | |
-v task_separator="$task_separator" ' | |
function ltrim(s) { sub(/^[ \t\r\n]+/, "", s); return s } | |
function rtrim(s) { sub(/[ \t\r\n]+$/, "", s); return s } | |
function trim(s) { return rtrim(ltrim(s)); } | |
BEGIN { | |
current_field = 1 | |
message_prefix = "" | |
message = "" | |
} | |
NR == 1 && /^LOCAL/ { | |
next | |
} | |
NR == 1 { | |
# Calculate message prefix from task file, if any | |
if (system("test -f " task_file) == 0) { | |
while ((getline task_file_line < task_file) > 0) { | |
message_prefix = message_prefix task_file_line task_separator | |
} | |
} | |
# Recognize project name and project name separator | |
if (tolower($current_field) == tolower(project_name) \ | |
&& trim($(current_field + 1)) == trim(project_name_separator)) { | |
current_field += 2 | |
} | |
# Get rest of message | |
while (current_field <= NF) { | |
message = message == "" ? $current_field : message " " $current_field | |
current_field++ | |
} | |
# Remove message prefix from message if already present | |
i = index(message, message_prefix) | |
if (i > 0) { | |
message = substr(message, 1, i - 1) substr(message, i + length(message_prefix)) | |
} | |
# Add message prefix to message | |
message = message_prefix message | |
# Add full-stop to end of message if not present | |
if (substr(message, length(message), 1) != ".") { | |
message = message "." | |
} | |
# Print resulting message | |
if (add_project_name) { | |
print project_name project_name_separator message | |
} | |
else { | |
print message | |
} | |
} | |
NR > 1 && !/^git-svn-id:/ { | |
} | |
' "$1" > "$temp_file" | |
if [ $? -eq 0 ] | |
then | |
cp "$temp_file" "$1" | |
else | |
exit 1 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment