#!/bin/bash # Auto format changed java files using google-java-format. # To install, copy this file into $repo/.git/hooks and remove the .sh extension. # Download the google-java-format JAR from # https://github.com/google/google-java-format # A more mature implementation of this would be a plugin for Yelp's pre-commit library: # http://pre-commit.com/ echo "Running auto-formatter for any changed Java files" echo "(formatting changes will be automatically added to your commit)" # Grab root directory to help with creating an absolute path for changed files. root_dir="$(git rev-parse --show-toplevel)" [ -d "${root_dir}" ] || exit 1 # TODO add your path here. jar_base_dir="git_hooks/" # To avoid any unexpected behavior, we need to "stash" any unstaged changes. # We could use "git stash" but the situation gets complicated because we # need to make additional changes with the formatter. # Here's how we could do this if we didn't need to make additional changes: # http://stackoverflow.com/a/20480591 # But since we do, we follow the same general pattern as the "pre-commit" lib: # https://github.com/pre-commit/pre-commit/blob/master/pre_commit/staged_files_only.py#L15 # Basically we just diff the unstaged changes, store the patch, and apply it later. # In the future, we should consider migrating to using that library. staged_changes_diff=$(mktemp -t format_patch) git diff --ignore-submodules --binary --exit-code --no-color > $staged_changes_diff if [ $? -eq 1 ]; then echo "Found unstaged changes, storing in ${staged_changes_diff}" echo "Clearing unstaged changes for formatting, will restore after formatting." git checkout -- ${root_dir} stored_staged_changes=true else stored_staged_changes=false fi formatter_jar="${root_dir}/${jar_base_dir}/google-java-format-1.6-SNAPSHOT-all-deps.jar" formatter_cmd="java -jar ${formatter_jar}" # Format file in-place and use 4-space style (AOSP). formatter_args="--replace --aosp" # filter=ACMR shows only added, changed, modified, or renamed files. # Get only java files and prepend the root directory to make the paths absolute. # Only format files in src/main/java and src/test/java (src/test/test-project contains java files that need to be left alone) changed_java_files=($(git diff --cached --name-only --diff-filter=ACMR src/main/java src/test/java \ | grep ".*java$" \ | sed "s:^:${root_dir}/:")) # If we have changed java files, format them! if [ ${#changed_java_files[@]} -gt 0 ]; then # Do the formatting, stage the changes, and print out which files were changed. eval ${formatter_cmd} ${formatter_args} "${changed_java_files[@]}" git add "${changed_java_files[@]}" echo "${changed_java_files[@]}" | xargs basename | sed "s/^/ Formatting: /" fi echo "Finished formatting." if $stored_staged_changes ; then echo "Restoring unstaged changes" git apply "${staged_changes_diff}" if [ $? -eq 1 ]; then echo "Shoot! We failed to re-apply your unstaged changes." echo "The patch for these changes is preserved at ${staged_changes_diff}" fi fi