From 877b57e868376e292a7e912966096e89a29b93de Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Mon, 15 Sep 2014 19:51:07 -0700 Subject: [PATCH] clone.sh: properly set HEAD on initial clone Rather than leaving HEAD set to a symbolic ref to refs/heads/master, attempt to find a refs/heads/... ref that points to the same value as HEAD and then set HEAD to be a symbolic ref to that ref instead. If there's more than one match, the first is chosen. If no match is found then HEAD is left alone and will remain set to a symbolic ref to refs/heads/master. --- taskd/clone.sh | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/taskd/clone.sh b/taskd/clone.sh index 5a7d620..e2c788c 100755 --- a/taskd/clone.sh +++ b/taskd/clone.sh @@ -95,6 +95,9 @@ if [ -z "$mailaddrs" ]; then mailaddrs="$cfg_admin"; else mailaddrs="$mailaddrs, # Initial mirror echo "Initiating mirroring..." +headref= +showheadwarn= +warnempty= case "$url" in svn://* | svn+http://* | svn+https://*) [ -n "$cfg_mirror_svn" ] || { echo "Mirroring svn is disabled" >&2; exit 1; } @@ -210,8 +213,87 @@ case "$url" in # starting with Git 1.7.5 --mirror by itself will spew a warning # since we support older Git versions, just toss the warning git remote add --mirror origin "$url" 2>/dev/null - GIT_SSL_NO_VERIFY=1 git remote update - GIT_SSL_NO_VERIFY=1 git remote prune origin + # Set the correct HEAD symref by using ls-remote first + GIT_SSL_NO_VERIFY=1 GIT_TRACE_PACKET=1 git ls-remote origin >.refs-temp 2>.pkts-temp || \ + { + # Since everything was redirected, on failure there'd be no output, + # so let's make some failure output + cat .pkts-temp + echo "" + echo "git ls-remote \"$url\" failed" + exit 1 + } + # Compensate for git() {} side effects + unset GIT_TRACE_PACKET + # If the server is running at least Git 1.8.4.3 then it will send us the actual + # symref for HEAD. If we are running at least Git 1.7.5 then we can snarf that + # out of the packet trace data. + if [ -s .refs-temp ]; then + # Nothing to do unless the remote repository has at least 1 ref + # See if we got a HEAD ref + head="$(grep -E "^$octet20[[:space:]]+HEAD\$" <.refs-temp | awk '{print $1}')" + # If the remote has HEAD set to a symbolic ref that does not exist + # then we will not receive a HEAD ref in the ls-remote output + headref= + showheadwarn= + symrefcap= + if [ -n "$head" ]; then + symrefcap="$(sed -ne <.pkts-temp \ + '/packet:.*git<.*[[:space:]]symref=HEAD:refs\/heads\/[^[:space:]][^[:space:]]*/'"\ + "'{s/^.*[[:space:]]symref=HEAD:\(refs\/heads\/[^[:space:]][^[:space:]]*\).*$/\1/;p;}')" + # prefer $symrefcap (refs/heads/master if no $symrefcap) if it + # matches HEAD otherwise take the first refs/heads/... match + matchcnt=0 + while read ref; do + [ -n "$ref" ] || continue + matchcnt=$(( $matchcnt + 1 )) + if [ -z "$headref" ] || [ "$ref" = "${symrefcap:-refs/heads/master}" ]; then + headref="$ref" + fi + [ "$headref" = "${symrefcap:-refs/heads/master}" -a $matchcnt -gt 1 ] && break + done <<-EOT + $(grep -E "^$head[[:space:]]+refs/heads/[^[:space:]]+\$" <.refs-temp | \ + awk '{print $2}') + EOT + # Warn if there was more than one match and $symrefcap is empty + # or $symrefcap is not the same as $headref since our choice might + # differ from the source repository's HEAD + if [ $matchcnt -ge 1 -a "$symrefcap" != "$headref" ] && \ + [ -n "$symrefcap" -o $matchcnt -gt 1 ]; then + showheadwarn=1 + fi + fi + if [ -z "$headref" ]; then + # If we still don't have a HEAD ref then prefer refs/heads/master + # if it exists otherwise take the first refs/heads/... + # We do not support having a detached HEAD. + # We always warn now because we will be setting HEAD differently + # than the source repository had HEAD set + showheadwarn=1 + while read ref; do + [ -n "$ref" ] || continue + if [ -z "$headref" ] || [ "$ref" = "refs/heads/master" ]; then + headref="$ref" + fi + [ "$headref" = "refs/heads/master" ] && break + done <<-EOT + $(grep -E "^$octet20[[:space:]]+refs/heads/[^[:space:]]+\$" <.refs-temp | \ + awk '{print $2}') + EOT + fi + # If we STILL do not have a HEAD ref (perhaps the source repository + # contains only tags) then use refs/heads/master. It will be invalid + # but is no worse than we used to do by default and we'll warn about + # it. We do not support a HEAD symref to anything other than refs/heads/... + [ -n "$headref" ] || headref="refs/heads/master" + git symbolic-ref HEAD "$headref" + GIT_SSL_NO_VERIFY=1 git remote update + GIT_SSL_NO_VERIFY=1 git remote prune origin + else + warnempty=1 + git symbolic-ref HEAD "refs/heads/master" + fi + rm -f .refs-temp .pkts-temp ;; esac @@ -236,6 +318,17 @@ echo "Final touches..." git update-server-info trap "" EXIT +emptynote= +[ -z "$warnempty" ] || +emptynote=" +WARNING: You have mirrored an empty repository. +" +headnote= +[ -n "$showheadwarn" -a -n "$headref" ] && +headnote=" +NOTE: HEAD has been set to a symbolic ref to \"$headref\". + Use the \"Project settings\" link to choose a different HEAD symref. +" sizenote= ! is_gfi_mirror || sizenote=" @@ -252,7 +345,7 @@ Congratulations! The clone of project $proj just completed. * Source URL: $url * GitWeb interface: $cfg_gitweburl/$projdir * Project settings: $cfg_webadmurl/editproj.cgi?name=$(echo "$proj" | sed -e 's/[+]/%2B/g') -$sizenote +$emptynote$headnote$sizenote Have a lot of fun. EOT -- 2.11.4.GIT