diff --git a/.commitlintrc.yaml b/.commitlintrc.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..260c243fdf8193d18e824a12625c4e51b7a97bfc
--- /dev/null
+++ b/.commitlintrc.yaml
@@ -0,0 +1,6 @@
+extends:
+  - '@commitlint/config-conventional'
+rules:
+  body-max-line-length: [2, 'always', 120]
+  footer-max-line-length: [2, 'always', 120]
+  header-max-length: [2, 'always', 72]
diff --git a/commitlint.config.js b/commitlint.config.js
deleted file mode 100644
index e62b77c7af9cd1c61e6e1f09c53c6200610271c3..0000000000000000000000000000000000000000
--- a/commitlint.config.js
+++ /dev/null
@@ -1,8 +0,0 @@
-module.exports = {
-  extends: ['@commitlint/config-conventional'],
-  rules: {
-    'body-max-line-length': [2, 'always', 120],
-    'footer-max-line-length': [2, 'always', 120],
-    'header-max-length': [2, 'always', 72],
-  },
-};
diff --git a/templates/Git.yaml b/templates/Git.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..9082e562366b70c340b8bc0e4317ebe002612e58
--- /dev/null
+++ b/templates/Git.yaml
@@ -0,0 +1,86 @@
+# -*- coding: utf-8 -*-
+# vim: ft=yaml
+#
+# Hidden template jobs to be used in `.gitlab-ci.yml`
+#
+# - `.git:commitlint`: verify formatting of commit messages
+#
+# - `.git:merge-to`: merge release tags to the `${GIT_MERGE_TARGET}` branch
+#
+---
+#
+# .git:commitlint
+# ===============
+#
+# Verify formatting of all commit messages in the range
+# `${BASE_BRANCH}..${CI_COMMIT_SHA}`.
+#
+# USAGE
+# =====
+#
+# include:
+#   - project: baby-gnu/ci-tools
+#     ref: main
+#     file: /templates/Git.yaml
+#
+# stages:
+#   - lint
+#
+# commitlint: {extends: '.git:commitlint'}
+#
+# REQUIREMENTS
+# ============
+#
+# - a `lint` stage must be present in your pipeline or it must be
+#   overriden by the extending job to feet your need
+#
+# - the `.not-on-stable` rules templates or it must be overriden by
+#   the extending job to feet your need
+#
+# - a `${CI_COMMIT_SHA}` pointing to the last commit to verify
+#
+# - a `commitlint` configuration file
+#
+# OPTIONAL VARIABLES
+# ==================
+#
+# - `BASE_BRANCH`: name of the referrence branch, defaults to
+#   `${CI_DEFAULT_BRANCH}`
+#
+# - `GIT_COMMITLINT_IMAGE`: name of the `commitlint` docker image to use
+#
+# USED CI VARIABLES
+# =================
+#
+# - `CI_COMMIT_SHA`
+#
+# - `CI_DEFAULT_BRANCH`
+#
+# SEE ALSO
+# ========
+#
+# - Upstream: https://github.com/conventional-changelog/commitlint/
+#
+.git:commitlint:
+  stage: lint
+  extends: .not-on-stable
+  image: "${GIT_COMMITLINT_IMAGE}"
+  variables:
+    GIT_COMMITLINT_IMAGE: 'hub.eole.education/eole/commitlint:latest'
+    BASE_BRANCH: "${CI_DEFAULT_BRANCH}"
+  script:
+    - echo -e "\e[0Ksection_start:`date +%s`:git-fetch[collapsed=true]\r\e[0KFetch all remotes"
+    - 'git fetch --all'
+    - echo -e "\e[0Ksection_end:`date +%s`:git-fetch\r\e[0K"
+    # Set default commit hashes for `--from` and `--to`
+    - echo -e "\e[0Ksection_start:`date +%s`:commitlint-configure[collapsed=true]\r\e[0KCalculate 'COMMITLINT_FROM' and 'COMMITLINT_TO'"
+    - 'export COMMITLINT_FROM="$(git merge-base origin/${BASE_BRANCH} HEAD)"'
+    - 'export COMMITLINT_TO="${CI_COMMIT_SHA}"'
+    - echo -e "\e[0Ksection_end:`date +%s`:commitlint-configure\r\e[0K"
+    # Run `commitlint`
+    - echo -e "\e[0Ksection_start:`date +%s`:commitlint\r\e[0KVerify commit message between '${COMMITLINT_FROM}..${COMMITLINT_TO}'"
+    - 'commitlint --from "${COMMITLINT_FROM}"
+                  --to   "${COMMITLINT_TO}"
+                  --verbose'
+    - echo -e "\e[0Ksection_end:`date +%s`:commitlint\r\e[0K"
+...
diff --git a/templates/Lint/Commitlint.yaml b/templates/Lint/Commitlint.yaml
index 154d9dbe9e0f03a68d207a1ce9e17c331e44872d..f73e85ea41965e74b767f7e95590ba72dcbffe2d 100644
--- a/templates/Lint/Commitlint.yaml
+++ b/templates/Lint/Commitlint.yaml
@@ -1,59 +1,23 @@
 # -*- coding: utf-8 -*-
 # vim: ft=yaml
 #
-# Verify formatting of all commit messages in the range
-# `$BASE_BRANCH..$CI_COMMIT_SHA`.
-#
-# USAGE
-# =====
-#
-# include:
-#   - project: EOLE/infra/ci-tools
-#     ref: stable
-#     file: /templates/Rules.yaml
-#   - project: EOLE/infra/ci-tools
-#     ref: stable
-#     file: /templates/Lint/Commitlint.yaml
-#
-# stages:
-#   - lint
-#
-# OPTIONAL VARIABLES
-# ==================
-#
-# - `BASE_BRANCH`: name of the referrence branch, defaults to
-#   `${CI_DEFAULT_BRANCH}`
-# - `COMMITLINT_IMAGE`: name of the `commitlint` docker image to use
-#
-# REQUIREMENTS
-# ============
-#
-# - The `.not-on-stable` rules template
-# - A `lint` stage must be present in your pipeline or it must be
-#   overriden by the extending job to feet your need.
-#
-# USED CI VARIABLES
-# =================
-#
-# - `CI_COMMIT_SHA`
-# - `CI_DEFAULT_BRANCH`
-# - `CI_REPOSITORY_URL`
+# Backward compatible template.
+# Use `templates/Git.yaml` instead
 #
 ---
-commitlint:
-  stage: lint
-  extends: .not-on-stable
-  image: "$COMMITLINT_IMAGE"
-  variables:
-    COMMITLINT_IMAGE: 'hub.eole.education/eole/commitlint:latest'
-    BASE_BRANCH: "${CI_DEFAULT_BRANCH}"
+include:
+  local: /templates/Git.yaml
+
+commitlint: {extends: '.git:commitlint'}
+
+deprecation-commitlint:
+  # Execute at the same time
+  extends: commitlint
+  # The failure must be a warning
+  allow_failure: true
   script:
-    - 'git fetch --all'
-    # Set default commit hashes for `--from` and `--to`
-    - 'export COMMITLINT_FROM="$(git merge-base origin/${BASE_BRANCH} HEAD)"'
-    - 'export COMMITLINT_TO="${CI_COMMIT_SHA}"'
-    # Run `commitlint`
-    - 'commitlint --from "${COMMITLINT_FROM}"
-                  --to   "${COMMITLINT_TO}"
-                  --verbose'
+    - 'echo "DEPRECATION WARNING: replace commitlint by .git:commitlint"'
+    - 'echo "DEPRECATION WARNING: see /templates/Git.yaml"'
+    # Be visible in the CI pipeline
+    - '/bin/false'
 ...