In some CI setups special keywords might be used to trigger specific behavior in the pipeline. For example the CI pipelines might build feature branches if and only if the latest git commit message in a branch-push contains keywords like [build].
Even though this works the commit-messages become polluted with these keywords. So I was wondering whether it's possible to make some sort of local git-commit / git-push hook to automatically cleanup the latest git-commit message if it contains the [build] keyword a few seconds after the original commit message makes it across (to trigger the CI pipeline).
Here is the solution I came up with. Seems to be working fine based on my tests. Place this script as '.git/hooks/pre-push' and make sure to 'chmod +x .git/hooks/pre-push' too.
It detects whether the latest git-commit message contains the keyword [build] and if it does it pushes it across, waits for a few seconds (*), it amends the latest git-commit message to clean it up and it then carefully force-repushes the branch.
Feature branches cooperate very well with this technique even when multiple devs are working on them - it's highly unlikely that within that sensitive period of 3secs someone else will tweak the branch.
Note that technique is meant to be applied on feature branches - not the cornerstone branches and especially not 'master', 'main' or even 'develop'.
#!/bin/bash
# https://stackoverflow.com/a/77462557/863651
#
# to make this pre-push-hook trigger across all of your repositories just name it 'pre-push', put it under your git-global-hooks directory and give it +x permissions
#
# tip: to setup your git-global-hooks directory simply run 'git config --global core.hooksPath /path/to/my/centralized/hooks' (requires git 2.9+ released in June 2016 on your machine)
set -e
declare current_branch="$(git symbolic-ref HEAD)"
if [[ "$current_branch" == "refs/head/main" || "$current_branch" == "refs/head/master" || "$current_branch" == "refs/head/develop" ]]; then
exit 0 # better not fiddle with the commit history of these branches
fi
declare latest_commit_message="$(git log --format=%B -n 1 HEAD)"
declare latest_commit_message_lowercased="$(echo "$latest_commit_message" | tr '[:upper:]' '[:lower:]')"
# "${latest_commit_message,,}" unfortunately we cant use this syntax to lowercase the string on macos because it runs on an old version of bash
if [[ "${latest_commit_message_lowercased}" == *"[build]"* ]]; then
# commit message contains [build] keyword - will push it and clean it up a few seconds later via ammend + force repush
cat <<EOL >./.git/ammend_commit_a_few_seconds_later.sh
#!/bin/bash
set -e
sleep 3
declare commit_message_to_clean_up=\$(git log --format=%B -n 1 HEAD) # get the latest commit message
declare commit_message_cleaned="\$(echo "\${commit_message_to_clean_up}" | sed -E 's/[ \\t]*\\[build\\][ \\t]*/ /ig' | sed -E '1s/^[ \\t]+//')" # we cant use \s in these regexes because if we do then in macos these regexes go haywire
if [[ "\${commit_message_to_clean_up}" == "\${commit_message_cleaned}" ]]; then # just to be on the safe side
# echo "** commit-ammender: nothing to do"
exit 0
fi
git commit --amend --message "\${commit_message_cleaned}" # amend the latest commit message to replace [build] with a single whitespace
git push origin "\$(git rev-parse --abbrev-ref HEAD)" --force-with-lease # force-push the branch
echo "Branch pushed and commit message amended."
rm -f "./.git/ammend_commit_a_few_seconds_later.sh" # in macos sometimes this helper file is not getting deleted doesnt really matter "out of sight - out of mind"
EOL
chmod +x ./.git/ammend_commit_a_few_seconds_later.sh
./.git/ammend_commit_a_few_seconds_later.sh &
fi
exit 0