X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=build%2Fcommit-msg;h=e0803eaeaa046a94af75171eda9966bea083933d;hp=80a3442ae16ba361e6f8e140ff49bc03b3cde481;hb=034a0ef411296dd022f4ec2dfbbe7d60970e92bf;hpb=a0bdd95f385168306b85fdee1e6bcf3dead772eb diff --git a/build/commit-msg b/build/commit-msg index 80a3442..e0803ea 100755 --- a/build/commit-msg +++ b/build/commit-msg @@ -12,108 +12,281 @@ # Should be installed as .git/hooks/commit-msg. # -ORIGINAL="$1" -REVISED="$(mktemp "$1.XXXXXX")" -SIGNOFF="Signed-off-by:" -CHANGEID="Change-Id:" +init() { + set -a + readonly ORIGINAL="$1" + readonly REVISED="$(mktemp "$ORIGINAL.XXXXXX")" + readonly SAVE="$(basename $ORIGINAL).$(date +%Y%m%d.%H%M%S)" + readonly SIGNOFF="Signed-off-by:" + readonly CHANGEID="Change-Id:" + readonly TESTPARAMS="Test-Parameters:" + readonly INNOCUOUS=$(echo \ + Acked-by \ + Tested-by \ + Reported-by \ + Reviewed-by \ + CC \ + | tr ' ' '|') + readonly WIDTH_SUM=62 + readonly WIDTH_REG=70 + readonly JIRA_FMT_A="^[A-Z]\{2,5\}-[0-9]\{1,5\} [-a-z0-9]\{2,11\}: " + readonly JIRA_FMT_B="^[A-Z]\{2,5\}-[0-9]\{1,5\} " -# Check for, and add if missing, a unique Change-Id + # Identify a name followed by an email address. + # + readonly EMAILPAT=$'[ \t]*[^<> ]* [^<>]* <[^@ \t>]+@[a-zA-Z0-9.-]+\.[a-z]+>' + + HAS_ERROR=false + HAS_SUMMARY=false + HAS_LAST_BLANK=false + HAS_BODY=false + HAS_SIGNOFF=false + HAS_CHANGEID=false + + IS_WRAPPING_UP=false + + LINE="" + NUM=0 + set +a +} + +# die: commit-msg fatal error: script error or empty input message +# All output redirected to stderr. +# +die() { + echo "commit-msg fatal error: $*" + test -f "$REVISED" && rm -f "$REVISED" + exit 1 +} 1>&2 + +# Called when doing the final "wrap up" clause because we've found +# one of the tagged lines that belongs in the final section. +# +function ck_wrapup() { + $IS_WRAPPING_UP && return + + $HAS_LAST_BLANK || error "blank line must preceed signoff section" + $HAS_SUMMARY || error "missing commit summary line." + $HAS_BODY || error "missing commit description." + + HAS_LAST_BLANK=false + IS_WRAPPING_UP=true +} + +function do_signoff() { + ck_wrapup + # Signed-off-by: First Last + local txt=$(echo "${LINE#*: }" | grep -E "${EMAILPAT}") + if (( ${#txt} == 0 )); then + error "$SIGNOFF line requires name and email address" + else + HAS_SIGNOFF=true # require at least one + fi +} + +function do_changeid() { + ck_wrapup + $HAS_CHANGEID && error "multiple $CHANGEID lines are not allowed" + + # Change-Id: I1234567890123456789012345678901234567890 + # capital "I" plus 40 hex digits + # + local txt=$(echo "$LINE" | grep "^$CHANGEID I[0-9a-fA-F]\{40\}\$") + (( ${#txt} > 0 )) || + error "has invalid $CHANGEID line for Gerrit tracking" + + HAS_CHANGEID=true +} + +# All "innocuous" lines specify a person and email address +# +function do_innocuous() { + ck_wrapup + local txt=$(echo "${LINE#*: }" | grep -E "${EMAILPAT}") + (( ${#txt} == 0 )) && error "invalid name and address" +} + +function do_default_line() { + $IS_WRAPPING_UP && { + error "invalid signoff section line" + return + } + if (( NUM == 1 )); then + HAS_JIRA_COMPONENT=$(echo "$LINE" | grep "$JIRA_FMT_A") + + if (( ${#HAS_JIRA_COMPONENT} == 0 )); then + HAS_JIRA=$(echo "$LINE" | grep "$JIRA_FMT_B") + if (( ${#HAS_JIRA} > 0 )); then + error "has no component in summary." + else + error "missing JIRA ticket number." + fi + elif (( ${#LINE} > WIDTH_SUM )); then + error "summary longer than $WIDTH_SUM columns." + else + HAS_SUMMARY=true + fi + + elif (( ${#LINE} > WIDTH_REG )); then + error "has line longer than $WIDTH_REG columns." + elif ! $HAS_BODY && ! $HAS_LAST_BLANK; then + error "has no blank line after summary." + else + HAS_BODY=true + fi + HAS_LAST_BLANK=false +} + +# Add a new unique Change-Id +# new_changeid() { - { - git var GIT_AUTHOR_IDENT - git var GIT_COMMITTER_IDENT - git write-tree - git rev-parse HEAD 2>/dev/null - grep -v "$SIGNOFF" "$ORIGINAL" | git stripspace -s - } | git hash-object -t commit --stdin + local NEWID=$({ + git var GIT_AUTHOR_IDENT + git var GIT_COMMITTER_IDENT + git write-tree + git rev-parse HEAD 2>/dev/null + grep -v "^$SIGNOFF" "$ORIGINAL" | git stripspace -s + } | git hash-object --stdin) + (( ${#NEWID} > 0 )) || + die "git hash-object failed for $CHANGEID:" + + echo "$CHANGEID I$NEWID" } +# A commit message error was encountered. +# All output redirected to stderr. +# +error() { + (( ${#LINE} > 0 )) && echo "line $NUM: $LINE" + echo "error: commit message $*" | fmt + HAS_ERROR=true +} 1>&2 + usage() { - [ "$LINE" ] && echo "error:$NUM: $LINE" 1>&2 && echo "" 1>&2 + exec 1>&2 + cat <<- EOF - cat 1>&2 <<- EOF - Commit message $1 - $2 - See http://wiki.whamcloud.com/display/PUB/Submitting+Changes + See http://wiki.whamcloud.com/display/PUB/Commit+Comments for full details. An example valid commit comment is: LU-nnn component: short description of change under 64 columns - A more detailed explanation. This can be as detailed as you'd like. - Please explain both what problem was solved and a good high-level - description of how it was solved. Wrap lines at 70 columns or less. + The "component:" should be a lower-case single-word subsystem of the + Lustre code that best encompasses the change being made. Examples of + components include modules like: llite, lov, lmv, osc, mdc, ldlm, lnet, + ptlrpc, mds, oss, osd, ldiskfs, libcfs, socklnd, o2iblnd; functional + subsystems like: recovery, quota, grant; and auxilliary areas like: + build, tests, docs. This list is not exhaustive, but is a guideline. + + The commit comment should contain a detailed explanation of the change + being made. This can be as long as you'd like. Please give details + of what problem was solved (including error messages or problems that + were seen), a good high-level description of how it was solved, and + which parts of the code were changed (including important functions + that were changed, if this is useful to understand the patch, and + for easier searching). Wrap lines at/under $WIDTH_REG columns. + + Finish the comment with a blank line and a blank-line-free + sign off section: $SIGNOFF Your Real Name $CHANGEID Ixxxx(added automatically if missing)xxxx + + The "$CHANGEID" line should only be there when updating a previous + commit/submission. Copy the one from the original commit. + + The "sign off section" may also include several other tag lines: + $(for T in $(tr '|' ' ' <<< "$INNOCUOUS"); do \ + echo " $T: Some Person "; \ + done) + $TESTPARAMS optional additional test parameters + {Organization}-bug-id: associated external change identifier EOF - exit 1 + mv "$ORIGINAL" "$SAVE" && + echo "$0: saved original commit comment to $SAVE" 1>&2 } -export HAS_SIGNOFF=false -export HAS_SUMMARY=false -export HAS_BLANK=false -export HAS_COMMENTS=false - -grep -q "^$CHANGEID" "$ORIGINAL" && HAS_CHANGEID=true || HAS_CHANGEID=false - -export LINE="" -export NUM=1 - -IFS="" -while read LINE; do - LENGTH=$(echo $LINE | wc -c) - - case "$LINE" in - $SIGNOFF*) - $HAS_SUMMARY || usage "missing summary before $SIGNOFF." - $HAS_BLANK || usage "missing blank line before $SIGNOFF." - GOOD=$(echo "$LINE" | grep "^$SIGNOFF .* .* <.*@.*>") - [ -z "$GOOD" ] && - usage "missing valid commit summary line to show" \ - "agreement with code submission requirements at" - HAS_SIGNOFF=true - echo $LINE - $HAS_CHANGEID || echo "$CHANGEID I$(new_changeid)" - ;; - $CHANGEID*) - $HAS_SUMMARY || usage "missing summary before $CHANGEID line." - $HAS_BLANK || usage "missing blank line before $CHANGEID line." - HAS_CHANGEID=true - echo $LINE - ;; - "") [ $HAS_SUMMARY -a $NUM -eq 2 ] && HAS_BLANK=true - echo $LINE - ;; - \#*) HAS_COMMENTS=true - continue - ;; - *) if [ $NUM -eq 1 ]; then - FMT="^[A-Z]\{2,5\}-[0-9]\{1,5\} [-a-z0-9]\{2,9\}: " - GOOD=$(echo "$LINE" | grep "$FMT") - [ $LENGTH -gt 64 ] && - usage "summary longer than 64 columns." - if [ -z "$GOOD" ]; then - FMT="^[A-Z]\{2,5\}-[0-9]\{1,5\} " - NO_SUBSYS=$(echo "$LINE" | grep "$FMT") - [ "$NO_SUBSYS" ] && - usage "has no subsys: in commit summary" - usage "missing valid commit summary line." - fi - HAS_SUMMARY=true - elif [ $LENGTH -gt 70 ]; then - usage "has lines longer than 70 columns." - fi - echo $LINE - ;; - esac - - NUM=$((NUM + 1)) -done < "$ORIGINAL" > "$REVISED" - -[ $NUM -eq 1 ] && exit 1 # empty file - -LINE="" -$HAS_SUMMARY || usage "missing commit summary line." -$HAS_SIGNOFF || usage "missing $SIGNOFF line." -$HAS_CHANGEID && rm "$REVISED" || mv "$REVISED" "$ORIGINAL" +init ${1+"$@"} +exec 3< "$ORIGINAL" 4> "$REVISED" || exit 1 + +while IFS= read -u3 LINE; do + ((NUM += 1)) + case "$LINE" in + $SIGNOFF* ) do_signoff ;; + $CHANGEID* ) do_changeid ;; + $TESTPARAMS* ) ck_wrapup ;; + + "") + HAS_LAST_BLANK=true + $IS_WRAPPING_UP && continue + ;; + + \#*) + continue ## ignore and suppress comments + ;; + + "diff --git a/"* ) + # Beginning of uncommented diffstat from "commit -v". If + # there are diff and index lines, skip the rest of the input: + # diff --git a/build/commit-msg b/build/commit-msg + # index 80a3442..acb4c50 100755 + # deleted file mode 100644 + # old mode 100644 + # If a "diff --git" line is not followed by one of these + # lines, do the default line processing on both lines. + # + IFS= read -u3 INDEX || break + ((NUM += 1)) + case "$INDEX" in + "index "[0-9a-fA-F]*) break ;; + "deleted file mode "*) break ;; + "old mode "*) break ;; + "new file mode "*) break ;; + esac + LINE=${LINE}$'\n'${INDEX} + do_default_line + ;; + + *) + if [[ "$LINE" =~ ^($INNOCUOUS): ]]; then + do_innocuous + elif [[ "$LINE" =~ ^[A-Za-z0-9_-]+-bug-id: ]]; then + ck_wrapup + else + # Allow arbitrary external bug identifiers for tracking. + # I can't seem to find a pattern for the "case" that + # checks for "*-bug-id", so this is checked here. + do_default_line + fi + ;; + esac + + echo "$LINE" >&4 +done + +(( NUM <= 0 )) && die "empty commit message" + +unset LINE +$HAS_SIGNOFF || error "missing valid $SIGNOFF: line." + +if $HAS_ERROR; then + exec 3<&- 4>&- + usage + rm "$REVISED" + exit 1 +fi + +$HAS_CHANGEID || new_changeid >&4 +exec 3<&- 4>&- + +mv "$REVISED" "$ORIGINAL" + +## Local Variables: +## Mode: shell-script +## sh-basic-offset: 8 +## sh-indent-after-do: 8 +## sh-indentation: 8 +## sh-indent-for-case-label: 0 +## sh-indent-for-case-alt: 8 +## End: