#!/bin/sh self="$0" dirname_self=$(dirname "$self") usage() { cat <&2 Usage: $self [option] This script applies a whitespace transformation to the commit at HEAD. If no options are given, then the modified files are left in the working tree. Options: -h, --help Shows this message -n, --dry-run Shows a diff of the changes to be made. --amend Squashes the changes into the commit at HEAD This option will also reformat the commit message. --commit Creates a new commit containing only the whitespace changes --msg-only Reformat the commit message only, ignore the patch itself. EOF rm -f ${CLEAN_FILES} exit 1 } log() { echo "${self##*/}: $@" >&2 } vpx_style() { astyle --style=bsd --min-conditional-indent=0 --break-blocks \ --pad-oper --pad-header --unpad-paren \ --align-pointer=name \ --indent-preprocessor --convert-tabs --indent-labels \ --suffix=none --quiet "$@" sed -i 's/[[:space:]]\{1,\},/,/g' "$@" } apply() { [ $INTERSECT_RESULT -ne 0 ] && patch -p1 < "$1" } commit() { LAST_CHANGEID=$(git show | awk '/Change-Id:/{print $2}') if [ -z "$LAST_CHANGEID" ]; then log "HEAD doesn't have a Change-Id, unable to generate a new commit" exit 1 fi # Build a deterministic Change-Id from the parent's NEW_CHANGEID=${LAST_CHANGEID}-styled NEW_CHANGEID=I$(echo $NEW_CHANGEID | git hash-object --stdin) # Commit, preserving authorship from the parent commit. git commit -a -C HEAD > /dev/null git commit --amend -F- << EOF Cosmetic: Fix whitespace in change ${LAST_CHANGEID:0:9} Change-Id: ${NEW_CHANGEID} EOF } show_commit_msg_diff() { if [ $DIFF_MSG_RESULT -ne 0 ]; then log "Modified commit message:" diff -u "$ORIG_COMMIT_MSG" "$NEW_COMMIT_MSG" | tail -n +3 fi } amend() { show_commit_msg_diff if [ $DIFF_MSG_RESULT -ne 0 ] || [ $INTERSECT_RESULT -ne 0 ]; then git commit -a --amend -F "$NEW_COMMIT_MSG" fi } diff_msg() { git log -1 --format=%B > "$ORIG_COMMIT_MSG" "${dirname_self}"/wrap-commit-msg.py \ < "$ORIG_COMMIT_MSG" > "$NEW_COMMIT_MSG" cmp -s "$ORIG_COMMIT_MSG" "$NEW_COMMIT_MSG" DIFF_MSG_RESULT=$? } # Temporary files ORIG_DIFF=orig.diff.$$ MODIFIED_DIFF=modified.diff.$$ FINAL_DIFF=final.diff.$$ ORIG_COMMIT_MSG=orig.commit-msg.$$ NEW_COMMIT_MSG=new.commit-msg.$$ CLEAN_FILES="${ORIG_DIFF} ${MODIFIED_DIFF} ${FINAL_DIFF}" CLEAN_FILES="${CLEAN_FILES} ${ORIG_COMMIT_MSG} ${NEW_COMMIT_MSG}" # Preconditions [ $# -lt 2 ] || usage # Check that astyle supports pad-header and align-pointer=name if ! astyle --pad-header --align-pointer=name < /dev/null; then log "Install astyle v1.24 or newer" exit 1 fi if ! git diff --quiet HEAD; then log "Working tree is dirty, commit your changes first" exit 1 fi # Need to be in the root cd "$(git rev-parse --show-toplevel)" # Collect the original diff git show > "${ORIG_DIFF}" # Apply the style guide on new and modified files and collect its diff for f in $(git diff HEAD^ --name-only -M90 --diff-filter=AM \ | grep '\.[ch]$'); do case "$f" in third_party/*) continue;; nestegg/*) continue;; esac vpx_style "$f" done git diff --no-color --no-ext-diff > "${MODIFIED_DIFF}" # Intersect the two diffs "${dirname_self}"/intersect-diffs.py \ "${ORIG_DIFF}" "${MODIFIED_DIFF}" > "${FINAL_DIFF}" INTERSECT_RESULT=$? git reset --hard >/dev/null # Fixup the commit message diff_msg # Handle options if [ -n "$1" ]; then case "$1" in -h|--help) usage;; -n|--dry-run) cat "${FINAL_DIFF}"; show_commit_msg_diff;; --commit) apply "${FINAL_DIFF}"; commit;; --amend) apply "${FINAL_DIFF}"; amend;; --msg-only) amend;; *) usage;; esac else apply "${FINAL_DIFF}" if ! git diff --quiet; then log "Formatting changes applied, verify and commit." log "See also: http://www.webmproject.org/code/contribute/conventions/" git diff --stat fi fi rm -f ${CLEAN_FILES}