4 # for sending emails (-a option)
5 ADMINS="Thierry.Parmentelat@inria.fr baris@metin.org"
6 # the other end of the mirror (-r option)
7 REMOTE_GIT="git://git.planet-lab.org"
13 # local path to the reference (bare) repos
15 # local path to the working repos; can be trashed if needed
16 LOCAL_MIRROR_DIR="/git-mirror"
17 # a file in each repo to avoid too many notifications
18 NOTIFIED_FILE="NOTIFIED"
19 # global stamp to avoid multiple instances of this script
20 LOCKFILE=$LOCAL_MIRROR_DIR/LOCK
21 # global list - errors to report
25 [ -n "$VERBOSE" ] && echo "--------------------" "$@"
29 [ -n "$QUIET" ] || echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "$@"
33 msg "Received signal - cleaning up $LOCKFILE and aborting"
39 local REPO_DIR=$1; shift
40 local SUBJECT=$1; shift
42 verbose "notify" $SUBJECT
44 # already notified within a half day ?
46 is_recent=$(find $REPO_DIR/$NOTIFIED_FILE -mmin -$GRACE 2> /dev/null)
47 # not there or less than 12 our old: don't notify
48 if [ -n "$is_recent" ] ; then
49 verbose "skipping recent notification -- $SUBJECT"
53 for admin in $ADMINS; do
54 echo -e "$BODY" | mail -s "$SUBJECT" $admin
56 touch $REPO_DIR/$NOTIFIED_FILE
59 function clear_notify () {
60 local REPO_DIR=$1; shift
61 rm -f $REPO_DIR/$NOTIFIED_FILE
64 # run directory command
65 # -or- to ignore errors
66 # run -i directory "command"
69 case "$1" in -i) IGNORE=true ; shift ;; esac
70 local REPO_DIR=$1; shift
73 pushd ${REPO_DIR} > /dev/null
74 # quiet mode: record stdout and err for the mail
76 if [ -n "$QUIET" ] ; then
77 OUTPUT=$($COMMAND 2>&1) || ok=
78 TORECORD="[$REPO_DIR] $COMMAND\n$OUTPUT"
80 if [ -z "$IGNORE" ] ; then
81 echo "[$REPO_DIR] Running $COMMAND"
85 echo "[$REPO_DIR] Running $COMMAND [ignored]"
92 # let's record the failure unless ignore is set
93 if [ -z "$IGNORE" ] ; then
94 FAILED_CMDS=("${FAILED_CMDS[@]}" "$TORECORD")
97 verbose "after run with $COMMAND" "ok=$ok" "#failed=${#FAILED_CMDS[@]}"
101 function merge_all_branches () {
102 local REPO_DIR=$1; shift
104 local REMOTE=$1; shift
106 pushd $REPO_DIR > /dev/null
107 BRANCHES=$(git branch -r | grep $REMOTE | grep -v HEAD | sed "s/.*\///g" | grep -v master)
108 HAS_MASTER=$(git branch -l | grep master)
111 [ -n "$HAS_MASTER" ] && run ${REPO_DIR} git checkout master
112 [ -n "$HAS_MASTER" ] && run ${REPO_DIR} git merge --ff $REMOTE/master
113 for BRANCH in $BRANCHES ; do
114 run -i ${REPO_DIR} git branch $BRANCH $REMOTE/$BRANCH
115 run ${REPO_DIR} git checkout $BRANCH
116 run ${REPO_DIR} git merge --ff $REMOTE/$BRANCH
120 function push_all_branches () {
121 local REPO_DIR=$1; shift
123 local PUSH_TO=$1; shift
124 local PUSH_FROM=$1; shift
126 pushd $REPO_DIR > /dev/null
127 BRANCHES=$(git branch -r | grep $PUSH_FROM | grep -v HEAD | sed "s/.*\///g" | grep -v master)
128 HAS_MASTER=$(git branch -l | grep master)
131 [ -n "$HAS_MASTER" ] && run ${REPO_DIR} git push $PUSH_TO master:master
132 for BRANCH in $BRANCHES ; do
133 run ${REPO_DIR} git push $PUSH_TO $BRANCH:$BRANCH
137 function mirror_repo () {
139 FAILED_CMDS=() # reset previous failure if any
141 NAME=$(basename ${repo} ".git")
143 REPO_DIR=${LOCAL_MIRROR_DIR}/${NAME}
144 REMOTE_REPO=${REMOTE_GIT}/${GIT_NAME}
145 MASTER_REPO=${MASTER_GIT}/${GIT_NAME}
147 # if there is no remote repository it may be that we only have
148 # the repository locally and don't need to mirror
149 git ls-remote $REMOTE_REPO &> /dev/null || return
151 if [ -d ${REPO_DIR} ] ; then
152 msg "pulling from ${NAME}"
153 run ${REPO_DIR} git fetch origin --tags
154 run ${REPO_DIR} git fetch origin
155 merge_all_branches $REPO_DIR $NAME origin
156 if [ -n "$FAILED_CMDS" ]; then
158 body="Can not fetch from ${MASTER_REPO}\n\n------------\n FAILED COMMANDS:\n"
159 for line in "${FAILED_CMDS[@]}"; do body="$body$line\n"; done
160 notify $REPO_DIR "git-mirror.sh failed to merge remote with module ${NAME}" "$body"
164 msg "mirroring ${NAME} for the first time"
165 run ${LOCAL_MIRROR_DIR} git clone ${REMOTE_REPO}
166 run ${REPO_DIR} git remote add local_master ${MASTER_REPO}
169 msg "pushing ${NAME} to local master"
170 run ${REPO_DIR} git fetch local_master --tags
171 run ${REPO_DIR} git fetch local_master
172 merge_all_branches $REPO_DIR $NAME local_master
173 if [ -n "$FAILED_CMDS" ]; then
174 pushd ${REPO_DIR} > /dev/null
175 STATUS_OUT=$(git status)
178 body="STATUS in ${REPO_DIR}:\n${STATUS_OUT} \n\n------------\n FAILED COMMANDS:\n"
179 for line in "${FAILED_CMDS[@]}"; do body="$body$line\n"; done
180 notify $REPO_DIR "git-mirror.sh failed on with module ${NAME}" "$body"
183 run ${REPO_DIR} git push --tags local_master
184 push_all_branches $REPO_DIR $NAME local_master origin
185 if [ -n "$FAILED_CMDS" ]; then
187 body="FAILED COMMANDS:\n"
188 for line in "${FAILED_CMDS[@]}"; do body="$body$line\n"; done
189 notify $REPO_DIR "git-mirror.sh failed to push back with module ${NAME}" "$body"
192 # success, remove previous check file if any
193 clear_notify $REPO_DIR
197 echo "Usage $COMMAND [options] REPONAME*"
198 echo " [-a admin-mails] : provide space-separated admins emails"
199 echo " [-r remote-git-url] : e.g. -r git://git.onelab.eu/"
200 echo " [-q] quiet mode for running under cron"
201 echo " [-v] verbose mode"
202 echo " [-f] force mode, runs even if the lock file is present"
203 echo " see also manage-git-mirror.sh"
207 while getopts "a:r:qvfh" opt; do
210 r) REMOTE_GIT=$OPTARG ;;
215 \?) echo "Invalid option: -$opt" >&2 ;;
220 # skip this if force is set
221 # the natural usage of force is, manage-git-mirror.sh touches the lock file
222 # which prevents the cron jobs from running during an hour
223 # then you can use with -f which leaves the lock file as it is
224 if [ -z "$FORCE" ] ; then
225 # is the stamp older than an hour ?
227 is_old=$(find $LOCKFILE -mmin +$GRACE 2> /dev/null)
228 if [ -n "$is_old" ] ; then
229 msg "$LOCKFILE is older than $GRACE minutes - removing"
233 if [ -f $LOCKFILE ] ; then
234 msg "Found $LOCKFILE - another git-mirror seems to be running. Aborting... "
241 # if force is set we leave the lock file as is
242 [ -z "$FORCE" ] && date > $LOCKFILE
243 for gitrepo in "$@"; do mirror_repo $gitrepo ; done
244 [ -z "$FORCE" ] && rm -f $LOCKFILE