diff --git a/cmake/modules/CbmTargets.cmake b/cmake/modules/CbmTargets.cmake
index ab37a0728e2674256e49db1d51aa91e91c9b4bca..3aac007772a5a7555ec680368e2a939fd5904d10 100644
--- a/cmake/modules/CbmTargets.cmake
+++ b/cmake/modules/CbmTargets.cmake
@@ -26,7 +26,7 @@ macro(define_additional_targets)
 
     # Create a list C, C++ and header files which have been changed in the
     # current commit
-    execute_process(COMMAND ${CMAKE_SOURCE_DIR}/cmake/scripts/find_files.sh
+    execute_process(COMMAND ${CMAKE_SOURCE_DIR}/cmake/scripts/find_files.sh ${BASE_COMMIT}
                     WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
                     OUTPUT_VARIABLE FileList
                     OUTPUT_STRIP_TRAILING_WHITESPACE
@@ -58,6 +58,50 @@ macro(define_additional_targets)
 
   endif()
 
+  find_package2(PRIVATE ClangTidy)
+  if(ClangTidy_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/.git)
+    if (FAIRROOT_TIDY_BASE)
+      set(BASE_COMMIT ${FAIRROOT_TIDY_BASE})
+    else()
+      set(BASE_COMMIT upstream/master)
+    endif()
+
+    # Create a list C, C++ and header files which have been changed in the
+    # current commit
+    execute_process(COMMAND ${CMAKE_SOURCE_DIR}/cmake/scripts/find_files.sh ${BASE_COMMIT}
+                    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+                    OUTPUT_VARIABLE FileList
+                    OUTPUT_STRIP_TRAILING_WHITESPACE
+                   )
+    string(REGEX REPLACE " " ";" FileList "${FileList}")
+
+    # Loop over the files and create the code whch is executed when running
+    # "make FormatCheck".
+    # The produced code will run clang-format on one of the files. If
+    # clang-format finds code which are not satisfying our code rules a
+    # detailed error message is created. This error message can be checked on
+    # our CDash web page.
+    foreach(file ${FileList})
+
+      set(file1 ${CMAKE_BINARY_DIR}/${file}.ct_out)
+
+      list(APPEND myfilelist1 ${file1})
+      add_custom_command(OUTPUT ${file1}
+                         COMMAND ${CMAKE_SOURCE_DIR}/cmake/scripts/check-tidy.sh ${file} ${file1} ${CMAKE_BINARY_DIR}
+                         WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+                        )
+    endforeach()
+
+    # Create the target FormatCheck which only depends on the files creted in
+    # previous step. When running "make FormatCheck" clang-format is executed
+    # for all C, C++ and header files which have changed in the commit.
+    add_custom_target(TidyCheck
+                      DEPENDS ${myfilelist1}
+                      )
+
+  endif()
+
+
 # TODO: check if still needed
 #  if(RULE_CHECKER_FOUND)
 #    ADD_CUSTOM_TARGET(RULES
diff --git a/cmake/modules/FindClangTidy.cmake b/cmake/modules/FindClangTidy.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..885261d86467fb0a63f0b77ca2cfdf58d237d719
--- /dev/null
+++ b/cmake/modules/FindClangTidy.cmake
@@ -0,0 +1,85 @@
+# Defines the following variables:
+#
+#   ClangTidy_FOUND - Found clang-tidy
+#   CLANG_TIDY_BIN - clang-tidy executable
+
+find_program(CLANG_TIDY_BIN
+  NAMES clang-tidy
+        clang-tidy-16
+        clang-tidy-15
+        clang-tidy-14
+        clang-tidy-13
+        clang-tidy-12
+        clang-tidy-11
+)
+
+Message("CLANG_TIDY_BIN: ${CLANG_TIDY_BIN}")
+
+#list(APPEND required_tidy_checks
+#  modernize-deprecated-headers
+#  modernize-use-nullptr
+#)
+
+# Extract the list of checks from the clang-tidy configuration
+# The line looks like: "Checks:          '-*,modernize-deprecated-headers'"
+# Remove everything beside the part between the quotes and convert the
+# string to a list
+# The checks must not contain any wildcards
+# IDEA: Check if one can extract the same information from clang-tidy --dump-version
+
+file(STRINGS ${CMAKE_SOURCE_DIR}/.clang-tidy required_tidy_checks_from_file REGEX "Checks.*")
+
+string(LENGTH ${required_tidy_checks_from_file} _length)
+string(FIND ${required_tidy_checks_from_file} "'" _pos_first_quote)
+math(EXPR _pos_first_quote ${_pos_first_quote}+1)
+string(SUBSTRING ${required_tidy_checks_from_file} ${_pos_first_quote} ${_length} required_tidy_checks_from_file)
+
+string(FIND ${required_tidy_checks_from_file} "'" _pos_last_quote)
+string(SUBSTRING ${required_tidy_checks_from_file} 0 ${_pos_last_quote} required_tidy_checks_from_file)
+
+string(REPLACE "," ";" required_tidy_checks ${required_tidy_checks_from_file})
+
+message(VERBOSE "required_tidy_checks: ${required_tidy_checks}")
+if (CLANG_TIDY_BIN)
+  # Loop over list of required checks
+  # Succeed if all required checks are supported by the clang version
+
+  execute_process(COMMAND  ${CLANG_TIDY_BIN} -checks=-*,modernize-* --list-checks
+                  OUTPUT_VARIABLE available_tidy_checks
+                 )
+  message(VERBOSE "Available clang-tidy checks: ${available_tidy_checks}")
+
+  set(CLANG_TIDY_CHECKS_SUPPORTED TRUE)
+  foreach(check IN LISTS required_tidy_checks)
+    # Skip all checks which contain wildcards
+    string(REGEX MATCH "\\*" _wildcard ${check})
+    if(_wildcard)
+      message("The checker doesn't support wildcards in the check")
+      string(REGEX MATCH "^-\\*" _wildcard ${check})
+      if(_wildcard)
+        message("The check ${check} is droped.")
+        continue()
+      else()
+        message("The check ${check} violates this. clang-tidy support is disabled.")
+        set(CLANG_TIDY_CHECKS_SUPPORTED FALSE)
+        break()
+      endif()
+    endif()
+    string(FIND "${available_tidy_checks}" "${check}" check_avail)
+    if(${check_avail} EQUAL -1)
+      message("clang-tidy doesn't support the needed Check ${check}. clang-tidy support is disabled.")
+      set(CLANG_TIDY_CHECKS_SUPPORTED FALSE)
+      break()
+    endif()
+    message("Used check: ${check}")
+  endforeach()
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(ClangTidy
+  REQUIRED_VARS CLANG_TIDY_BIN CLANG_TIDY_CHECKS_SUPPORTED
+)
+
+if(ClangTidy_FOUND)
+  message("The found clang tidy supports all requested checks.")
+endif()
diff --git a/cmake/scripts/check-tidy.sh b/cmake/scripts/check-tidy.sh
new file mode 100755
index 0000000000000000000000000000000000000000..30d3109ad156a0f45c65575755632636691a6a37
--- /dev/null
+++ b/cmake/scripts/check-tidy.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+# Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+# SPDX-License-Identifier: GPL-3.0-only
+# First commited by Florian Uhlig
+
+infile=$1
+outfile=$2
+builddir=$3
+
+CLANG_TIDY_BIN=${CLANG_TIDY_BIN:-clang-tidy}
+
+# special case when a file was deleted
+# don't run the test in such a case
+if [ ! -e $infile ]; then
+  exit 0
+fi
+
+extension=${infile##*.}
+
+# Only check source or header files
+if [ "$extension" == "h" -o "$extension" == "cxx" ]; then
+  echo "Checking file $infile"
+else
+  exit 0
+fi
+
+# Don't do anythink for LinkDef files
+if [[ $infile =~ "LinkDef" ]]; then
+  exit 0
+fi
+
+# Check if the file is a target in the compilation database or a header file
+# for which the corresponding source file is in the compilation database
+if [[ "$extension" == "h" ]]; then
+  checkfile=${infile%.*}.cxx
+else
+  checkfile=$infile
+fi
+
+file=$(grep '"file"' $builddir/compile_commands.json | grep "$checkfile" | cut -d: -f2 | cut -d\" -f2)
+
+if [[ "$file" != "" ]]; then
+  # create directory if it not yet exist
+  dir="$(dirname $outfile)"
+  mkdir -p $dir
+  OUTPUT=$($CLANG_TIDY_BIN -p $builddir $file 2> $outfile)
+else
+  OUTPUT=""
+fi
+
+if [ "$OUTPUT" == "" ]; then
+  exit 0
+else
+  echo "ERROR: clang-tidy check failed for file $infile. Suggested changes:" > $outfile
+  echo  >> $outfile
+  echo "$OUTPUT" >> $outfile
+  echo "ERROR: clang-tidy check failed for file $infile. Suggested changes:"
+  echo
+  echo "$OUTPUT"
+  exit 1
+fi
diff --git a/cmake/scripts/checktidy.cmake b/cmake/scripts/checktidy.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..d9e932863aedfe2cb0959ad8ae41623507ab7828
--- /dev/null
+++ b/cmake/scripts/checktidy.cmake
@@ -0,0 +1,34 @@
+cmake_host_system_information(RESULT fqdn QUERY FQDN)
+
+Set(CTEST_SOURCE_DIRECTORY $ENV{SOURCEDIR})
+Set(CTEST_BINARY_DIRECTORY $ENV{BUILDDIR})
+Set(CTEST_PROJECT_NAME "CbmRoot")
+set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
+set(CTEST_USE_LAUNCHERS ON)
+
+Set(CTEST_CONFIGURE_COMMAND " \"${CMAKE_EXECUTABLE_NAME}\" \"-G${CTEST_CMAKE_GENERATOR}\" \"-DCTEST_USE_LAUNCHERS=${CTEST_USE_LAUNCHERS}\" \"-DBUILD_FOR_TIDY=ON\" \"${CTEST_SOURCE_DIRECTORY}\" ")
+
+if ("$ENV{CTEST_SITE}" STREQUAL "")
+  set(CTEST_SITE "${fqdn}")
+else()
+  set(CTEST_SITE $ENV{CTEST_SITE})
+endif()
+
+if ("$ENV{LABEL}" STREQUAL "")
+  set(CTEST_BUILD_NAME "tidy-check")
+else()
+  set(CTEST_BUILD_NAME $ENV{LABEL})
+endif()
+
+ctest_start(Experimental)
+
+ctest_configure(BUILD "${CTEST_BINARY_DIRECTORY}")
+
+ctest_build(TARGET KFPARTICLE PARALLEL_LEVEL $ENV{NCPU})
+ctest_build(TARGET ANALYSISTREE PARALLEL_LEVEL $ENV{NCPU})
+ctest_build(TARGET GTEST PARALLEL_LEVEL $ENV{NCPU})
+ctest_build(TARGET NICAFEMTO PARALLEL_LEVEL $ENV{NCPU})
+
+ctest_build(TARGET TidyCheck PARALLEL_LEVEL $ENV{NCPU})
+
+ctest_submit()
diff --git a/cmake/scripts/find_files.sh b/cmake/scripts/find_files.sh
index 4ebd9ea054c79bd67c08360b8abe20d51a58ef51..55cc481eba63d27d9f9b326bcc535bf5235a5332 100755
--- a/cmake/scripts/find_files.sh
+++ b/cmake/scripts/find_files.sh
@@ -3,9 +3,7 @@
 # SPDX-License-Identifier: GPL-3.0-only
 # First commited by Florian Uhlig
 
-
-BASE_COMMIT=${FAIRROOT_FORMAT_BASE:-HEAD}
-GIT_CLANG_FORMAT_BIN=${FAIRROOT_GIT_CLANG_FORMAT_BIN:-git-clang-format}
+BASE_COMMIT=${1:-HEAD}
 
 FILES=$(git diff --name-only $BASE_COMMIT | grep -E '.*\.(h|hpp|c|C|cpp|cxx|tpl)$' | grep -viE '.*LinkDef.h$')