Commit the missing files related to coding style
This commit is contained in:
parent
2eda5f8d5f
commit
90b39435b6
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
# Global Options Go Here
|
||||
IndentWidth: 2
|
||||
ColumnLimit: 100
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlignConsecutiveMacros: true
|
||||
ForEachMacros: []
|
||||
IndentWidth: 2
|
||||
...
|
|
@ -0,0 +1,342 @@
|
|||
#! /bin/bash
|
||||
#
|
||||
# Copyright 2018 Undo Ltd.
|
||||
#
|
||||
# https://github.com/barisione/clang-format-hooks
|
||||
|
||||
# Force variable declaration before access.
|
||||
set -u
|
||||
# Make any failure in piped commands be reflected in the exit code.
|
||||
set -o pipefail
|
||||
|
||||
readonly bash_source="${BASH_SOURCE[0]:-$0}"
|
||||
|
||||
##################
|
||||
# Misc functions #
|
||||
##################
|
||||
|
||||
function error_exit() {
|
||||
for str in "$@"; do
|
||||
echo -n "$str" >&2
|
||||
done
|
||||
echo >&2
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
########################
|
||||
# Command line parsing #
|
||||
########################
|
||||
|
||||
function show_help() {
|
||||
if [ -t 1 ] && hash tput 2> /dev/null; then
|
||||
local -r b=$(tput bold)
|
||||
local -r i=$(tput sitm)
|
||||
local -r n=$(tput sgr0)
|
||||
else
|
||||
local -r b=
|
||||
local -r i=
|
||||
local -r n=
|
||||
fi
|
||||
|
||||
cat << EOF
|
||||
${b}SYNOPSIS${n}
|
||||
|
||||
To reformat git diffs:
|
||||
|
||||
${i}$bash_source [OPTIONS] [FILES-OR-GIT-DIFF-OPTIONS]${n}
|
||||
|
||||
To reformat whole files, including unchanged parts:
|
||||
|
||||
${i}$bash_source [-f | --whole-file] FILES${n}
|
||||
|
||||
${b}DESCRIPTION${n}
|
||||
|
||||
Reformat C or C++ code to match a specified formatting style.
|
||||
|
||||
This command can either work on diffs, to reformat only changed parts of
|
||||
the code, or on whole files (if -f or --whole-file is used).
|
||||
|
||||
${b}FILES-OR-GIT-DIFF-OPTIONS${n}
|
||||
List of files to consider when applying clang-format to a diff. This is
|
||||
passed to "git diff" as is, so it can also include extra git options or
|
||||
revisions.
|
||||
For example, to apply clang-format on the changes made in the last few
|
||||
revisions you could use:
|
||||
${i}\$ $bash_source HEAD~3${n}
|
||||
|
||||
${b}FILES${n}
|
||||
List of files to completely reformat.
|
||||
|
||||
${b}-f, --whole-file${n}
|
||||
Reformat the specified files completely (including parts you didn't
|
||||
change).
|
||||
The fix is printed on stdout by default. Use -i if you want to modify
|
||||
the files on disk.
|
||||
|
||||
${b}--staged, --cached${n}
|
||||
Reformat only code which is staged for commit.
|
||||
The fix is printed on stdout by default. Use -i if you want to modify
|
||||
the files on disk.
|
||||
|
||||
${b}-i${n}
|
||||
Reformat the code and apply the changes to the files on disk (instead
|
||||
of just printing the fix on stdout).
|
||||
|
||||
${b}--apply-to-staged${n}
|
||||
This is like specifying both --staged and -i, but the formatting
|
||||
changes are also staged for commit (so you can just use "git commit"
|
||||
to commit what you planned to, but formatted correctly).
|
||||
|
||||
${b}--style STYLE${n}
|
||||
The style to use for reformatting code.
|
||||
If no style is specified, then it's assumed there's a .clang-format
|
||||
file in the current directory or one of its parents.
|
||||
|
||||
${b}--help, -h, -?${n}
|
||||
Show this help.
|
||||
EOF
|
||||
}
|
||||
|
||||
# getopts doesn't support long options.
|
||||
# getopt mangles stuff.
|
||||
# So we parse manually...
|
||||
declare positionals=()
|
||||
declare has_positionals=false
|
||||
declare whole_file=false
|
||||
declare apply_to_staged=false
|
||||
declare staged=false
|
||||
declare in_place=false
|
||||
declare style=file
|
||||
declare ignored=()
|
||||
while [ $# -gt 0 ]; do
|
||||
declare arg="$1"
|
||||
shift # Past option.
|
||||
case "$arg" in
|
||||
-h | -\? | --help )
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-f | --whole-file )
|
||||
whole_file=true
|
||||
;;
|
||||
--apply-to-staged )
|
||||
apply_to_staged=true
|
||||
;;
|
||||
--cached | --staged )
|
||||
staged=true
|
||||
;;
|
||||
-i )
|
||||
in_place=true
|
||||
;;
|
||||
--style=* )
|
||||
style="${arg//--style=/}"
|
||||
;;
|
||||
--style )
|
||||
[ $# -gt 0 ] || \
|
||||
error_exit "No argument for --style option."
|
||||
style="$1"
|
||||
shift
|
||||
;;
|
||||
--internal-opt-ignore-regex=* )
|
||||
ignored+=("${arg//--internal-opt-ignore-regex=/}")
|
||||
;;
|
||||
--internal-opt-ignore-regex )
|
||||
ignored+=("${arg//--internal-opt-ignore-regex=/}")
|
||||
[ $# -gt 0 ] || \
|
||||
error_exit "No argument for --internal-opt-ignore-regex option."
|
||||
ignored+=("$1")
|
||||
shift
|
||||
;;
|
||||
-- )
|
||||
# Stop processing further arguments.
|
||||
if [ $# -gt 0 ]; then
|
||||
positionals+=("$@")
|
||||
has_positionals=true
|
||||
fi
|
||||
break
|
||||
;;
|
||||
-* )
|
||||
error_exit "Unknown argument: $arg"
|
||||
;;
|
||||
*)
|
||||
positionals+=("$arg")
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Restore positional arguments, access them from "$@".
|
||||
if [ ${#positionals[@]} -gt 0 ]; then
|
||||
set -- "${positionals[@]}"
|
||||
has_positionals=true
|
||||
fi
|
||||
|
||||
[ -n "$style" ] || \
|
||||
error_exit "If you use --style you need to specify a valid style."
|
||||
|
||||
#######################################
|
||||
# Detection of clang-format & friends #
|
||||
#######################################
|
||||
|
||||
# clang-format.
|
||||
declare format="${CLANG_FORMAT:-}"
|
||||
if [ -z "$format" ]; then
|
||||
format=$(type -p clang-format)
|
||||
fi
|
||||
|
||||
if [ -z "$format" ]; then
|
||||
error_exit \
|
||||
$'You need to install clang-format.\n' \
|
||||
$'\n' \
|
||||
$'On Ubuntu/Debian this is available in the clang-format package or, in\n' \
|
||||
$'older distro versions, clang-format-VERSION.\n' \
|
||||
$'On Fedora it\'s available in the clang package.\n' \
|
||||
$'You can also specify your own path for clang-format by setting the\n' \
|
||||
$'$CLANG_FORMAT environment variable.'
|
||||
fi
|
||||
|
||||
# clang-format-diff.
|
||||
if [ "$whole_file" = false ]; then
|
||||
invalid="/dev/null/invalid/path"
|
||||
if [ "${OSTYPE:-}" = "linux-gnu" ]; then
|
||||
readonly sort_version=-V
|
||||
else
|
||||
# On macOS, sort doesn't have -V.
|
||||
readonly sort_version=-n
|
||||
fi
|
||||
declare paths_to_try=()
|
||||
# .deb packages directly from upstream.
|
||||
# We try these first as they are probably newer than the system ones.
|
||||
while read -r f; do
|
||||
paths_to_try+=("$f")
|
||||
done < <(compgen -G "/usr/share/clang/clang-format-*/clang-format-diff.py" | sort "$sort_version" -r)
|
||||
# LLVM official releases (just untarred in /usr/local).
|
||||
while read -r f; do
|
||||
paths_to_try+=("$f")
|
||||
done < <(compgen -G "/usr/local/clang+llvm*/share/clang/clang-format-diff.py" | sort "$sort_version" -r)
|
||||
# Maybe it's in the $PATH already? This is true for Ubuntu and Debian.
|
||||
paths_to_try+=( \
|
||||
"$(type -p clang-format-diff 2> /dev/null || echo "$invalid")" \
|
||||
"$(type -p clang-format-diff.py 2> /dev/null || echo "$invalid")" \
|
||||
)
|
||||
# Fedora.
|
||||
paths_to_try+=( \
|
||||
/usr/share/clang/clang-format-diff.py \
|
||||
)
|
||||
# Gentoo.
|
||||
while read -r f; do
|
||||
paths_to_try+=("$f")
|
||||
done < <(compgen -G "/usr/lib/llvm/*/share/clang/clang-format-diff.py" | sort -n -r)
|
||||
# Homebrew.
|
||||
while read -r f; do
|
||||
paths_to_try+=("$f")
|
||||
done < <(compgen -G "/usr/local/Cellar/clang-format/*/share/clang/clang-format-diff.py" | sort -n -r)
|
||||
|
||||
declare format_diff=
|
||||
|
||||
# Did the user specify a path?
|
||||
if [ -n "${CLANG_FORMAT_DIFF:-}" ]; then
|
||||
format_diff="$CLANG_FORMAT_DIFF"
|
||||
else
|
||||
for path in "${paths_to_try[@]}"; do
|
||||
if [ -e "$path" ]; then
|
||||
# Found!
|
||||
format_diff="$path"
|
||||
if [ ! -x "$format_diff" ]; then
|
||||
format_diff="python $format_diff"
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ -z "$format_diff" ]; then
|
||||
error_exit \
|
||||
$'Cannot find clang-format-diff which should be shipped as part of the same\n' \
|
||||
$'package where clang-format is.\n' \
|
||||
$'\n' \
|
||||
$'Please find out where clang-format-diff is in your distro and report an issue\n' \
|
||||
$'at https://github.com/barisione/clang-format-hooks/issues with details about\n' \
|
||||
$'your operating system and setup.\n' \
|
||||
$'\n' \
|
||||
$'You can also specify your own path for clang-format-diff by setting the\n' \
|
||||
$'$CLANG_FORMAT_DIFF environment variable, for instance:\n' \
|
||||
$'\n' \
|
||||
$' CLANG_FORMAT_DIFF="python /.../clang-format-diff.py" \\\n' \
|
||||
$' ' "$bash_source"
|
||||
fi
|
||||
|
||||
readonly format_diff
|
||||
fi
|
||||
|
||||
|
||||
############################
|
||||
# Actually run the command #
|
||||
############################
|
||||
|
||||
if [ "$whole_file" = true ]; then
|
||||
|
||||
[ "$has_positionals" = true ] || \
|
||||
error_exit "No files to reformat specified."
|
||||
[ "$staged" = false ] || \
|
||||
error_exit "--staged/--cached only make sense when applying to a diff."
|
||||
|
||||
read -r -a format_args <<< "$format"
|
||||
format_args+=("-style=file")
|
||||
[ "$in_place" = true ] && format_args+=("-i")
|
||||
|
||||
"${format_args[@]}" "$@"
|
||||
|
||||
else # Diff-only.
|
||||
|
||||
if [ "$apply_to_staged" = true ]; then
|
||||
[ "$staged" = false ] || \
|
||||
error_exit "You don't need --staged/--cached with --apply-to-staged."
|
||||
[ "$in_place" = false ] || \
|
||||
error_exit "You don't need -i with --apply-to-staged."
|
||||
staged=true
|
||||
readonly patch_dest=$(mktemp)
|
||||
trap '{ rm -f "$patch_dest"; }' EXIT
|
||||
else
|
||||
readonly patch_dest=/dev/stdout
|
||||
fi
|
||||
|
||||
declare git_args=(git diff -U0 --no-color)
|
||||
[ "$staged" = true ] && git_args+=("--staged")
|
||||
|
||||
# $format_diff may contain a command ("python") and the script to excute, so we
|
||||
# need to split it.
|
||||
read -r -a format_diff_args <<< "$format_diff"
|
||||
[ "$in_place" = true ] && format_diff_args+=("-i")
|
||||
|
||||
# Build the regex for paths to consider or ignore.
|
||||
# We use negative lookahead assertions which preceed the list of allowed patterns
|
||||
# (that is, the extensions we want).
|
||||
exclusions_regex=
|
||||
if [ "${#ignored[@]}" -gt 0 ]; then
|
||||
for pattern in "${ignored[@]}"; do
|
||||
exclusions_regex="$exclusions_regex(?!$pattern)"
|
||||
done
|
||||
fi
|
||||
|
||||
"${git_args[@]}" "$@" \
|
||||
| "${format_diff_args[@]}" \
|
||||
-p1 \
|
||||
-style="$style" \
|
||||
-iregex="$exclusions_regex"'.*\.(c|cpp|cxx|cc|h|hpp|m|mm|js|java)' \
|
||||
> "$patch_dest" \
|
||||
|| exit 1
|
||||
|
||||
if [ "$apply_to_staged" = true ]; then
|
||||
if [ ! -s "$patch_dest" ]; then
|
||||
echo "No formatting changes to apply."
|
||||
exit 0
|
||||
fi
|
||||
patch -p0 < "$patch_dest" || \
|
||||
error_exit "Cannot apply fix to local files."
|
||||
git apply -p0 --cached < "$patch_dest" || \
|
||||
error_exit "Cannot apply fix to git staged changes."
|
||||
fi
|
||||
|
||||
fi
|
|
@ -0,0 +1,411 @@
|
|||
#! /bin/bash
|
||||
#
|
||||
# Copyright 2018 Undo Ltd.
|
||||
#
|
||||
# https://github.com/barisione/clang-format-hooks
|
||||
|
||||
# Force variable declaration before access.
|
||||
set -u
|
||||
# Make any failure in piped commands be reflected in the exit code.
|
||||
set -o pipefail
|
||||
|
||||
readonly bash_source="${BASH_SOURCE[0]:-$0}"
|
||||
|
||||
if [ -t 1 ] && hash tput 2> /dev/null; then
|
||||
readonly b=$(tput bold)
|
||||
readonly i=$(tput sitm)
|
||||
readonly n=$(tput sgr0)
|
||||
else
|
||||
readonly b=
|
||||
readonly i=
|
||||
readonly n=
|
||||
fi
|
||||
|
||||
function error_exit() {
|
||||
for str in "$@"; do
|
||||
echo -n "$b$str$n" >&2
|
||||
done
|
||||
echo >&2
|
||||
|
||||
exit 1
|
||||
}
|
||||
|
||||
# realpath is not available everywhere.
|
||||
function realpath() {
|
||||
if [ "${OSTYPE:-}" = "linux-gnu" ]; then
|
||||
readlink -m "$@"
|
||||
else
|
||||
# Python should always be available on macOS.
|
||||
# We use sys.stdout.write instead of print so it's compatible with both Python 2 and 3.
|
||||
python -c "import sys; import os.path; sys.stdout.write(os.path.realpath('''$1''') + '\\n')"
|
||||
fi
|
||||
}
|
||||
|
||||
# realpath --relative-to is only available on recent Linux distros.
|
||||
# This function behaves identical to Python's os.path.relpath() and doesn't need files to exist.
|
||||
function rel_realpath() {
|
||||
local -r path=$(realpath "$1")
|
||||
local -r rel_to=$(realpath "${2:-$PWD}")
|
||||
|
||||
# Split the paths into components.
|
||||
IFS='/' read -r -a path_parts <<< "$path"
|
||||
IFS='/' read -r -a rel_to_parts <<< "$rel_to"
|
||||
|
||||
# Search for the first different component.
|
||||
for ((idx=1; idx<${#path_parts[@]}; idx++)); do
|
||||
if [ "${path_parts[idx]}" != "${rel_to_parts[idx]:-}" ]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
result=()
|
||||
# Add the required ".." to the $result array.
|
||||
local -r first_different_idx="$idx"
|
||||
for ((idx=first_different_idx; idx<${#rel_to_parts[@]}; idx++)); do
|
||||
result+=("..")
|
||||
done
|
||||
# Add the required components from $path.
|
||||
for ((idx=first_different_idx; idx<${#path_parts[@]}; idx++)); do
|
||||
result+=("${path_parts[idx]}")
|
||||
done
|
||||
|
||||
if [ "${#result[@]}" -gt 0 ]; then
|
||||
# Join the array with a "/" as separator.
|
||||
echo "$(export IFS='/'; echo "${result[*]}")"
|
||||
else
|
||||
echo .
|
||||
fi
|
||||
}
|
||||
|
||||
# Find the top-level git directory (taking into account we could be in a submodule).
|
||||
declare git_test_dir=.
|
||||
declare top_dir
|
||||
|
||||
while true; do
|
||||
top_dir=$(git -C "$git_test_dir" rev-parse --show-toplevel) || \
|
||||
error_exit "You need to be in the git repository to run this script."
|
||||
|
||||
# Try to handle git worktree.
|
||||
# The best way to deal both with git submodules and worktrees would be to
|
||||
# use --show-superproject-working-tree, but it's not supported in git 2.7.4
|
||||
# which is shipped in Ubuntu 16.04.
|
||||
declare git_common_dir
|
||||
if git_common_dir=$(git -C "$git_test_dir" rev-parse --git-common-dir 2>/dev/null); then
|
||||
# The common dir could be relative, so we make it absolute.
|
||||
git_common_dir=$(cd "$git_test_dir" && realpath "$git_common_dir")
|
||||
declare maybe_top_dir
|
||||
maybe_top_dir=$(realpath "$git_common_dir/..")
|
||||
if [ -e "$maybe_top_dir/.git" ]; then
|
||||
# We are not in a submodules, otherwise common dir would have been
|
||||
# something like PROJ/.git/modules/SUBMODULE and there would not be
|
||||
# a .git directory in PROJ/.git/modules/.
|
||||
top_dir="$maybe_top_dir"
|
||||
fi
|
||||
fi
|
||||
|
||||
[ -e "$top_dir/.git" ] || \
|
||||
error_exit "No .git directory in $top_dir."
|
||||
|
||||
if [ -d "$top_dir/.git" ]; then
|
||||
# We are done! top_dir is the root git directory.
|
||||
break
|
||||
elif [ -f "$top_dir/.git" ]; then
|
||||
# We are in a submodule.
|
||||
git_test_dir="$git_test_dir/.."
|
||||
fi
|
||||
done
|
||||
|
||||
readonly top_dir
|
||||
|
||||
hook_path="$top_dir/.git/hooks/pre-commit"
|
||||
readonly hook_path
|
||||
|
||||
me=$(realpath "$bash_source") || exit 1
|
||||
readonly me
|
||||
|
||||
me_relative_to_hook=$(rel_realpath "$me" "$(dirname "$hook_path")") || exit 1
|
||||
readonly me_relative_to_hook
|
||||
|
||||
my_dir=$(dirname "$me") || exit 1
|
||||
readonly my_dir
|
||||
|
||||
apply_format="$my_dir/apply-format"
|
||||
readonly apply_format
|
||||
|
||||
apply_format_relative_to_top_dir=$(rel_realpath "$apply_format" "$top_dir") || exit 1
|
||||
readonly apply_format_relative_to_top_dir
|
||||
|
||||
function is_installed() {
|
||||
if [ ! -e "$hook_path" ]; then
|
||||
echo nothing
|
||||
else
|
||||
existing_hook_target=$(realpath "$hook_path") || exit 1
|
||||
readonly existing_hook_target
|
||||
|
||||
if [ "$existing_hook_target" = "$me" ]; then
|
||||
# Already installed.
|
||||
echo installed
|
||||
else
|
||||
# There's a hook, but it's not us.
|
||||
echo different
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function install() {
|
||||
if ln -s "$me_relative_to_hook" "$hook_path" 2> /dev/null; then
|
||||
echo "Pre-commit hook installed."
|
||||
else
|
||||
local -r res=$(is_installed)
|
||||
if [ "$res" = installed ]; then
|
||||
error_exit "The hook is already installed."
|
||||
elif [ "$res" = different ]; then
|
||||
error_exit "There's already an existing pre-commit hook, but for something else."
|
||||
elif [ "$res" = nothing ]; then
|
||||
error_exit "There's no pre-commit hook, but we couldn't create a symlink."
|
||||
else
|
||||
error_exit "Unexpected failure."
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function uninstall() {
|
||||
local -r res=$(is_installed)
|
||||
if [ "$res" = installed ]; then
|
||||
rm "$hook_path" || \
|
||||
error_exit "Couldn't remove the pre-commit hook."
|
||||
elif [ "$res" = different ]; then
|
||||
error_exit "There's a pre-commit hook installed, but for something else. Not removing."
|
||||
elif [ "$res" = nothing ]; then
|
||||
error_exit "There's no pre-commit hook, nothing to uninstall."
|
||||
else
|
||||
error_exit "Unexpected failure detecting the pre-commit hook status."
|
||||
fi
|
||||
}
|
||||
|
||||
function show_help() {
|
||||
cat << EOF
|
||||
${b}SYNOPSIS${n}
|
||||
|
||||
$bash_source [install|uninstall]
|
||||
|
||||
${b}DESCRIPTION${n}
|
||||
|
||||
Git hook to verify and fix formatting before committing.
|
||||
|
||||
The script is invoked automatically when you commit, so you need to call it
|
||||
directly only to set up the hook or remove it.
|
||||
|
||||
To setup the hook run this script passing "install" on the command line.
|
||||
To remove the hook run passing "uninstall".
|
||||
|
||||
${b}CONFIGURATION${n}
|
||||
|
||||
You can configure the hook using the "git config" command.
|
||||
|
||||
${b}hooks.clangFormatDiffInteractive${n} (default: true)
|
||||
By default, the hook requires user input. If you don't run git from a
|
||||
terminal, you can disable the interactive prompt with:
|
||||
${i}\$ git config hooks.clangFormatDiffInteractive false${n}
|
||||
|
||||
${b}hooks.clangFormatDiffStyle${n} (default: file)
|
||||
Unless a different style is specified, the hook expects a file named
|
||||
.clang-format to exist in the repository. This file should contain the
|
||||
configuration for clang-format.
|
||||
You can specify a different style (in this example, the WebKit one)
|
||||
with:
|
||||
${i}\$ git config hooks.clangFormatDiffStyle WebKit${n}
|
||||
EOF
|
||||
}
|
||||
|
||||
if [ $# = 1 ]; then
|
||||
case "$1" in
|
||||
-h | -\? | --help )
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
install )
|
||||
install
|
||||
exit 0
|
||||
;;
|
||||
uninstall )
|
||||
uninstall
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
[ $# = 0 ] || error_exit "Invalid arguments: $*"
|
||||
|
||||
|
||||
# This is a real run of the hook, not a install/uninstall run.
|
||||
|
||||
if [ -z "${GIT_DIR:-}" ] && [ -z "${GIT_INDEX_FILE:-}" ]; then
|
||||
error_exit \
|
||||
$'It looks like you invoked this script directly, but it\'s supposed to be used\n' \
|
||||
$'as a pre-commit git hook.\n' \
|
||||
$'\n' \
|
||||
$'To install the hook try:\n' \
|
||||
$' ' "$bash_source" $' install\n' \
|
||||
$'\n' \
|
||||
$'For more details on this script try:\n' \
|
||||
$' ' "$bash_source" $' --help\n'
|
||||
fi
|
||||
|
||||
[ -x "$apply_format" ] || \
|
||||
error_exit \
|
||||
$'Cannot find the apply-format script.\n' \
|
||||
$'I expected it here:\n' \
|
||||
$' ' "$apply_format"
|
||||
|
||||
readonly style=$(cd "$top_dir" && git config hooks.clangFormatDiffStyle || echo file)
|
||||
|
||||
apply_format_opts=(
|
||||
"--style=$style"
|
||||
--cached
|
||||
)
|
||||
|
||||
readonly exclusions_file="$top_dir/.clang-format-hook-exclude"
|
||||
if [ -e "$exclusions_file" ]; then
|
||||
while IFS= read -r line; do
|
||||
if [[ "$line" && "$line" != "#"* ]]; then
|
||||
apply_format_opts+=("--internal-opt-ignore-regex=$line")
|
||||
fi
|
||||
done < "$exclusions_file"
|
||||
fi
|
||||
|
||||
readonly patch=$(mktemp)
|
||||
trap '{ rm -f "$patch"; }' EXIT
|
||||
"$apply_format" --style="$style" --cached "${apply_format_opts[@]}" > "$patch" || \
|
||||
error_exit $'\nThe apply-format script failed.'
|
||||
|
||||
if [ "$(wc -l < "$patch")" -eq 0 ]; then
|
||||
echo "The staged content is formatted correctly."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
# The code is not formatted correctly.
|
||||
|
||||
interactive=$(cd "$top_dir" && git config --bool hooks.clangFormatDiffInteractive)
|
||||
if [ "$interactive" != false ]; then
|
||||
# Interactive is the default, so anything that is not false is converted to
|
||||
# true, including possibly invalid values.
|
||||
interactive=true
|
||||
fi
|
||||
readonly interactive
|
||||
|
||||
if [ "$interactive" = false ]; then
|
||||
echo "${b}The staged content is not formatted correctly.${n}"
|
||||
echo "You can fix the formatting with:"
|
||||
echo " ${i}\$ ./$apply_format_relative_to_top_dir --apply-to-staged${n}"
|
||||
echo
|
||||
echo "You can also make this script interactive (if you use git from a terminal) with:"
|
||||
echo " ${i}\$ git config hooks.clangFormatDiffInteractive true${n}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if hash colordiff 2> /dev/null; then
|
||||
colordiff < "$patch"
|
||||
else
|
||||
echo "${b}(Install colordiff to see this diff in color!)${n}"
|
||||
echo
|
||||
cat "$patch"
|
||||
fi
|
||||
|
||||
# We don't want to suggest applying clang-format after merge resolution
|
||||
if git rev-parse MERGE_HEAD >/dev/null 2>&1; then
|
||||
readonly this_is_a_merge=true
|
||||
else
|
||||
readonly this_is_a_merge=false
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "${b}The staged content is not formatted correctly.${n}"
|
||||
echo "The fix shown above can be applied automatically to the commit."
|
||||
echo
|
||||
|
||||
if $this_is_a_merge; then
|
||||
echo "${b}You appear to be committing the result of a merge. It is not${n}"
|
||||
echo "${b}recommended to apply the fix if it will reformat any code you${n}"
|
||||
echo "${b}did not modify in your branch.${n}"
|
||||
echo
|
||||
readonly recommend_apply=" (not recommended for merge!)"
|
||||
readonly recommend_force=""
|
||||
readonly bold_apply=""
|
||||
readonly bold_force="${b}"
|
||||
else
|
||||
readonly recommend_apply=""
|
||||
readonly recommend_force=" (not recommended!)"
|
||||
readonly bold_apply="${b}"
|
||||
readonly bold_force=""
|
||||
fi
|
||||
|
||||
echo "You can:"
|
||||
echo " ${bold_apply}[a]: Apply the fix${recommend_apply}${n}"
|
||||
echo " ${bold_force}[f]: Force and commit anyway${recommend_force}${n}"
|
||||
echo " [c]: Cancel the commit"
|
||||
echo " [?]: Show help"
|
||||
echo
|
||||
|
||||
readonly tty=${PRE_COMMIT_HOOK_TTY:-/dev/tty}
|
||||
|
||||
while true; do
|
||||
echo -n "What would you like to do? [a/f/c/?] "
|
||||
read -r answer < "$tty"
|
||||
case "$answer" in
|
||||
|
||||
[aA] )
|
||||
patch -p0 < "$patch" || \
|
||||
error_exit \
|
||||
$'\n' \
|
||||
$'Cannot apply fix to local files.\n' \
|
||||
$'Have you modified the file locally after starting the commit?'
|
||||
git apply -p0 --cached < "$patch" || \
|
||||
error_exit \
|
||||
$'\n' \
|
||||
$'Cannot apply fix to git staged changes.\n' \
|
||||
$'This may happen if you have some overlapping unstaged changes. To solve\n' \
|
||||
$'you need to stage or reset changes manually.'
|
||||
|
||||
if $this_is_a_merge; then
|
||||
echo
|
||||
echo "Applied the fix to reformat the merge commit."
|
||||
echo "You can always abort by quitting your editor with no commit message."
|
||||
echo
|
||||
echo -n "Press return to continue."
|
||||
read -r < "$tty"
|
||||
fi
|
||||
;;
|
||||
|
||||
[fF] )
|
||||
echo
|
||||
if ! $this_is_a_merge; then
|
||||
echo "Will commit anyway!"
|
||||
echo "You can always abort by quitting your editor with no commit message."
|
||||
echo
|
||||
echo -n "Press return to continue."
|
||||
read -r < "$tty"
|
||||
fi
|
||||
exit 0
|
||||
;;
|
||||
|
||||
[cC] )
|
||||
error_exit "Commit aborted as requested."
|
||||
;;
|
||||
|
||||
\? )
|
||||
echo
|
||||
show_help
|
||||
echo
|
||||
continue
|
||||
;;
|
||||
|
||||
* )
|
||||
echo 'Invalid answer. Type "a", "f" or "c".'
|
||||
echo
|
||||
continue
|
||||
|
||||
esac
|
||||
break
|
||||
done
|
Loading…
Reference in New Issue