Last active
June 8, 2020 10:42
-
-
Save T3sT3ro/902d67f29d2c913b3fef6034776777e0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Service side git pre-receive hook | |
# Written by Gautier Pelloux-Prayer <[email protected]>, 2014 | |
# | |
# Public domain | |
# | |
# Simply put this script in server git bare repository <your-git-folder>/hooks and | |
# apply 'chmod +x pre-receive'. | |
# | |
# Prevent pushing invalid submodule reference in a git repository: | |
# - reference to non-yet-pushed submodule commit "reference is not a tree" error | |
# by forcing user to push submodules first. This is based on the following information: | |
# http://stackoverflow.com/questions/3418674/bash-shell-script-function-to-verify-git-tag-or-commit-exists-and-has-been-pushe | |
# - unwanted submodule downgrading or branch switching, mainly caused by previously | |
# forgot call to 'git submodule pull'. User can still do that if the commit message specify it. | |
ENABLE_DEBUG=0 | |
function ECHO_DEBUG { | |
[ $ENABLE_DEBUG = 1 ] && echo -e "\033[33m$@\033[0m" | |
} | |
# get the previous commit ID, new commit ID, and ref | |
read oldoldrev newrev ref | |
# creating or deleting branch, no need to check | |
if [ ${oldoldrev:0:8} = "00000000" ] || [ ${newrev:0:8} = "00000000" ]; then | |
exit 0 | |
fi | |
commit_msg=$(git log --format=%s -n 1 $newrev) | |
ECHO_DEBUG "$oldoldrev $newrev $ref msg='$commit_msg'" | |
# Get a list of submodules | |
git config --file <(git show $ref:.gitmodules) --get-regexp 'submodule..*.path' | | |
while read key path; do | |
url=$(git config --file <(git show $ref:.gitmodules) --get "${key/.path/.url}") | |
# submodules URL are (mainly) absolute URLs: eg git://some.random.url.org/a-repository | |
# hook can only check for local repositories stored on the same filesystem, so we assume that | |
# if this is our submodule, it will be located at ../a-repository.git | |
url=$(perl -pe 's/.*:(.*?)(?:.git)?$/\1/' <<< $url) | |
# gitlab disk path in hashed storage [...]/@hashed/... | |
url="/srv/gitlab/repositories/$(gitlab-rails runner "Project.find_by_full_path('$url').disk_path")" | |
if [ ! -d "$url" ]; then | |
ECHO_DEBUG "Modified submodule $url but could not find it locally! Canceling check..." | |
continue | |
fi | |
# get absolute URL | |
url=$(cd $url && pwd) | |
# foreach commit being pushed, check if this one has been modified | |
git diff "$ref..$newrev" -- "$path" | grep '^-Subproject commit' -A1 | cut -d' ' -f3 | | |
while read sub_old_rev && read sub_new_rev; do | |
ECHO_DEBUG "Checking submodule update: old=$sub_old_rev -> $sub_new_rev" | |
ECHO_DEBUG "$commit_msg | $url" | |
# unset to fix `git *` commands crashing for other git directories. Maybe connected to git GIT-SH-SETUP(1) | |
unset GIT_ALTERNATE_OBJECT_DIRECTORIES | |
unset GIT_OBJECT_DIRECTORY | |
# for each submodule updated, check if the commit exists | |
LINES=$(GIT_DIR="$url" git branch --contains "$sub_new_rev" 2>/dev/null | wc -l) | |
if [ $LINES == 0 ]; then | |
echo "*****************************************" | |
echo "Error in ${newrev:0:8}($commit_msg):" | |
echo " Subcommit '${sub_new_rev:0:8}' not found in submodule '$path' ($url)." >&2 | |
echo " Please push that submodule first!" >&2 | |
echo "*****************************************" | |
exit 1 | |
else | |
# and also check that the new reference is NOT parent of the old reference (submodule downgrade) | |
if GIT_DIR="$url" git rev-list "$sub_old_rev" | grep -q "$sub_new_rev"; then | |
expc_pattern="[Outgrade submodule]" | |
if grep -Fq "$expc_pattern" <<< "$commit_msg"; then | |
echo "*****************************************" | |
echo "Warning! You are going to out-grade submodule '$path' from ${sub_old_rev:0:8} to ${sub_new_rev:0:8}, but since your commit message contains '$expc_pattern', I'll let you do that." | |
echo "*****************************************" | |
else | |
echo "*****************************************" | |
echo "Error in ${newrev:0:8}($commit_msg):" | |
echo " Error! You are trying to out-grade submodule '$path' from ${sub_old_rev:0:8} to ${sub_new_rev:0:8}, which usually means you forgot to execute 'git submodule update'. Did you?" | |
echo " If you REALLY want to OUTGRADE submodule '$path', please add '$expc_pattern' in your commit message (id='${newrev:0:8}') using 'git commit --amend'. Current message is: '$commit_msg'". | |
echo "*****************************************" | |
exit 1 | |
fi | |
# finally check that old reference IS reference of new reference (submodule branch switch) | |
elif ! GIT_DIR="$url" git rev-list "$sub_new_rev" | grep -q "$sub_old_rev"; then | |
expc_pattern="[Switch submodule branch]" | |
if grep -Fq "$expc_pattern" <<< "$commit_msg"; then | |
echo "*****************************************" | |
echo "Warning! You are going to switch submodule branch '$path' from ${sub_old_rev:0:8} to ${sub_new_rev:0:8}, but since your commit message contains '$expc_pattern', I'll let you do that." | |
echo "*****************************************" | |
else | |
echo "*****************************************" | |
echo "Error in ${newrev:0:8}($commit_msg):" | |
echo " Error! You are trying to switch submodule branch '$path' from ${sub_old_rev:0:8} to ${sub_new_rev:0:8}, which usually means you forgot to execute 'git submodule update'. Did you?" | |
echo " If you REALLY want to SWITCH BRANCH submodule '$path', please add '$expc_pattern' in your commit message (id='${newrev:0:8}') using 'git commit --amend'. Current message is: '$commit_msg'". | |
echo "*****************************************" | |
exit 1 | |
fi | |
fi | |
fi | |
done || exit 1 | |
done || exit 1 | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment