#!/usr/bin/env bash # # Wrapper for git to handle more subdirs at the same time # # no params, no action if [ "$#" -eq "0" ] ; then git echo echo "Additional options available only in this 'g' wrapper:" echo echo "Usage: g [options] [git commands]" echo " -f Force - act on all the repos, not only the changed ones" echo " -s Silent - do not report the repo names." echo " -v Verbose - Print git commands." echo " -1 report the repos name on the first line of the output as :" echo " -z just to some house cleaning (hooks mostly). this is a stand-alone option as in ./g -z" echo " --set-push-user [username] re-write an existing tree's config with an fd.o commit account name" echo " --last-working checks out the last known working build (useful for windows)"; echo " --set-last-working adds a note denoting a working build"; echo " --push-notes pushes all notes"; exit $? fi if [ ! "`type -p git`" ]; then echo "Cannot find the git binary! Is git installed and is in PATH?" exit 1 fi pushd $(dirname $0) > /dev/null COREDIR=$(pwd) popd > /dev/null refresh_hooks() { repo=$1 case "$repo" in core) pushd $COREDIR > /dev/null for hook_name in $(ls -1 $COREDIR/git-hooks) ; do hook=".git/hooks/$hook_name" if [ ! -x "$hook" ] ; then rm -f "$hook" ln -sf "$COREDIR/git-hooks/$hook_name" "$hook" fi done popd > /dev/null ;; translations) if [ -d $COREDIR/clone/translations ] ; then pushd $COREDIR/clone/translations > /dev/null for hook_name in $(ls -1 $COREDIR/clone/translations/git-hooks); do hook=".git/hooks/$hook_name" if [ ! -x "$hook" ] ; then rm -f "$hook" ln -sf "$COREDIR/clone/translations/git-hooks/$hook_name" "$hook" fi done # .gitattribute should be per-repo, avoid entangling repos if [ -L .gitattributes ] ; then rm -f .gitattributes fi popd > /dev/null fi ;; binfilter|help|dictionaries) if [ -d $COREDIR/clone/$repo ] ; then pushd $COREDIR/clone/$repo > /dev/null # fixme: we should really keep these per-repo to # keep the repos independant. since these two # are realy not independant yet, we keep using core's hooks for hook_name in $(ls -1 $COREDIR/git-hooks) ; do hook=".git/hooks/$hook_name" if [ ! -x "$hook" ] ; then rm -f "$hook" ln -sf "$COREDIR/git-hooks/$hook_name" "$hook" fi done # .gitattribute should be per-repo, avoid entangling repos if [ -L .gitattributes ] ; then rm -f .gitattributes fi popd > /dev/null fi ;; esac } refresh_all_hooks() { repos="core $(cat "$COREDIR/bin/repo-list")" for repo in $repos ; do refresh_hooks $repo done } postprocess() { rc=$1 if $DO_HOOK_REFRESH ; then refresh_all_hooks fi exit $rc; } CLONEDIR="$COREDIR/clone" if [ ! -e ${CLONEDIR} ]; then mkdir -p "$CLONEDIR"; fi # extra params for some commands, like log EXTRA= COMMAND="$1" PAGER= RELATIVIZE=1 PUSH_ALL= PUSH_USER= PUSH_NOTES= LAST_WORKING= SET_LAST_WORKING= ALLOW_EMPTY= KEEP_GOING=0 REPORT_REPOS=1 REPORT_COMMANDS=0 REPORT_COMPACT=0 DO_HOOK_REFRESH=false while [ "${COMMAND:0:1}" = "-" ] ; do case "$COMMAND" in -f) KEEP_GOING=1 ;; -s) REPORT_REPOS=0 ;; -v) REPORT_COMMANDS=1 ;; -1) REPORT_COMPACT=1 ;; --set-push-user) shift PUSH_USER="$1" ;; --last-working) LAST_WORKING=1 ;; --set-last-working) SET_LAST_WORKING=1 ;; --push-notes) PUSH_NOTES=1 ;; -z) DO_HOOK_REFRESH=true postprocess 0 ;; esac shift COMMAND="$1" done case "$COMMAND" in apply) EXTRA="-p0 --stat --apply --index --ignore-space-change --whitespace=error" RELATIVIZE=0 ;; clone|fetch|pull) DO_HOOK_REFRESH=true ;; diff) PAGER='--no-pager' REPORT_REPOS=0 ;; log) if [ "$#" = "1" ] ; then EXTRA='-1' fi PAGER='--no-pager' ;; push) if [ "$#" != "1" ] ; then PUSH_ALL=1 fi ;; esac # absolutize the parameters first unset FILES FILESNUM=0 while shift ; do PARAM="$1" if [ -z "$PARAM" ] ; then continue elif [ "${PARAM:0:1}" = "-" ] ; then if [ \( "$COMMAND" = "checkout" -a "$PARAM" = "-b" \) -o \ \( "$COMMAND" = "clone" -a "$PARAM" = "--reference" \) -o \ \( "$COMMAND" = "commit" -a "$PARAM" = "-m" \) -o \ \( "$COMMAND" = "commit" -a "$PARAM" = "-am" \) -o \ \( "$COMMAND" = "tag" -a "$PARAM" = "-m" \) ] then # params that take an argument FILES[$FILESNUM]="$PARAM" FILESNUM=$(($FILESNUM+1)) shift FILES[$FILESNUM]="$1" FILESNUM=$(($FILESNUM+1)) else if [ "$COMMAND" = "commit" -a "$PARAM" = "-F" ] then shift # this still needs some magic to handle relative paths EXTRA="${EXTRA} -F ${1}" else [ "$COMMAND" = "commit" -a "$PARAM" = "--allow-empty" ] && ALLOW_EMPTY=1 FILES[$FILESNUM]="$PARAM" FILESNUM=$(($FILESNUM+1)) fi fi else if [ "$COMMAND" = "apply" ] ; then grep -qs $'^+ *\t' "$PARAM" && { echo "Patch '$PARAM' introduces tabs in indentation, aborting." echo echo "Please fix the patch (something like s/^\(+ *\)\t/\1 /) and try again." echo exit 1 } fi if [ "$COMMAND" == "rev-parse" ] ; then # this is not a file FILES[$FILESNUM]="$PARAM" FILESNUM=$(($FILESNUM+1)) else # make the paths absolute FILES[$FILESNUM]=$(perl -e 'use Cwd "abs_path"; print abs_path(shift);' "$PARAM") if [ -z "${FILES[$FILESNUM]}" -o ! -e "${FILES[$FILESNUM]}" ] ; then # it is probably not a file, but a tag name, or something FILES[$FILESNUM]="$PARAM" fi FILESNUM=$(($FILESNUM+1)) fi fi done # do it! DIRS="core $(cd $CLONEDIR ; ls)" if [ "$COMMAND" = "clone" ] ; then DIRS=$(cat "$COREDIR/bin/repo-list") fi for REPO in $DIRS ; do DIR="$CLONEDIR/$REPO" NAME="$REPO" if [ "$REPO" = "core" ] ; then DIR="$COREDIR" NAME="main repo" fi if [ -d "$DIR" -a "z$PUSH_USER" != "z" ]; then echo "setting up push url for $DIR" (cd $DIR && git config remote.origin.pushurl "ssh://${PUSH_USER}@git.freedesktop.org/git/libreoffice/${REPO}") elif [ -d "$DIR" -a "z$LAST_WORKING" != "z" ]; then echo "fetching notes for $REPO ..." (cd $DIR && git fetch origin 'refs/notes/*:refs/notes/*') hash=`(cd $DIR && git log --pretty='%H %N' | grep 'win32 working build' | head -n1 | sed 's/ win32.*//')` if test "z$hash" != "z"; then echo "update to $hash" (cd $DIR && git checkout $hash) else echo "Warning: missing known working note on repo $REPO" fi elif [ -d "$DIR" -a "z$SET_LAST_WORKING" != "z" ]; then echo "fetching notes for $REPO ..." (cd $DIR && git fetch origin 'refs/notes/*:refs/notes/*') (cd $DIR && git notes add -m 'win32 working build') elif [ -d "$DIR" -a "z$PUSH_NOTES" != "z" ]; then echo "pushing notes for $REPO ..." (cd $DIR && git push origin 'refs/notes/*:refs/notes/*') elif [ \( -d "$DIR" -a -d "$DIR"/.git \) -o \( "$COMMAND" = "clone" \) ] ; then ( # executed in a subshell if [ "$COMMAND" != "clone" ] ; then cd "$DIR" else cd "$CLONEDIR" fi # relativize the absolutized params again if we want to operate # only on the files belonging to this exact repo if [ "$RELATIVIZE" = "1" -a -n "$FILES" ] ; then FILESNUM=0 INSERTNUM=0 PWD=$(pwd) PWDLEN=$(pwd | wc -c) for I in "${FILES[@]}" ; do I="${I//@REPO@/${REPO}}" unset FILES[$FILESNUM] FILESNUM=$(($FILESNUM+1)) # filter out files that don't belong to this repo if [ \( "${I:0:1}" = "/" \) -a \( "$COMMAND" != "clone" \) ] ; then if [ "${I:0:$PWDLEN}" = "$PWD/" ] ; then FILES[$INSERTNUM]="${I:$PWDLEN}" INSERTNUM=$(($INSERTNUM+1)) fi else FILES[$INSERTNUM]="$I" INSERTNUM=$(($INSERTNUM+1)) fi done [ "$INSERTNUM" = "0" ] && exit 0 fi # some extra params case "$COMMAND" in apply) for I in * ; do if [ -d "$I" ] ; then EXTRA="$EXTRA --include=$I/*" else EXTRA="$EXTRA --include=$I" fi done ;; commit) if [ "$ALLOW_EMPTY" != "1" ] ; then [ -z "$(git diff-index --name-only HEAD --)" ] && exit 0 fi ;; push) if [ "$PUSH_ALL" != "1" ] ; then [ -n "$(git rev-list @{upstream}..HEAD)" ] || exit 0 fi ;; status) LOCALCOMMITS="$(git rev-list @{upstream}..HEAD)" if [ -z "$LOCALCOMMITS" ] ; then [ -z "$(git diff-index --name-only HEAD --)" ] && exit 0 fi ;; clone) EXTRA="$(git config remote.origin.url)" EXTRA=${EXTRA/core/${REPO}} ;; esac # do it! if [ "$COMMAND" != "clone" -o ! -d $DIR ] ; then if [ "$REPORT_REPOS" = "1" -a "$COMMAND" != "grep" ] ; then if [ "$REPORT_COMPACT" = "1" ] ; then echo -n "${REPO}:" else echo "===== $NAME =====" fi fi if [ "$REPORT_COMMANDS" = "1" ] ; then echo "+ git $PAGER $COMMAND $EXTRA ${FILES[@]}" fi git $PAGER "$COMMAND" $EXTRA "${FILES[@]}" RETURN=$? fi # now we can change the dir in case of clone as well if [ "$COMMAND" = "clone" ] ; then cd $DIR fi case "$COMMAND" in pull|clone) # update links if [ "$DIR" != "$COREDIR" ]; then for link in $(ls) ; do if [ ! -e "$COREDIR/$link" ] ; then if test -h "$COREDIR/$link"; then rm "$COREDIR/$link" echo -n "re-" fi echo "creating missing link $link" ln -s "$DIR/$link" "$COREDIR/$link" fi done fi ;; status) # git status returns error in some versions, clear that RETURN=0 ;; grep) # git grep return an 'error' if nothing is found # still we should continue grepping the other repos RETURN=0 ;; esac if [ "$KEEP_GOING" = "1" ] ; then RETURN=0 fi exit $RETURN ) || postprocess $? fi done # Cleanup the broken links if [ "$COMMAND" = "pull" ] ; then for link in $(ls $COREDIR) ; do if [ -h "$COREDIR/$link" -a ! -e "$COREDIR/$link" ]; then echo "Removing broken link $link" rm $COREDIR/$link fi done fi # warn if [ "$COMMAND" = "apply" ] ; then echo echo "Don't forget to check the status & commit now ;-)" echo fi postprocess $? # vi:set shiftwidth=4 expandtab: