diff --git a/docs/GETTING-STARTED.md b/docs/GETTING-STARTED.md
index 605627caaf8070b302f8d3400eff26ee233f7b2a..366c5bd2303063f4e8e554da37533c7837c3cdcd 100644
--- a/docs/GETTING-STARTED.md
+++ b/docs/GETTING-STARTED.md
@@ -740,3 +740,46 @@ To be used, you need to:
 +    IMAGE_TAG: latest
  ...
 ```
+
+#### Avoid regression after stable release
+
+When the next release is prepared in the `$TESTING_BRANCH`, any fixes
+should be applied to the `$DEV_BRANCH` to avoid regressions.
+
+You can either manually merge the fixes to `$DEV_BRANCH` or use the
+`.git:merge-to` template job to automatically merge the release tag to
+the `$DEV_BRANCH`.
+
+To do so, you need to:
+
+1. include [`templates/Rules.yaml`](#target-when-to-run-jobs) file to
+   define the `.not-on-stable` rules template
+2. include the [`templates/Git.yaml`](templates/Git.yaml) file
+3. be sure to have the `release` stage to your current `stages` in
+   your `.gitlab-ci.yml`
+4. define the `merge-to-dev` job extending [`.git:merge-to`](templates/Git.yaml#L88-L185)
+
+```diff
+--- .gitlab-ci.yaml.orig
++++ .gitlab-ci.yaml
+@@ -60,7 +60,8 @@
+
+
+ ###############################################################################
+-# `release` stage: `semantic-release`, `testing-prerelease`, `tag *`
++# `release` stage: `semantic-release`, `testing-prerelease`,
++#                  `merge-to-dev`, `tag *`
+ ###############################################################################
+ # Create the release versions on `$STABLEE_BRANCH`
+ new-release: {extends: '.semantic-release:stable'}
+@@ -69,6 +70,9 @@
+ # update `release.config.js` variable `betaBranch`
+ testing-prerelease: {extends: '.semantic-release:testing}
+
++# Avoid regression by merging all pre-release fixes to `$DEV_BRANCH`
++merge-to-dev: {extends: '.git:merge-to', variables: {GIT_MERGE_TARGET: $DEV_BRANCH}}
++
+ ## tag contribution branches with a more stable name than `git-${CI_COMMIT_SHORT_SHA}`
+ tag contrib branch:
+   extends:
+```
diff --git a/templates/Git.yaml b/templates/Git.yaml
index 9082e562366b70c340b8bc0e4317ebe002612e58..69bb71fc8c7c0d69e7ea78167edcfd07f27757a5 100644
--- a/templates/Git.yaml
+++ b/templates/Git.yaml
@@ -83,4 +83,104 @@
                   --to   "${COMMITLINT_TO}"
                   --verbose'
     - echo -e "\e[0Ksection_end:`date +%s`:commitlint\r\e[0K"
+
+
+#
+# .git:merge-to
+# =============
+#
+# Merge `${GIT_MERGE_SOURCE}` to a `${GIT_MERGE_TARGET}` branch.
+#
+# USAGE
+# =====
+#
+# include:
+#   - project: baby-gnu/ci-tools
+#     ref: main
+#     file: /templates/Git.yaml
+#
+# stages:
+#   - release
+#
+# merge-to-dev: {extends: '.git:merge-to', variables: {GIT_MERGE_TARGET: $DEV_BRANCH}}
+#
+# REQUIREMENTS
+# ============
+#
+# - a `release` stage must be present in your pipeline or it must be
+#   overriden by the extending job to feet your need
+#
+# - the `.on-release-tag` rules templates or it must be overriden by
+#   the extending job to feet your need
+#
+# - a `${GIT_MERGE_TARGET}` branch name
+#
+# - a `${GIT_MERGE_SOURCE}` reference, by default `${CI_COMMIT_TAG}`
+#
+# - a `${GITLAB_TOKEN}` to push the merged `${GIT_MERGE_TARGET}`
+#   branch
+#
+# OPTIONAL VARIABLES
+# ==================
+#
+# - `GIT_AUTHOR_NAME`: name of the optional merge commit author,
+#   extracted from `CI_COMMIT_AUTHOR` by default
+#
+# - `GIT_AUTHOR_EMAIL`: email of the optional merge commit author,
+#   extracted from `CI_COMMIT_AUTHOR` by default
+#
+# - `GIT_COMMITTER_NAME`: name of the optional merge commit committer,
+#   default to `${GIT_AUTHOR_NAME}`
+#
+# - `GIT_COMMITTER_EMAIL`: email of the optional merge commit, default
+#   to `${GIT_AUTHOR_EMAIL}`
+#
+# - `GIT_MERGE_TO_IMAGE`: name of the docker image to execute `git`
+#   commands
+#
+# USED CI VARIABLES
+# =================
+#
+# - `CI_COMMIT_TAG`
+#
+# - `CI_COMMIT_AUTHOR`
+#
+# - `CI_REPOSITORY_URL`
+#
+# - `CI_JOB_TOKEN`
+#
+.git:merge-to:
+  stage: release
+  extends: .on-release-tag
+  image: "${GIT_MERGE_TO_IMAGE}"
+  variables:
+    GIT_MERGE_TO_IMAGE: 'bitnami/git:latest'
+    GIT_MERGE_SOURCE: ${CI_COMMIT_TAG}
+    GIT_MERGE_TARGET: ''
+  script:
+    # Configure git to be able to create merge commit
+    # Extract author name and mail if not SET
+    - echo -e "\e[0Ksection_start:`date +%s`:git-configure[collapsed=true]\r\e[0KConfigure git variables from '${CI_COMMIT_AUTHOR}'"
+    - export GIT_AUTHOR_NAME="${GIT_AUTHOR_NAME:-${CI_COMMIT_AUTHOR% <*}}"
+    - export TEMP_MAIL_PREFIX="${CI_COMMIT_AUTHOR#*<}"
+    - export GIT_AUTHOR_EMAIL="${GIT_AUTHOR_EMAIL:-${TEMP_MAIL_PREFIX%>}}"
+    - export GIT_COMMITTER_NAME="${GIT_COMMITTER_NAME:-${GIT_AUTHOR_NAME}}"
+    - export GIT_COMMITTER_EMAIL="${GIT_COMMITTER_EMAIL:-${GIT_AUTHOR_EMAIL}}"
+    - echo -e "\e[0Ksection_end:`date +%s`:git-configure\r\e[0K"
+    # Add `upstream` remote to get access to `upstream/dev`
+    # Use `${GITLAB_TOKEN}` for write permission
+    - echo -e "\e[0Ksection_start:`date +%s`:git-remote-upstream-add[collapsed=true]\r\e[0KAdd upstream repository and checkout '${GIT_MERGE_TARGET}'"
+    - "git remote show upstream 2> /dev/null || git remote add upstream ${CI_REPOSITORY_URL/${CI_JOB_TOKEN}/${GITLAB_TOKEN}}"
+    - 'git fetch --all'
+    - 'git checkout -B ${GIT_MERGE_TARGET} upstream/${GIT_MERGE_TARGET}'
+    - echo -e "\e[0Ksection_end:`date +%s`:git-remote-upstream-add\r\e[0K"
+    # Merge the release tag
+    - echo -e "\e[0Ksection_start:`date +%s`:git-merge-release[collapsed=true]\r\e[0KMerge '${GIT_MERGE_SOURCE}' in '${GIT_MERGE_TARGET}'"
+    - 'git merge --no-edit ${GIT_MERGE_SOURCE}'
+    - 'git push upstream ${GIT_MERGE_TARGET}'
+    - echo -e "\e[0Ksection_end:`date +%s`:git-merge-release\r\e[0K"
+    # Remove `upstream` to avoid caching `${GITLAB_TOKEN}`
+    - echo -e "\e[0Ksection_start:`date +%s`:git-cleanup[collapsed=true]\r\e[0KCleanup git repository"
+    - "git remote remove upstream"
+    - echo -e "\e[0Ksection_end:`date +%s`:git-cleanup\r\e[0K"
 ...