Tag Archives: git

Uncategorized

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;
Uncategorized

submodule moment

Seems that new features and concepts appear in Git at such a steady pace that it’s difficult for the jargon to keep up. I don’t follow the git mailing list as closely as I should: it’s too high-traffic and I already don’t have enough time to do the work I have on my plate.

Right now what’s getting tongue-tied is referring to the commit-id of a submodule stored in the HEAD of a containing repository. As far as I know, there’s not a good, succinct and unambiguous term for this commit-id.

I have decided to call it the ‘submodule moment‘, because moment captures the ideas nicely:

  • a submodule at a particular point in its history (which we typically think of as linear in time)
  • a property of the submodule as it relates to it’s parent repository (a moment in mathematics is a statistical property of random variables)

More importantly, though, this term disambiguates itself from the other verbal references to commit identifiers.

Uncategorized

Extended Git Submodule Status

At work I’m involved in some projects that will very likely make heavy use of submodules. The reason is that submodules make it very convenient to make use of a set of “common” code without a ton of duplication. We’re currently breaking our “common” code into packages that can be included in a project independent of each other, and they will likely exist as submodules.

The challenge is that submodule support in Git isn’t quite as polished as you’d like it to be. What do you do if you have 20+ submodules, some of which may be on a branch and contain uncommitted changes that need to be dealt with? What if it’s been two weeks since you last looked at it?

One solution would be to write a git-all script like this (which is simpler than a real git-all would be, and may actually be incorrect):

#!/bin/sh
# git-all

for repo in $(find . -name '.git' -type d | xargs dirname); do
    pushd $repo >/dev/null
    git $*
    popd >/dev/null
done

We used this approach, particularly before the advent of git-submodule, and some projects still use it. It’s not a bad approach, but it’s not really what you want.

Here’s another solution that’s a little more integrated into git itself:

#!/bin/sh
# git-submodule-changes

. git-sh-setup

status=0

for sm in `git-submodule status
              | sed 's/^[[:space:]]*\(.*\)/\1/'
              | cut -d ' ' -f 2`;
do
    pushd $sm >/dev/null
    substat=$(git-ls-files -d -m -o -s -u -t
                  | cut -d ' ' -f 1 | sort | uniq)
    substat=$(echo $substat | tr -d '[[:space:]]')

    if [ "$substat" != "" ]; then
        status=1
    fi

    printf "%7s %s\n" "$substat" $sm
    popd >/dev/null
done

exit $status

I think this works better. An added benefit is that you can do line-based scripting with tools like grep, sed, and awk (and tons of other unix utilities) with it, because all the info appears on one line:

{master}$ git submodule-changes
      ? sub0
        sub1

This isn’t terribly illuminating, but the format is just “[HMRCK?] submodule-path” on each line. The optional codes are the single-character status codes from git-ls-files.

An even better solution would be to build this into git-submodule.sh, which I will look into as I get time.

Uncategorized

Useful Git Prompt

Here’s a snippet of bash script that I wrote to make my prompt tell me when I’m in a git repository and what branch I’m on.

And to my pleasant surprise, the PROMPT_COMMAND mechanism will respect color codes, so if you have your branch listings in git colorized, it will be reflected in your prompt.

function rfind
{
    dir=$PWD
    while [ ! -e $1 ]; do
        if [ $PWD == "/" ]; then
            command cd $dir
            return 1
        else
            command cd ..
        fi
    done

    rfdir=$PWD
    command cd $dir

    return 0
}

function git_dir
{
     typeset str
     rfdir=""
     rfind '.git'
     if [ $? -eq 0 ]; then
        str="{`git branch | grep '*' | cut -d ' ' -f 2`}"
        rfdir=`echo $rfdir | sed "s#$HOME#~#"`
     fi

    echo "$str"
}

function mkprompt
{
    typeset branch
    branch=`git_dir`
    PS1="${branch}$ "
}

PROMPT_COMMAND=mkprompt
export PROMPT_COMMAND