From 16257b2ef663d7dce3b3d9545962e02c61a2b756 Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Sun, 25 Sep 2016 08:41:36 -0700 Subject: [PATCH] clone/update: optimize ref removal The clone operation needs to remove any pre-existing refs before restarting a clone and also any garbage git-svn refs after an svn clone. The update operation needs to remove any garbage git-svn refs after an update and also unclean refs when switching to a clean mirror. Calling update-ref once for each ref to be removed is slow and can be terribly painful when thousands of refs are involved. Instead use update-ref --stdin and process all the changes at once. We do, however, provide a slow backwards compatibiliy function so that a server that for some incomprehensible reason is not running at least Git version 1.8.5 (when update-ref --stdin functionality was added) can still operate properly. Signed-off-by: Kyle J. McKay --- jobd/update.sh | 25 ++++++++++--------------- shlib.sh | 21 +++++++++++++++++++++ taskd/clone.sh | 15 ++++----------- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/jobd/update.sh b/jobd/update.sh index 1e692cb..8f4b6ef 100755 --- a/jobd/update.sh +++ b/jobd/update.sh @@ -248,16 +248,9 @@ case "$url" in chmod -R ug+rw,o+r svn # git svn also leaves behind ref turds that end with @nnn # We get rid of them now - git for-each-ref --format='%(objectname) %(refname)' | \ - { while read sha1 ref; do - case "$ref" in - ?*@[1-9]|?*@[1-9][0-9]|?*@[1-9][0-9][0-9]|?*@[1-9][0-9][0-9][0-9]|\ - ?*@[1-9][0-9][0-9][0-9][0-9]|?*@[1-9][0-9][0-9][0-9][0-9][0-9]|\ - ?*@[1-9][0-9][0-9][0-9][0-9][0-9][0-9]|\ - ?*@[1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]) - git update-ref -d "$ref" - esac - done; } + git for-each-ref --format='%(refname)' | + LC_ALL=C sed '/^..*@[1-9][0-9]*$/!d; s/^/delete /' | + git_updateref_stdin unset GIT_ASKPASS_PASSWORD ;; darcs://*) @@ -318,11 +311,13 @@ case "$url" in if [ -n "$nextisclean" ]; then # We must manually purge the unclean refs now as even prune won't do it git for-each-ref --format='%(refname)' | - while read -r aref; do - case "$aref" in refs/heads/*|refs/tags/*|refs/notes/*|refs/top-bases/*) :;; *) - git update-ref -d "$aref" - esac - done + LC_ALL=C sed \ + -e '/^refs\/heads\//d' \ + -e '/^refs\/tags\//d' \ + -e '/^refs\/notes\//d' \ + -e '/^refs\/top-bases\//d' \ + -e 's/^/delete /' | + git_updateref_stdin fi git config --bool girocco.lastupdateclean ${nextisclean:-0} fi diff --git a/shlib.sh b/shlib.sh index dfd5ae2..329b9d1 100644 --- a/shlib.sh +++ b/shlib.sh @@ -259,6 +259,27 @@ git() ( exec "$cfg_git_bin" "$@" ) +# Since we do not yet require at least Git 1.8.5 this is a compatibility function +# that allows us to use git update-ref --stdin where supported and the slow shell +# script where not, but only the "delete" operation is currently supported. +git_updateref_stdin() { + if [ -n "$var_have_git_185" ]; then + git update-ref --stdin + else + while read -r _op _ref; do + case "$_op" in + delete) + git update-ref -d "$_ref" + ;; + *) + echo "bad git_updateref_stdin op: $_op" >&2 + exit 1 + ;; + esac + done + fi +} + # see comments for git() -- callers must explicitly export all variables # intended for the commands these functions run before calling them perl() { command "${var_perl_bin:-perl}" "$@"; } diff --git a/taskd/clone.sh b/taskd/clone.sh index d7ad402..ce7551a 100755 --- a/taskd/clone.sh +++ b/taskd/clone.sh @@ -149,7 +149,7 @@ cleanup_failed_clone() { # Remove all pre-existing refs rm -f packed-refs - eval "$(git for-each-ref --shell --format='git update-ref -d %(refname) || :')" 2>/dev/null || : + git for-each-ref --format='delete %(refname)' | git_updateref_stdin 2>/dev/null || : # The initial state before a clone starts has HEAD as a symbolic-ref to master git symbolic-ref HEAD refs/heads/master @@ -280,16 +280,9 @@ case "$url" in chmod -R ug+rw,o+r svn # git svn also leaves behind ref turds that end with @nnn # We get rid of them now - git for-each-ref --format='%(objectname) %(refname)' | \ - { while read sha1 ref; do - case "$ref" in - ?*@[1-9]|?*@[1-9][0-9]|?*@[1-9][0-9][0-9]|?*@[1-9][0-9][0-9][0-9]|\ - ?*@[1-9][0-9][0-9][0-9][0-9]|?*@[1-9][0-9][0-9][0-9][0-9][0-9]|\ - ?*@[1-9][0-9][0-9][0-9][0-9][0-9][0-9]|\ - ?*@[1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]) - git update-ref -d "$ref" - esac - done; } + git for-each-ref --format='%(refname)' | + LC_ALL=C sed '/^..*@[1-9][0-9]*$/!d; s/^/delete /' | + git_updateref_stdin unset GIT_ASKPASS_PASSWORD ;; darcs://*) -- 2.11.4.GIT