pre-commit script for submodule hygiene

Our team at work is using git submodules to track re-usable code across projects, and it’s been pretty good so far, but we have hit minor snags along the way (such as the absence of a ‘git submodule rm’ command!). Another one is that using submodules adds a step to the sequence of things you have to do to publish changes: pushing submodule commits. It’s an easy thing to forget, but it’s a headache for anyone on the other end of a pull when git-checkout-index fails. This pre-commit hook script will cause the commit to fail if the commit contains new submodule moments and those moments are not present in the corresponding submodule origin.

#!/bin/sh

function array_has
{
    for item in $1
    do
	if [ "$item" = "$2" ]; then
	    return 1;
	fi
    done

    return 0;
}

diffs=`git diff --cached --name-only`

IFS=`echo -en "\n\b"`
for smstat in `git submodule 2>/dev/null`
do
    if [[ "$smstat" =~ '^\+(.*)' ]]; then
	smstat=${BASH_REMATCH[1]}
    fi

    head=$(echo $smstat | awk '{print $1}')
    path=$(echo $smstat | awk '{print $2}')
    moment=$(git ls-files -s $path | awk '{print $2}')

    array_has $diffs $path
    if [ $? ]; then
	pushd >/dev/null $path
	for rhead in $(git ls-remote -h origin 
	               | awk '{print $1}')
	do
	    if [ "$(git rev-list $moment ^$rhead)" 
		    != "" ] 
	    then
		unpub=1;
	    fi
	done
    fi

    if [[ $unpub -gt 0 ]]; then
	echo -n "ERROR: you are trying to commit "
	echo -n "unpublished changes to the $path "
	echo    "submodule."
	exit 1;
    fi

done

exit 0;

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>