git-mirror lock
[infrastructure.git] / scripts / git-mirror.sh
1 #!/bin/bash
2
3 ADMINS="Talip-Baris.Metin@inria.fr Thierry.Parmentelat@inria.fr"
4 MIRROR_GIT="git://git.planet-lab.org"
5 MASTER_GIT="/git"
6 LOCAL_MIRROR_DIR="/git-mirror"
7 QUIET=0
8 FAILED=0
9 FAILED_CMDS=""
10 NOTIFIED_FILE="NOTIFIED_ADMINS"
11 RUNNING_FILE=$LOCAL_MIRROR_DIR/RUNNING_MIRROR
12
13 function msg () {
14     if [ $QUIET -eq 0 ]
15     then
16         echo "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx $@"
17     fi
18 }
19
20 function error () {
21     SUBJECT=$1
22     MSG=$2
23     CHECK_FILE=$3/$NOTIFIED_FILE
24
25     if [ -f $CHECK_FILE ]
26     then
27         return
28     fi
29
30     for admin in $ADMINS
31     do
32         echo -e "$MSG" | mail -s "$SUBJECT" $admin
33     done
34     touch $CHECK_FILE
35 }
36
37 function run () {
38     if [ $QUIET -eq 1 ]
39     then
40         COMMAND="$1 &> /dev/null"
41     else
42         COMMAND="$1"
43         msg $COMMAND
44     fi
45     REPO=$2
46
47     pushd ${REPO} > /dev/null
48     eval $COMMAND
49     if [ $? -ne 0 ]
50     then
51         FAILED=1
52         FAILED_CMDS="$FAILED_CMDS\n$COMMAND" 
53     fi
54     popd > /dev/null
55 }
56
57 function run_ignore_errors () {
58     OLD_FAILED=$FAILED
59     OLD_FAILED_CMDS=$FAILED_CMDS
60     run "$1" "$2"
61     FAILED=$OLD_FAILED
62     FAILED_CMDS=$OLD_FAILED_CMDS
63 }
64
65 function merge_all_branches () {
66     NAME=$1
67     REMOTE=$2
68     REPO_DIR=$3
69
70     pushd $REPO_DIR > /dev/null
71     BRANCHES=$(git branch -r | grep $REMOTE | grep -v HEAD | sed "s/.*\///g" | grep -v master)
72     popd > /dev/null
73
74     run "git checkout master" ${REPO_DIR}
75     run "git merge --ff $REMOTE/master" ${REPO_DIR}
76     for BRANCH in $BRANCHES ; do
77         run_ignore_errors "git branch $BRANCH $REMOTE/$BRANCH" ${REPO_DIR}
78         run "git checkout $BRANCH" ${REPO_DIR}
79         run "git merge --ff $REMOTE/$BRANCH" ${REPO_DIR}
80     done
81 }
82
83 function push_all_branches () {
84     NAME=$1
85     REMOTE=$2
86     REPO_DIR=$3
87
88     pushd $REPO_DIR > /dev/null
89     BRANCHES=$(git branch -r | grep $REMOTE | grep -v HEAD | sed "s/.*\///g" | grep -v master)
90     popd > /dev/null
91
92     run "git push $REMOTE master:master" ${REPO_DIR}
93     for BRANCH in $BRANCHES ; do
94         run "git push $REMOTE $BRANCH:$BRANCH" ${REPO_DIR}
95     done
96 }
97
98 function mirror () {
99     for arg in "$@" ; do
100         FAILED=0   # reset previous failure if any
101
102         NAME=$(basename ${arg} | sed s/.git$//g)
103         GIT_NAME=${NAME}.git
104         REPO_DIR=${LOCAL_MIRROR_DIR}/${NAME}
105         MIRROR_REPO=${MIRROR_GIT}/${GIT_NAME}
106         MASTER_REPO=${MASTER_GIT}/${GIT_NAME}
107
108
109         # if there is no remote repository it may be that we only have
110         # the repository locally and don't need to mirror
111         git ls-remote $MIRROR_REPO &> /dev/null
112         if [ $? -eq 0 ]
113         then
114             if [ -d ${REPO_DIR} ]
115             then
116                 msg "pulling from ${NAME}"
117                 run "git fetch origin --tags" ${REPO_DIR}
118                 run "git fetch origin" ${REPO_DIR}
119                 merge_all_branches $NAME origin $REPO_DIR
120                 if [ $? -ne 0 ]
121                 then
122                     error "git-mirror.sh failed" "Can not fetch from ${MASTER_REPO}" $REPO_DIR
123                 fi
124             else
125                 msg "mirroring ${NAME} for the first time"
126                 run "git clone ${MIRROR_REPO}" ${LOCAL_MIRROR_DIR}
127                 run "git remote add local_master ${MASTER_REPO}" ${REPO_DIR}
128             fi
129
130             msg "pushing ${NAME} to local master"
131             run "git fetch local_master --tags" ${REPO_DIR}
132             run "git fetch local_master" ${REPO_DIR}
133             merge_all_branches $NAME local_master $REPO_DIR
134             if [ $FAILED -ne 0 ]
135             then
136                 pushd ${REPO_DIR} > /dev/null
137                 STATUS_OUT=$(git status)
138                 popd > /dev/null                
139                 error "git-mirror.sh failed on ${MIRROR_REPO}" "STATUS:\n$STATUS_OUT \n\n------------\n FAILED COMMANDS:\n$FAILED_CMDS" $REPO_DIR
140             else
141                 run "git push --tags local_master" ${REPO_DIR}
142                 push_all_branches $NAME local_master $REPO_DIR
143
144                 # success, remove previous check file if any
145                 CHECK_FILE=$REPO_DIR/$NOTIFIED_FILE
146                 rm -f $CHECK_FILE
147             fi
148         fi
149     done
150 }
151
152
153 while getopts ":hq" opt
154 do
155   case $opt in
156       q)
157           QUIET=1
158           break
159           ;;
160       h)
161           echo "USAGE: $0 [-q] REPONAME*"
162           exit 1
163           ;;
164       \?)
165           echo "Invalid option: -$OPTARG" >&2
166           ;;
167   esac
168 done
169
170
171 if [ -f $RUNNING_FILE ]
172 then
173     if [ $QUIET -eq 0 ]
174     then
175         echo "Another git-mirror is running. Aborting... " $RUNNING_FILE
176     fi
177 else
178     shift $((OPTIND-1))
179     touch $RUNNING_FILE
180     mirror $@
181     rm -f $RUNNING_FILE
182 fi
183
184