Home | History | Annotate | Download | only in tools
      1 #!/bin/sh
      2 self="$0"
      3 dirname_self=$(dirname "$self")
      4 
      5 usage() {
      6   cat <<EOF >&2
      7 Usage: $self [option]
      8 
      9 This script applies a whitespace transformation to the commit at HEAD. If no
     10 options are given, then the modified files are left in the working tree.
     11 
     12 Options:
     13   -h, --help     Shows this message
     14   -n, --dry-run  Shows a diff of the changes to be made.
     15   --amend        Squashes the changes into the commit at HEAD
     16                      This option will also reformat the commit message.
     17   --commit       Creates a new commit containing only the whitespace changes
     18   --msg-only     Reformat the commit message only, ignore the patch itself.
     19 
     20 EOF
     21   rm -f ${CLEAN_FILES}
     22   exit 1
     23 }
     24 
     25 
     26 log() {
     27   echo "${self##*/}: $@" >&2
     28 }
     29 
     30 
     31 vpx_style() {
     32   for f; do
     33     case "$f" in
     34       *.h|*.c|*.cc)
     35         "${dirname_self}"/vpx-astyle.sh "$f"
     36         ;;
     37     esac
     38   done
     39 }
     40 
     41 
     42 apply() {
     43   [ $INTERSECT_RESULT -ne 0 ] && patch -p1 < "$1"
     44 }
     45 
     46 
     47 commit() {
     48   LAST_CHANGEID=$(git show | awk '/Change-Id:/{print $2}')
     49   if [ -z "$LAST_CHANGEID" ]; then
     50     log "HEAD doesn't have a Change-Id, unable to generate a new commit"
     51     exit 1
     52   fi
     53 
     54   # Build a deterministic Change-Id from the parent's
     55   NEW_CHANGEID=${LAST_CHANGEID}-styled
     56   NEW_CHANGEID=I$(echo $NEW_CHANGEID | git hash-object --stdin)
     57 
     58   # Commit, preserving authorship from the parent commit.
     59   git commit -a -C HEAD > /dev/null
     60   git commit --amend -F- << EOF
     61 Cosmetic: Fix whitespace in change ${LAST_CHANGEID:0:9}
     62 
     63 Change-Id: ${NEW_CHANGEID}
     64 EOF
     65 }
     66 
     67 
     68 show_commit_msg_diff() {
     69   if [ $DIFF_MSG_RESULT -ne 0 ]; then
     70     log "Modified commit message:"
     71     diff -u "$ORIG_COMMIT_MSG" "$NEW_COMMIT_MSG" | tail -n +3
     72   fi
     73 }
     74 
     75 
     76 amend() {
     77   show_commit_msg_diff
     78   if [ $DIFF_MSG_RESULT -ne 0 ] || [ $INTERSECT_RESULT -ne 0 ]; then
     79     git commit -a --amend -F "$NEW_COMMIT_MSG"
     80   fi
     81 }
     82 
     83 
     84 diff_msg() {
     85   git log -1 --format=%B > "$ORIG_COMMIT_MSG"
     86   "${dirname_self}"/wrap-commit-msg.py \
     87       < "$ORIG_COMMIT_MSG" > "$NEW_COMMIT_MSG"
     88   cmp -s "$ORIG_COMMIT_MSG" "$NEW_COMMIT_MSG"
     89   DIFF_MSG_RESULT=$?
     90 }
     91 
     92 
     93 # Temporary files
     94 ORIG_DIFF=orig.diff.$$
     95 MODIFIED_DIFF=modified.diff.$$
     96 FINAL_DIFF=final.diff.$$
     97 ORIG_COMMIT_MSG=orig.commit-msg.$$
     98 NEW_COMMIT_MSG=new.commit-msg.$$
     99 CLEAN_FILES="${ORIG_DIFF} ${MODIFIED_DIFF} ${FINAL_DIFF}"
    100 CLEAN_FILES="${CLEAN_FILES} ${ORIG_COMMIT_MSG} ${NEW_COMMIT_MSG}"
    101 
    102 # Preconditions
    103 [ $# -lt 2 ] || usage
    104 
    105 # Check that astyle supports pad-header and align-pointer=name
    106 if ! astyle --pad-header --align-pointer=name < /dev/null; then
    107   log "Install astyle v1.24 or newer"
    108   exit 1
    109 fi
    110 
    111 if ! git diff --quiet HEAD; then
    112   log "Working tree is dirty, commit your changes first"
    113   exit 1
    114 fi
    115 
    116 # Need to be in the root
    117 cd "$(git rev-parse --show-toplevel)"
    118 
    119 # Collect the original diff
    120 git show > "${ORIG_DIFF}"
    121 
    122 # Apply the style guide on new and modified files and collect its diff
    123 for f in $(git diff HEAD^ --name-only -M90 --diff-filter=AM); do
    124   case "$f" in
    125     third_party/*) continue;;
    126     nestegg/*) continue;;
    127   esac
    128   vpx_style "$f"
    129 done
    130 git diff --no-color --no-ext-diff > "${MODIFIED_DIFF}"
    131 
    132 # Intersect the two diffs
    133 "${dirname_self}"/intersect-diffs.py \
    134     "${ORIG_DIFF}" "${MODIFIED_DIFF}" > "${FINAL_DIFF}"
    135 INTERSECT_RESULT=$?
    136 git reset --hard >/dev/null
    137 
    138 # Fixup the commit message
    139 diff_msg
    140 
    141 # Handle options
    142 if [ -n "$1" ]; then
    143   case "$1" in
    144     -h|--help) usage;;
    145     -n|--dry-run) cat "${FINAL_DIFF}"; show_commit_msg_diff;;
    146     --commit) apply "${FINAL_DIFF}"; commit;;
    147     --amend) apply "${FINAL_DIFF}"; amend;;
    148     --msg-only) amend;;
    149     *) usage;;
    150   esac
    151 else
    152   apply "${FINAL_DIFF}"
    153   if ! git diff --quiet; then
    154     log "Formatting changes applied, verify and commit."
    155     log "See also: http://www.webmproject.org/code/contribute/conventions/"
    156     git diff --stat
    157   fi
    158 fi
    159 
    160 rm -f ${CLEAN_FILES}
    161