#!/bin/bash
+ADMINS="Talip-Baris.Metin@inria.fr Thierry.Parmentelat@inria.fr"
MIRROR_GIT="git://git.planet-lab.org"
MASTER_GIT="/git"
LOCAL_MIRROR_DIR="/git-mirror"
QUIET=0
+FAILED=0
+FAILED_CMDS=""
+NOTIFIED_FILE="NOTIFIED_ADMINS"
+RUNNING_FILE=$LOCAL_MIRROR_DIR/RUNNING_MIRROR
function msg () {
- if [ $QUIET -eq 0 ]
- then
- echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx $1"
+ if [ $QUIET -eq 0 ] ; then
+ echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "$@"
fi
}
function error () {
- echo "[ERROR] xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx $1"
- exit 1
+ SUBJECT=$1
+ MSG=$2
+ CHECK_FILE=$3/$NOTIFIED_FILE
+
+ if [ -f $CHECK_FILE ]; then
+ return
+ fi
+
+ for admin in $ADMINS; do
+ echo -e "$MSG" | mail -s "$SUBJECT" $admin
+ done
+ touch $CHECK_FILE
}
function run () {
- if [ $QUIET -eq 1 ]
- then
+ if [ $QUIET -eq 1 ]; then
COMMAND="$1 &> /dev/null"
else
COMMAND="$1"
+ msg $COMMAND
fi
REPO=$2
pushd ${REPO} > /dev/null
eval $COMMAND
+ if [ $? -ne 0 ]; then
+ FAILED=1
+ FAILED_CMDS="$FAILED_CMDS\n$COMMAND"
+ fi
+ popd > /dev/null
+}
+
+function run_ignore_errors () {
+ OLD_FAILED=$FAILED
+ OLD_FAILED_CMDS=$FAILED_CMDS
+ run "$1" "$2"
+ FAILED=$OLD_FAILED
+ FAILED_CMDS=$OLD_FAILED_CMDS
+}
+
+function merge_all_branches () {
+ NAME=$1
+ REMOTE=$2
+ REPO_DIR=$3
+
+ pushd $REPO_DIR > /dev/null
+ BRANCHES=$(git branch -r | grep $REMOTE | grep -v HEAD | sed "s/.*\///g" | grep -v master)
popd > /dev/null
+
+ run "git checkout master" ${REPO_DIR}
+ run "git merge --ff $REMOTE/master" ${REPO_DIR}
+ for BRANCH in $BRANCHES ; do
+ run_ignore_errors "git branch $BRANCH $REMOTE/$BRANCH" ${REPO_DIR}
+ run "git checkout $BRANCH" ${REPO_DIR}
+ run "git merge --ff $REMOTE/$BRANCH" ${REPO_DIR}
+ done
+}
+
+function push_all_branches () {
+ NAME=$1
+ REMOTE=$2
+ REPO_DIR=$3
+
+ pushd $REPO_DIR > /dev/null
+ BRANCHES=$(git branch -r | grep $REMOTE | grep -v HEAD | sed "s/.*\///g" | grep -v master)
+ popd > /dev/null
+
+ run "git push $REMOTE master:master" ${REPO_DIR}
+ for BRANCH in $BRANCHES ; do
+ run "git push $REMOTE $BRANCH:$BRANCH" ${REPO_DIR}
+ done
}
function mirror () {
for arg in "$@" ; do
- NAME=$(echo ${arg} | sed 's/\/$//')
+ FAILED=0 # reset previous failure if any
+
+ NAME=$(basename ${arg} | sed s/.git$//g)
GIT_NAME=${NAME}.git
REPO_DIR=${LOCAL_MIRROR_DIR}/${NAME}
MIRROR_REPO=${MIRROR_GIT}/${GIT_NAME}
MASTER_REPO=${MASTER_GIT}/${GIT_NAME}
- if [ -d ${REPO_DIR} ]
- then
- msg "pulling from ${REPO_NAME}"
- run "git fetch origin --tags" ${REPO_DIR}
- run "git fetch origin" ${REPO_DIR}
- run "git merge --ff origin/master" ${REPO_DIR}
- if [ $? -ne 0 ]
- then
- error "Can not fetch from ${MASTER_REPO}"
+
+ # if there is no remote repository it may be that we only have
+ # the repository locally and don't need to mirror
+ git ls-remote $MIRROR_REPO &> /dev/null
+ if [ $? -eq 0 ]; then
+ if [ -d ${REPO_DIR} ]; then
+ msg "pulling from ${NAME}"
+ run "git fetch origin --tags" ${REPO_DIR}
+ run "git fetch origin" ${REPO_DIR}
+ merge_all_branches $NAME origin $REPO_DIR
+ if [ $? -ne 0 ]; then
+ error "git-mirror.sh failed" "Can not fetch from ${MASTER_REPO}" $REPO_DIR
+ fi
+ else
+ msg "mirroring ${NAME} for the first time"
+ run "git clone ${MIRROR_REPO}" ${LOCAL_MIRROR_DIR}
+ run "git remote add local_master ${MASTER_REPO}" ${REPO_DIR}
fi
- else
- msg "mirroring in ${REPO_NAME} for the first time"
- run "git clone ${MIRROR_REPO}" ${LOCAL_MIRROR_DIR}
- run "git remote add local_master ${MASTER_REPO}" ${REPO_DIR}
- fi
- msg "pushing ${REPO_NAME} to local master"
- run "git fetch local_master --tags" ${REPO_DIR}
- run "git fetch local_master" ${REPO_DIR}
- run "git merge --ff local_master/master" ${REPO_DIR}
- if [ $? -ne 0 ]
- then
- error "Can not fetch from ${MIRROR_REPO}"
- fi
+ msg "pushing ${NAME} to local master"
+ run "git fetch local_master --tags" ${REPO_DIR}
+ run "git fetch local_master" ${REPO_DIR}
+ merge_all_branches $NAME local_master $REPO_DIR
+ if [ $FAILED -ne 0 ]; then
+ pushd ${REPO_DIR} > /dev/null
+ STATUS_OUT=$(git status)
+ popd > /dev/null
+ error "git-mirror.sh failed on ${MIRROR_REPO}" "STATUS:\n$STATUS_OUT \n\n------------\n FAILED COMMANDS:\n$FAILED_CMDS" $REPO_DIR
+ else
+ run "git push --tags local_master" ${REPO_DIR}
+ push_all_branches $NAME local_master $REPO_DIR
- run "git push local_master" ${REPO_DIR}
- run "git push --tags local_master" ${REPO_DIR}
+ # success, remove previous check file if any
+ CHECK_FILE=$REPO_DIR/$NOTIFIED_FILE
+ rm -f $CHECK_FILE
+ fi
+ fi
done
}
+function failure () {
+ msg "Received signal - cleaning up $RUNNING_FILE and aborting"
+ rm -f $RUNNING_FILE
+ exit 1
+}
+
while getopts ":hq" opt
do
case $opt in
break
;;
h)
- echo "USAGE: $0 [-q] REPONAME*"
+ echo "Usage: $0 [-q] REPONAME*"
exit 1
;;
\?)
esac
done
+
+# is the stamp older than an hour ?
+# in minutes
+GRACE=60
+is_old=$(find $RUNNING_FILE -mmin +$GRACE 2> /dev/null)
+if [ -n "$is_old" ] ; then
+ msg "$RUNNING_FILE is older than $GRACE minutes - removing"
+ rm -f $RUNNING_FILE
+fi
+
+if [ -f $RUNNING_FILE ] ; then
+ msg "Found $RUNNING_FILE - another git-mirror seems to be running. Aborting... "
+ exit 1
+fi
+
+trap failure ERR INT
+
shift $((OPTIND-1))
+date > $RUNNING_FILE
mirror $@
-
+rm -f $RUNNING_FILE