Commit 1758b83a authored by Simon Spannagel's avatar Simon Spannagel
Browse files

Pre-commit: replace old hooks and update docs

parent 7233209e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ fork the [GitHub repository](https://github.com/allpix-squared/allpix-squared) a

Now you can start making changes and adding new functionality to the code.

1. Run `etc/git-hooks/install-hooks.sh` from the repository top folder to install the git-hook that automatically updates
1. Run `pre-commit install -f` from the repository top folder to install the git hooks that automatically update
   the format of the code to comply with the coding style.
2. Create a new branch from master with a description of the change using `git checkout -b my-new-branch-name`.
3. Read the relevant sections in the User's manual before starting to make changes.
+8 −18
Original line number Diff line number Diff line
@@ -5,26 +5,16 @@

# Check if the git hooks are installed and upt-to-date:
IF(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/.git)
    SET(HOOK_MISSING OFF)
    SET(HOOK_OUTDATED OFF)
    FOREACH(hook pre-commit-clang-format pre-push-tag-version)
        SET(HOOK_SRC "${CMAKE_SOURCE_DIR}/etc/git-hooks/${hook}-hook")
        SET(HOOK_DST "${CMAKE_SOURCE_DIR}/.git/hooks/${hook}")
        IF(NOT EXISTS ${HOOK_DST})
            SET(HOOK_MISSING ON)
    IF(NOT EXISTS "${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit")
        IF(EXISTS "${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit-clang-format")
            MESSAGE(WARNING "Git hooks are outdated - this project has moved to the pre-commit framework. Install via "
                    "pre-commit install -f")
        ELSE()
            EXECUTE_PROCESS(COMMAND "cmake" "-E" "compare_files" ${HOOK_SRC} ${HOOK_DST} RESULT_VARIABLE HOOKS_DIFFER)
            IF(${HOOKS_DIFFER})
                SET(HOOK_OUTDATED ON)
            ENDIF()
        ENDIF()
    ENDFOREACH()
    IF(${HOOK_MISSING})
        MESSAGE(WARNING "Git hooks are not installed - consider installing them via "
                        "${CMAKE_SOURCE_DIR}/etc/git-hooks/install-hooks.sh")
    ELSEIF(${HOOK_OUTDATED})
        MESSAGE(WARNING "Git hooks are outdated - consider updating them via "
                        "${CMAKE_SOURCE_DIR}/etc/git-hooks/install-hooks.sh")
            MESSAGE(WARNING "Git hooks are not installed - install via "
                    "pre-commit install")
        ENDIF()
    ELSE()
        MESSAGE(STATUS "Found pre-commit hooks installed.")
    ENDIF()
ENDIF()

+5 −6
Original line number Diff line number Diff line
@@ -53,16 +53,15 @@ code, just to adhere to the formatting. There are several options to integrate t
* A build target called `make format` is provided if the `clang-format` tool is installed. Running this command before
  committing code will ensure correct formatting.

* This can be further simplified by installing the *git hook* provided in the directory `/etc/git-hooks/`. A hook is a
  script called by `git` before a certain action. In this case, it is a pre-commit hook which automatically runs
  `clang-format` in the background and offers to update the formatting of the code to be committed. It can be installed
  by calling
* This can be further simplified by installing the *pre-commit git hooks*. A hook is a script called by `git` before a
  certain action. This reprository uses the [pre-commit framework](https://pre-commit.com/) to manage, update and run these
  hooks. The pre-commit framework can be activated by calling

  ```shell
  ./etc/git-hooks/install-hooks.sh
  pre-commit install
  ```

  once.
  once. If the previous manual git hooks were installed, a `-f` should be appended to force replacement of the old hooks.

The formatting rules are defined in the `.clang-format` file in the repository in machine-readable form (for `clang-format`,
that is) but can be summarized as follows:

etc/git-hooks/install-hooks.sh

deleted100755 → 0
+0 −48
Original line number Diff line number Diff line
#!/usr/bin/env bash

# SPDX-FileCopyrightText: 2017-2024 CERN and the Allpix Squared authors
# SPDX-License-Identifier: MIT

##---

REPLACE=("pre-commit-clang-format" "pre-push-tag-version")
WARNING=("pre-commit" "pre-push")

SRCDIR=$(git rev-parse --show-toplevel)/etc/git-hooks
TGTDIR=$(readlink -f $(git rev-parse --git-dir))/hooks

##---

InstallHook () {
    cp $SRCDIR/$1-hook $TGTDIR/$1
    chmod +x $TGTDIR/$1
}


InstallReplaceHooks () {
    for HOOK in "${REPLACE[@]}"
    do
	if [ -f $TGTDIR/$HOOK ]
	then
	    echo "WARNING: File $TGTDIR/$HOOK already exists! Overwriting previous version..."
	fi
	InstallHook $HOOK
    done
}

InstallWarningHooks () {
    for HOOK in "${WARNING[@]}"
    do
	if [ -f $TGTDIR/$HOOK ]
	then
	    echo "ERROR: File $TGTDIR/$HOOK already exists! Will not overwrite, add script manually if needed."
	else
	    InstallHook $HOOK
	fi
    done
}

##---

InstallReplaceHooks
InstallWarningHooks
+0 −172
Original line number Diff line number Diff line
#!/usr/bin/env bash

# SPDX-FileCopyrightText: 2017-2024 CERN and the Allpix Squared authors
# SPDX-License-Identifier: MIT

# git pre-commit hook that runs an clang-format stylecheck.
# Features:
#  - abort commit when commit does not comply with the style guidelines
#  - create a patch of the proposed style changes and apply it automatically

##################################################################
# SETTINGS
# set file containing the clang-format version number:
VERSIONFILE="CMakeLists.txt"

# retrieve the version number from the VERSIONFILE
CLANG_FORMAT_VERSION=$(git cat-file blob master:"$VERSIONFILE" | grep -m 1 'CLANG_FORMAT_VERSION' | sed 's/^[^"]*"//;s/"[^"]*$//')

# set path to clang-format binary
for bin in "clang-format-$CLANG_FORMAT_VERSION" "clang-format";
do
    CLANG_FORMAT=$(command -v "$bin")
    [ -n "$CLANG_FORMAT" ] && break
done


# set file extensions to handle
FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .tpp .C"

# if true also displays the changes and inform if no changes have to be made
VERBOSE=0

##################################################################

# check whether the given file matches any of the set extensions
matches_extension() {
    local filename=$(basename "$1")
    local extension=".${filename##*.}"
    local ext

    for ext in $FILE_EXTS; do [ "$ext" = "$extension" ] && return 0; done

    return 1
}

# necessary check for initial commit
if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# create a random filename to store our generated patch
prefix="pre-commit-clang-format"
suffix="$(date +%s)"
patch="/tmp/$prefix-$suffix.patch"
patch_color="/tmp/$prefix-$suffix-color.patch"
staged_file="/tmp/$prefix-$suffix-file.txt"

# clean up any older clang-format patches
rm -f /tmp/$prefix*.patch

# stop if clang format not available
if [ ! -x "$CLANG_FORMAT" ] ; then
    SCRIPTPATH=$( cd $(dirname $0) ; pwd -P )
    printf "Error: clang-format executable not found, cannot check format!\n"
    printf "Set the correct path in ${SCRIPTPATH}/$(basename "$0").\n"
    exit 1
fi

CLANG_FORMAT_VERSION_FOUND=`"$CLANG_FORMAT" --version | sed -n "s/.* \([0-9]\+\)\.[0-9]\+\.[0-9]\+.*/\1/p"`
if [ "${CLANG_FORMAT_VERSION_FOUND}" -lt "${CLANG_FORMAT_VERSION}" ]; then
    # Be indulgent with OSX users, no 4.0 seems to be available...
    if [ "$OSTYPE" != "darwin"* ]; then
        printf "Error: clang-format version $CLANG_FORMAT_VERSION or newer is required, cannot check format!\n"
        exit 1
    else
        printf "Warning: clang-format version $CLANG_FORMAT_VERSION or newer is required, formatting might be incompatible!\n"
    fi
fi


# create one patch containing all changes to the files
git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
do
    # ignore file if we do check for file extensions and the file
    # does not match any of the extensions specified in $FILE_EXTS
    if ! matches_extension "$file"; then
        continue;
    fi

    # temporary save the staged file to work with this instead of the real file
    git show :$file > $staged_file

    # clang-format our sourcefile, create a patch and append it to our $patch
    cat "$staged_file" | "$CLANG_FORMAT" -assume-filename=$file -style=file | \
        git diff --no-index -- "$staged_file" - | \
        sed -e "s|--- a$staged_file|--- a/$file|" | \
        sed -e "s|+++ b/-|+++ b/$file|" >> "$patch"

    # also make a colored patch for displaying if needed
    if [ "$VERBOSE" -ne 0 ] ; then
        cat "$staged_file" | "$CLANG_FORMAT" -assume-filename=$file -style=file | \
            git diff --color --no-index -- "$staged_file" - | \
            sed -e "s|--- a$staged_file|--- a/$file|" | \
            sed -e "s|+++ b/-|+++ b/$file|" >> "$patch_color"
    fi
done

# if no patch has been generated all is ok, clean up the file stub and exit
if [ ! -s "$patch" ] ; then
    if [ "$VERBOSE" -ne 0 ] ; then
        printf "Changes in this commit comply with the clang-format rules.\n"
    fi
    rm -f "$patch"
    rm -f "$patch_color"
    rm -f "$staged_file"
    exit 0
fi

# check if it is possible to apply this patch both to the index as well as the working directory
git status > /dev/null 2>&1
git apply --index --check $patch > /dev/null 2>&1
if [ $? -ne 0 ] ; then
    printf "The clang-format rules are not satisfied and changes have to be made!\n";
    printf "An automatic patch is not possible because you have differences between your staged and unstaged files...\n";
    printf "Nothing committed! Add changes to the index or apply format changes manually and commit again.\n";
    rm -f "$patch";
    rm -f "$patch_color";
    rm -f "$staged_file"
    exit 1;
fi

# a patch has been created, let the user know about it
printf "The clang-format rules are not satisfied and changes have to be made!\n"
printf "A patch has been created to fix the format automatically before continuing...\n"
if [ "$VERBOSE" -ne 0 ] ; then
    printf "The following differences were found between the code to commit "
    printf "and the clang-format rules:\n"
    cat "$patch_color"
    printf "\n"
fi

# assign stdin back to keyboard
exec < /dev/tty

# ask what we want to do with the patch
while true; do
    read -p "Do you want to install the patch (yes - commit with patch, no - do not commit)? [y/n] " yn
        case $yn in
            [Yy] )
                git apply --index $patch;
                rm -f "$patch";
                rm -f "$patch_color";
                rm -f "$staged_file"
                exit 0
            ;;
            [Nn] )
                printf "Changes are not applied! You can apply these changes manually with:\n";
                printf "  git apply --index $patch\n";
                printf "  (needs to be called from the root directory of your repository)\n";
                printf "Nothing committed! Apply format changes and commit again.\n";
                rm -f "$patch_color";
                rm -f "$staged_file"
                exit 1
            ;;
            * )
                echo "Please answer y(es) or n(o)."
            ;;
    esac
done
Loading