diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index abc064355eeb2aaaf1f89eead951c9db74df313d..5faeed9da50825a27b1c1adf12e1d8e04f15bd83 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,7 @@ stages:
   - checkRepository
   - checkFormat
   - build
+  - finalise
   - documentation
 
 RebaseCheck:
@@ -257,6 +258,22 @@ CbmRoot_Merge_realData:
     - cat Dart.cfg
     - $PWD/Dart.sh MergeRequest Dart.cfg
 
+InformCodeOwners:
+  stage: finalise
+  tags:
+    - CbmRoot_macosx
+  only:
+    refs:
+      - merge_requests
+    variables:
+      - $CI_MERGE_REQUEST_PROJECT_PATH == "computing/cbmroot" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"
+  script:
+    # Get the upstream repository manually. I did not find any other way to have it for
+    # comparison
+    - scripts/connect_upstream_repo.sh $CI_MERGE_REQUEST_PROJECT_URL
+    - git fetch upstream
+    - scripts/inform_codeowners.sh upstream
+
 pages:
   stage: documentation
   image: alpine
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..49d223de302c6066efd1308a276bd60f08c7a3b8
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1,110 @@
+# Ending a path in a `/` will specify the code owners for every file
+# nested in that directory, on any level
+/          @f.uhlig @v.friese @p.-a.loizeau
+/cmake/    @f.uhlig
+/external/ @f.uhlig
+/scripts/  @f.uhlig @p.-a.loizeau
+
+# Cleanup and proper separation needed
+/MQ/    @f.uhlig @p.-a.loizeau
+/fles/  @f.uhlig @p.-a.loizeau
+/macro/ @f.uhlig @p.-a.loizeau
+
+/macro/C2F/      @i.selyuzhenkov
+/macro/analysis/ @i.selyuzhenkov
+/macro/KF/       @s.gorbunov @v.akishina
+
+# Core
+/core/             @f.uhlig @v.friese @p.-a.loizeau
+/core/qa/          @s.gorbunov
+/core/data/test/   @f.uhlig
+/core/data/raw/    @p.-a.loizeau
+/core/base/utils/  @f.uhlig @s.lebedev
+/core/base/report/ @s.lebedev
+/core/base/draw/   @s.lebedev
+
+# Simulation
+/sim/                    @f.uhlig @v.friese @p.-a.loizeau
+/sim/transport/geosetup/ @e.lavrik
+
+# Reco
+/reco/              @f.uhlig @v.friese @p.-a.loizeau
+/reco/littrack/     @s.lebedev @andrey.lebedev
+/reco/L1/           @s.gorbunov @v.akishina
+/reco/eventbuilder/ @d.smith
+/reco/KF/           @s.gorbunov @v.akishina
+
+# Analysis
+/analysis/                                 @i.selyuzhenkov
+/analysis/PWGC2F/femtoscopy/               @d.wielanek
+/analysis/common/                          @i.selyuzhenkov
+/analysis/PWGHAD/                          @i.selyuzhenkov
+/analysis/PWGCHA/                          @i.selyuzhenkov
+/analysis/PWGDIL/                          @i.selyuzhenkov
+/analysis/PWGDIL/dielectron/pi0eta/        @c.pauly
+/analysis/PWGDIL/dielectron/papaframework/ @praisig @ebechtel
+/analysis/PWGDIL/dielectron/lmvm/          @i.selyuzhenkov
+/analysis/PWGDIL/dielectron/conversion/    @c.pauly
+/analysis/PWGDIL/dimuon/                   @i.selyuzhenkov
+
+## Detectors
+
+# TRD
+/core/data/trd/       @praisig
+/core/data/test/trd/  @praisig
+/core/detectors/trd/  @praisig
+/macro/trd/           @praisig
+/sim/detectors/trd/   @praisig
+/reco/detectors/trd/  @praisig
+
+# PSD
+/analysis/detectors/psd/ @karpushkin_AT_inr.ru
+/core/data/psd/          @karpushkin_AT_inr.ru
+/core/data/test/psd/     @karpushkin_AT_inr.ru
+/core/detectors/psd/     @karpushkin_AT_inr.ru
+/macro/psd/              @karpushkin_AT_inr.ru
+/sim/detectors/psd/      @karpushkin_AT_inr.ru
+/reco/detectors/psd/     @karpushkin_AT_inr.ru
+
+# MUCH
+/core/data/much/         @v.singhal
+/core/data/test/much/    @v.singhal
+/core/detectors/much/    @v.singhal
+/macro/much/             @v.singhal
+/sim/detectors/much/     @v.singhal
+/reco/detectors/much/    @v.singhal
+
+# TOF
+/analysis/detectors/tof/ @n.herrmann @i.deppner
+/core/data/tof/          @n.herrmann @i.deppner
+/core/data/test/tof/     @n.herrmann @i.deppner
+/core/detectors/tof/     @n.herrmann @i.deppner
+/fles/star2019/          @n.herrmann @i.deppner
+/macro/tof/              @n.herrmann @i.deppner
+/sim/detectors/tof/      @n.herrmann @i.deppner
+/reco/detectors/tof/     @n.herrmann @i.deppner
+/MQ/eTOF/                @n.herrmann @i.deppner
+/MQ/hitbuilder/          @n.herrmann @i.deppner
+
+# RICH
+/core/data/rich/      @s.lebedev
+/core/data/test/rich/ @s.lebedev
+/core/detectors/rich/ @s.lebedev
+/macro/rich/          @s.lebedev
+/sim/detectors/rich/  @s.lebedev
+/reco/detectors/rich/ @s.lebedev
+
+# STS
+/analysis/detectors/sts/ @v.friese
+/core/data/sts/          @v.friese
+/core/data/test/sts/     @v.friese
+/core/detectors/sts/     @v.friese
+/macro/sts/              @v.friese
+/sim/detectors/sts/      @v.friese
+/reco/detectors/sts/     @v.friese
+/MQ/sts/                 @v.friese
+
+# MVD
+/core/data/mvd/ @c.muentz @m.deveaux
+/macro/mvd/     @c.muentz @m.deveaux
+/mvd/           @c.muentz @m.deveaux
diff --git a/scripts/inform_codeowners.sh b/scripts/inform_codeowners.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4403dd252aebf0c65cff9557ede3768af65e5969
--- /dev/null
+++ b/scripts/inform_codeowners.sh
@@ -0,0 +1,178 @@
+#!/bin/bash
+
+main () {
+  # get passead parameters
+  # 1. git name of the official repo
+  if [[ ! $# -eq 1 ]]; then
+    echo "Pass the name of the upstream repository as argument"
+    exit 1
+  fi
+
+  # Since bash only supports dictionaries from version 4
+  # the dictionary is implemented as list. The values in
+  # the list are the directory (key) and the list of users owning
+  # this directory separated by a colon. If there is more than one
+  # user owning a directory the list of users is separated by a
+  # semicolon.
+  # An example of such an entry is "/macro/:f.uhlig;v.friese"
+  declare -a dictionary
+  declare -a unique_users
+
+  parse_codeowners_file
+  #declare -p dictionary
+
+  CHANGED_FILES=""
+  generate_file_list "$1"
+
+  ALL_USERS=""
+  generate_codeowners_list "$CHANGED_FILES"
+
+  generate_comment
+}
+
+
+# Add a key value pair to the dictionary
+function add_hash () {
+  dictionary+=("$1":"$2")
+}
+
+# extract the directory (key) from the passed string
+function get_key () {
+  dir="${1%%:*}"
+}
+
+# extract the user list (value) from the passed string
+function get_value () {
+  users="${1##*:}"
+}
+
+# Parse the CODEOWNERS file and store the information of owned directories
+# in a dictionray with the directory as key and the code owners as value
+# read a file line by line
+# Parse the remaining lines, split them at the first blank
+# First entry of the array is the directory, all other entries
+# are the list of people to inform
+# We have to check that the CODEOWNERS file is properly formated since the
+# used parser is very simply
+function parse_codeowners_file () {
+  while read line; do
+    # exclude commented and empty lines
+    if [[ $line =~ ^\# || $line =~ ^$ ]]; then
+     dummy=$line
+    else
+      # split the line at blank characters and store the results in an array
+      IFS=' ' read -ra my_array <<< "$line"
+      # first entry is the directory
+      directory="${my_array[0]}"
+      # in case of two entries the second is the user
+      if [[ ${#my_array[@]} -eq 2 ]]; then
+        users="${my_array[1]}"
+      # in case of more entries the entries from the second to the last are a list of users
+      # Add the users to a semicolon separated list and store the list in the user array
+      else
+        arr2=("${my_array[@]:1:${#my_array[@]}}")
+        newUserList=""
+        for i in "${arr2[@]}"; do
+          newUserList="$newUserList;$i"
+        done
+        newUserList="${newUserList:1}"
+        users="$newUserList"
+      fi
+      add_hash "$directory" "$users"
+    fi
+  done < CODEOWNERS
+}
+
+function generate_codeowners_list () {
+  CHANGED_FILES=$1
+  all_users=""
+  for file in $CHANGED_FILES; do
+#    echo $file
+    file="/$file"
+    inform_users=""
+    longest_match=0
+    for entry in "${dictionary[@]}"; do
+      get_key $entry
+      if [[ $file =~ ^$dir ]]; then
+        get_value $entry
+        # echo "The length of the match is ${#dir}"
+        if [[ ${#dir} -gt $longest_match ]]; then
+          inform_users=$users
+        fi
+      fi
+    done
+#    echo "We have to inform user $inform_users for file $file"
+    all_users="$all_users;$inform_users"
+  done
+  ALL_USERS="${all_users:1}"
+
+  # remove duplications from string
+  IFS=';'
+  read -ra users <<< "$ALL_USERS"
+
+  for user in "${users[@]}"; do   # access each element of array
+    if [[ ! " ${unique_users[@]} " =~ " ${user} " ]]; then
+      unique_users+=("$user")
+    fi
+  done
+  OWNERS_LIST=${unique_users[@]}
+
+  echo "We have to inform the following users about the code ownership: $OWNERS_LIST"
+}
+
+function generate_file_list() {
+
+  if [[ $# -eq 1 ]]; then
+    UPSTREAM=$1
+  else
+    if [ -z $UPSTREAM ]; then
+      UPSTREAM=$(git remote -v | grep git.cbm.gsi.de[:/]computing/cbmroot | cut -f1 | uniq)
+      if [ -z $UPSTREAM ]; then
+        echo "Error: Name of upstream repository not provided and not found by automatic means"
+        echo 'Please provide if by checking your remotes with "git remote -v" and exporting UPSTREAM'
+        echo "or passing as an argument"
+        exit -1
+      fi
+    fi
+  fi
+  echo "Upstream name is :" $UPSTREAM
+
+  BASE_COMMIT=$UPSTREAM/master
+  CHANGED_FILES=$(git diff --name-only $BASE_COMMIT)
+}
+
+function generate_comment() {
+
+  # Get Information about the MR in JSON format
+  # Check if the returned string contains the signature that the CodeOwners label is set
+  # Only create GitLab comment if there is no comment yet (CodeOwners label not set)
+  MRInfo=$(curl -s --request GET --header "PRIVATE-TOKEN: $COMMENT_TOKEN" "https://git.cbm.gsi.de/api/v4/projects/$CI_MERGE_REQUEST_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID" | grep labels.*\\[.*CodeOwners.*\\])
+  if [[ -z $MRInfo ]]; then
+
+    comment="Dear "
+    for user in "${unique_users[@]}"; do
+      comment="$comment $user,"
+    done
+    comment="$comment
+
+you have been identified as code owner of at least one file which was changed with this merge request.
+
+Please check the changes and approve them or request changes."
+
+
+    # Add a comment to the merge request which informs code owners about their duty to review
+    curl -s --request POST \
+       -d "body=$comment" \
+       --header "PRIVATE-TOKEN: $COMMENT_TOKEN" \
+       "https://git.cbm.gsi.de/api/v4/projects/$CI_MERGE_REQUEST_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"
+
+    # On first call set a label which can be checked later on to avoid sending the comment over and over again
+    curl -s --request PUT \
+       --header "PRIVATE-TOKEN: $COMMENT_TOKEN" \
+       "https://git.cbm.gsi.de/api/v4/projects/$CI_MERGE_REQUEST_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID?add_labels=CodeOwners"
+  else
+    echo "Information message already exits, so there is no need to create another one."
+  fi
+}
+
+main "$@"