toolbox: anchor _repo pattern to start of line
[girocco.git] / taskd / clone.sh
blob05fc767e28a274242af3fbbb6c91f99944c584a8
1 #!/bin/sh
3 # Invoked from taskd/taskd.pl
5 . @basedir@/shlib.sh
7 # darcs fast-export | git fast-import with error handling
8 git_darcs_fetch() (
9 set_utf8_locale
10 _err1=
11 _err2=
12 exec 3>&1
13 { read -r _err1 || :; read -r _err2 || :; } <<-EOT
15 exec 4>&3 3>&1 1>&4 4>&-
17 _e1=0
18 "$cfg_basedir"/bin/darcs-fast-export \
19 --export-marks="$(pwd)/dfe-marks" "$1" 3>&- || _e1=$?
20 echo $_e1 >&3
21 } | \
23 _e2=0
24 git fast-import \
25 --export-marks="$(pwd)/gfi-marks" \
26 --export-pack-edges="$(pwd)/gfi-packs" \
27 --force 3>&- || _e2=$?
28 echo $_e2 >&3
31 EOT
32 exec 3>&-
33 [ "$_err1" = 0 -a "$_err2" = 0 ]
34 return $?
37 # bzr fast-export | git fast-import with error handling
38 git_bzr_fetch() (
39 set_utf8_locale
40 _err1=
41 _err2=
42 exec 3>&1
43 { read -r _err1 || :; read -r _err2 || :; } <<-EOT
45 exec 4>&3 3>&1 1>&4 4>&-
47 _e1=0
48 bzr fast-export \
49 --export-marks="$(pwd)/bfe-marks" "$1" 3>&- || _e1=$?
50 echo $_e1 >&3
51 } | \
53 _e2=0
54 git fast-import \
55 --export-marks="$(pwd)/gfi-marks" \
56 --export-pack-edges="$(pwd)/gfi-packs" \
57 --force 3>&- || _e2=$?
58 echo $_e2 >&3
61 EOT
62 exec 3>&-
63 [ "$_err1" = 0 -a "$_err2" = 0 ]
64 return $?
67 send_clone_failed() {
68 trap "" EXIT
69 # We must now close the .clonelog file that is open on stdout and stderr
70 exec >/dev/null 2>&1
71 failaddrs="$(config_get owner || :)"
72 [ -z "$cfg_admincc" -o "$cfg_admincc" = "0" -o -z "$cfg_admin" ] || \
73 if [ -z "$failaddrs" ]; then failaddrs="$cfg_admin"; else failaddrs="$failaddrs,$cfg_admin"; fi
74 [ -z "$failaddrs" ] || \
76 cat <<EOT
77 Condolences. The clone of project $proj just failed.
79 * Source URL: $url
80 * Project settings: $cfg_webadmurl/editproj.cgi?name=$(echo "$proj" | sed -e 's/[+]/%2B/g')
82 The project settings link may be used to adjust the settings
83 and restart the clone in order to try the clone again.
84 EOT
85 if [ -f .clonelog -a -r .clonelog ]; then
86 echo ""
87 echo "Log follows:"
88 echo ""
89 loglines=$(wc -l <.clonelog)
90 if [ $loglines -le 203 ]; then
91 cat .clonelog
92 else
93 head -n 100 .clonelog
94 echo ""
95 echo "[ ... elided $(( $loglines - 200 )) middle lines ... ]"
96 echo ""
97 tail -n 100 .clonelog
100 } | mail -s "[$cfg_name] $proj clone failed" "$failaddrs" || :
103 set -e
105 umask 002
106 [ "$cfg_permission_control" != "Hooks" ] || umask 000
108 projdir="$1"
109 proj="${projdir%.git}"
111 cd "$cfg_reporoot/$projdir"
112 trap "echo '@OVER@'; touch .clone_failed; send_clone_failed" EXIT
113 [ -n "$cfg_mirror" ] || { echo "Mirroring is disabled" >&2; exit 1; }
114 url="$(config_get baseurl || :)"
115 case "$url" in *" "*|*" "*|"")
116 echo "Bad mirror URL (\"$url\")"
117 exit 1
118 esac
119 echo "Mirroring from URL \"$url\""
120 echo ""
122 if [ "$cfg_project_owners" = "source" ]; then
123 config_set owner "$(stat -c %U "$url" 2>/dev/null)"
126 mailaddrs="$(config_get owner || :)"
127 [ -z "$cfg_admin" ] || \
128 if [ -z "$mailaddrs" ]; then mailaddrs="$cfg_admin"; else mailaddrs="$mailaddrs,$cfg_admin"; fi
130 # Initial mirror
131 echo "Initiating mirroring..."
132 headref=
133 showheadwarn=
134 warnempty=
135 case "$url" in
136 svn://* | svn+http://* | svn+https://*)
137 [ -n "$cfg_mirror_svn" ] || { echo "Mirroring svn is disabled" >&2; exit 1; }
138 # We just remove svn+ here, so svn+http://... becomes http://...
139 # We also remove a trailing '/' to match what git-svn will do
140 svnurl="${url#svn+}"
141 svnurl="${svnurl%/}"
142 # We require svn info to succeed on the URL otherwise it's
143 # simply not a valid URL and without using -s on the init it
144 # will not otherwise be tested until the fetch
145 svn --non-interactive info "$svnurl" > /dev/null
146 # We initially use -s for the init which will possibly shorten
147 # the URL. However, the shortening can fail if a password is
148 # not required for the longer version but is for the shorter,
149 # so try again without -s if the -s version fails.
150 # We must use GIT_DIR=. here or ever so "helpful" git-svn will
151 # create a .git subdirectory!
152 GIT_DIR=. git svn init --prefix "" -s "$svnurl" < /dev/null || \
153 GIT_DIR=. git svn init --prefix "" "$svnurl" < /dev/null
154 # We need to remember this url so we can detect changes because
155 # ever so "helpful" git-svn may shorten it!
156 config_set svnurl "$svnurl"
157 # At this point, since we asked for a standard layout (-s) git-svn
158 # may have been "helpful" and adjusted our $svnurl to a prefix and
159 # then glued the removed suffix onto the front of any svn-remote.svn.*
160 # config items. We could avoid this by not using the '-s' option
161 # but then we might not get all the history. If, for example, we
162 # are cloning an http://svn.example.com/repos/public repository that
163 # early in its history moved trunk => public/trunk we would miss that
164 # earlier history without allowing the funky shorten+prefix behavior.
165 # So we read back the svn-remote.svn.fetch configuration and compute
166 # the prefix. This way we are sure to get the correct prefix.
167 gitsvnurl="$(git config --get svn-remote.svn.url || :)"
168 gitsvnfetch="$(git config --get-all svn-remote.svn.fetch | tail -1 || :)"
169 gitsvnprefix="${gitsvnfetch%%:*}"
170 gitsvnsuffix="${gitsvnprefix##*/}"
171 gitsvnprefix="${gitsvnprefix%$gitsvnsuffix}"
172 # Ask git-svn to store everything in the normal non-remote
173 # locations being careful to use the correct prefix
174 git config --replace-all svn-remote.svn.fetch "${gitsvnprefix}trunk:refs/heads/master"
175 git config --replace-all svn-remote.svn.branches "${gitsvnprefix}branches/*:refs/heads/*"
176 git config --replace-all svn-remote.svn.tags "${gitsvnprefix}tags/*:refs/tags/*"
177 # look for additional non-standard directories to fetch
178 # check for standard layout at the same time
179 foundstd=
180 foundfile=
181 { svn --non-interactive ls "$gitsvnurl/${gitsvnprefix}" 2>/dev/null || :; } | \
182 { while read file; do case $file in
183 # skip the already-handled standard ones and any with a space or tab
184 *' '*|*' '*) :;;
185 trunk/|branches/|tags/) foundstd=1;;
186 # only fetch extra directories from the $svnurl root (not any files)
187 *?/) git config --add svn-remote.svn.fetch \
188 "${gitsvnprefix}${file%/}:refs/heads/${file%/}";;
189 *?) foundfile=1;;
190 esac; done
191 # if files found and no standard directories present use a simpler layout
192 if [ -z "$foundstd" ] && [ -n "$foundfile" ]; then
193 git config --unset svn-remote.svn.branches
194 git config --unset svn-remote.svn.tags
195 git config --replace-all svn-remote.svn.fetch ':refs/heads/master'
196 fi; }
197 # Again, be careful to use GIT_DIR=. here or else new .git subdirectory!
198 GIT_DIR=. git svn fetch --quiet < /dev/null
199 # git svn does not preserve group permissions in the svn subdirectory
200 chmod -R ug+rw,o+r svn
201 # git svn also leaves behind ref turds that end with @nnn
202 # We get rid of them now
203 git show-ref | \
204 { while read sha1 ref; do
205 case "$ref" in
206 ?*@[1-9]|?*@[1-9][0-9]|?*@[1-9][0-9][0-9]|?*@[1-9][0-9][0-9][0-9]|\
207 ?*@[1-9][0-9][0-9][0-9][0-9]|?*@[1-9][0-9][0-9][0-9][0-9][0-9]|\
208 ?*@[1-9][0-9][0-9][0-9][0-9][0-9][0-9]|\
209 ?*@[1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9])
210 git update-ref -d "$ref"
211 esac
212 done; }
214 darcs://*)
215 [ -n "$cfg_mirror_darcs" ] || { echo "Mirroring darcs is disabled" >&2; exit 1; }
216 httpurl="http://${url#darcs://}"
217 # Remove any left-over .darcs dirs from a previous failed attempt
218 rm -rf *.darcs
219 # Remove any left-over export files from a previous failed attempt
220 rm -f dfe-marks gfi-marks gfi-packs
221 git_darcs_fetch "$httpurl"
223 bzr://*)
224 [ -n "$cfg_mirror_bzr" ] || { echo "Mirroring bzr is disabled" >&2; exit 1; }
225 # we just remove bzr:// here, a typical bzr url is just
226 # "lp:foo"
227 bzrurl="${url#bzr://}"
228 # Remove any left-over export files from a previous failed attempt
229 rm -f bfe-marks gfi-marks gfi-packs
230 git_bzr_fetch "$bzrurl"
232 hg+http://* | hg+https://*)
233 [ -n "$cfg_mirror_hg" ] || { echo "Mirroring hg is disabled" >&2; exit 1; }
234 # We just remove hg+ here, so hg+http://... becomes http://...
235 hgurl="${url#hg+}"
236 # Remove any left-over repo.hg dir from a previous failed attempt
237 rm -rf repo.hg
238 # Remove any left-over export files from a previous failed attempt
239 rm -f gfi-packs hg2git-heads hg2git-mapping hg2git-marks* hg2git-state
240 # Perform the initial hg clone
241 hg clone -U "$hgurl" "$(pwd)/repo.hg"
242 # Do the fast-export | fast-import
243 git_hg_fetch
246 git remote rm origin >/dev/null 2>&1 || :
247 # starting with Git 1.7.5 --mirror by itself will spew a warning
248 # since we support older Git versions, just toss the warning
249 git remote add --mirror origin "$url" 2>/dev/null
250 # Set the correct HEAD symref by using ls-remote first
251 GIT_SSL_NO_VERIFY=1 GIT_TRACE_PACKET=1 git ls-remote origin >.refs-temp 2>.pkts-temp || \
253 # Since everything was redirected, on failure there'd be no output,
254 # so let's make some failure output
255 cat .pkts-temp
256 echo ""
257 echo "git ls-remote \"$url\" failed"
258 exit 1
260 # Compensate for git() {} side effects
261 unset GIT_TRACE_PACKET
262 # If the server is running at least Git 1.8.4.3 then it will send us the actual
263 # symref for HEAD. If we are running at least Git 1.7.5 then we can snarf that
264 # out of the packet trace data.
265 if [ -s .refs-temp ]; then
266 # Nothing to do unless the remote repository has at least 1 ref
267 # See if we got a HEAD ref
268 head="$(grep -E "^$octet20[[:space:]]+HEAD\$" <.refs-temp | awk '{print $1}')"
269 # If the remote has HEAD set to a symbolic ref that does not exist
270 # then we will not receive a HEAD ref in the ls-remote output
271 headref=
272 showheadwarn=
273 symrefcap=
274 if [ -n "$head" ]; then
275 symrefcap="$(sed -ne <.pkts-temp \
276 '/packet:.*git<.*[[:space:]]symref=HEAD:refs\/heads\/[^[:space:]][^[:space:]]*/'"\
277 "'{s/^.*[[:space:]]symref=HEAD:\(refs\/heads\/[^[:space:]][^[:space:]]*\).*$/\1/;p;}')"
278 # prefer $symrefcap (refs/heads/master if no $symrefcap) if it
279 # matches HEAD otherwise take the first refs/heads/... match
280 matchcnt=0
281 while read ref; do
282 [ -n "$ref" ] || continue
283 matchcnt=$(( $matchcnt + 1 ))
284 if [ -z "$headref" ] || [ "$ref" = "${symrefcap:-refs/heads/master}" ]; then
285 headref="$ref"
287 [ "$headref" = "${symrefcap:-refs/heads/master}" -a $matchcnt -gt 1 ] && break
288 done <<-EOT
289 $(grep -E "^$head[[:space:]]+refs/heads/[^[:space:]]+\$" <.refs-temp | \
290 awk '{print $2}')
292 # Warn if there was more than one match and $symrefcap is empty
293 # or $symrefcap is not the same as $headref since our choice might
294 # differ from the source repository's HEAD
295 if [ $matchcnt -ge 1 -a "$symrefcap" != "$headref" ] && \
296 [ -n "$symrefcap" -o $matchcnt -gt 1 ]; then
297 showheadwarn=1
300 if [ -z "$headref" ]; then
301 # If we still don't have a HEAD ref then prefer refs/heads/master
302 # if it exists otherwise take the first refs/heads/...
303 # We do not support having a detached HEAD.
304 # We always warn now because we will be setting HEAD differently
305 # than the source repository had HEAD set
306 showheadwarn=1
307 while read ref; do
308 [ -n "$ref" ] || continue
309 if [ -z "$headref" ] || [ "$ref" = "refs/heads/master" ]; then
310 headref="$ref"
312 [ "$headref" = "refs/heads/master" ] && break
313 done <<-EOT
314 $(grep -E "^$octet20[[:space:]]+refs/heads/[^[:space:]]+\$" <.refs-temp | \
315 awk '{print $2}')
318 # If we STILL do not have a HEAD ref (perhaps the source repository
319 # contains only tags) then use refs/heads/master. It will be invalid
320 # but is no worse than we used to do by default and we'll warn about
321 # it. We do not support a HEAD symref to anything other than refs/heads/...
322 [ -n "$headref" ] || headref="refs/heads/master"
323 git symbolic-ref HEAD "$headref"
324 GIT_SSL_NO_VERIFY=1 git remote update
325 GIT_SSL_NO_VERIFY=1 git remote prune origin
326 else
327 warnempty=1
328 git symbolic-ref HEAD "refs/heads/master"
330 rm -f .refs-temp .pkts-temp
332 esac
334 # The objects subdirectories permissions must be updated now.
335 # In the case of a dumb http clone, the permissions will not be correct
336 # (missing group write) despite the core.sharedrepository=1 setting!
337 # The objects themselves seem to have the correct permissions.
338 # This problem appears to have been fixed in the most recent git versions.
339 perms=g+w
340 [ "$cfg_permission_control" != "Hooks" ] || perms=go+w
341 chmod $perms $(find objects -maxdepth 1 -type d) 2>/dev/null || :
343 # Initialize gitweb.lastreceive, gitweb.lastchange and info/lastactivity
344 git config gitweb.lastreceive "$(date '+%a, %d %b %Y %T %z')"
345 git config gitweb.lastchange "$(date '+%a, %d %b %Y %T %z')"
346 git for-each-ref --sort=-committerdate --format='%(committerdate:iso8601)' \
347 --count=1 refs/heads > info/lastactivity
348 [ -s info/lastactivity ] || rm -f info/lastactivity
350 # The rest
351 echo "Final touches..."
352 git update-server-info
353 trap "" EXIT
355 emptynote=
356 [ -z "$warnempty" ] ||
357 emptynote="
358 WARNING: You have mirrored an empty repository.
360 headnote=
361 [ -n "$showheadwarn" -a -n "$headref" ] &&
362 headnote="
363 NOTE: HEAD has been set to a symbolic ref to \"$headref\".
364 Use the \"Project settings\" link to choose a different HEAD symref.
366 sizenote=
367 ! is_gfi_mirror ||
368 sizenote="
369 NOTE: Since this is a mirror of a non-Git source, the initial repository
370 size may be somewhat larger than necessary. This will be corrected
371 shortly. If you intend to clone this repository you may want to
372 wait up to 1 hour before doing so in order to receive the more
373 compact final size.
375 [ -z "$mailaddrs" ] ||
376 mail -s "[$cfg_name] $proj clone completed" "$mailaddrs" <<EOT || :
377 Congratulations! The clone of project $proj just completed.
379 * Source URL: $url
380 * GitWeb interface: $cfg_gitweburl/$projdir
381 * Project settings: $cfg_webadmurl/editproj.cgi?name=$(echo "$proj" | sed -e 's/[+]/%2B/g')
382 $emptynote$headnote$sizenote
383 Have a lot of fun.
386 echo "Mirroring finished successfuly!"
387 # In case this is a re-mirror, lastgc could have been set already so clear it now
388 git config --unset gitweb.lastgc || :
389 rm .clone_in_progress
390 echo "$sizenote@OVER@"