From adb231cfdabd9b62c2d99519ac5f8ba29d689478 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Thu, 9 Jun 2011 09:47:02 +0200 Subject: [PATCH 1/3] git-sh-setup: add die_with_status This behaves similar to "die" but can exit with status different from the usual 1. Signed-off-by: Fredrik Gustafsson Mentored-by: Jens Lehmann Mentored-by: Heiko Voigt Signed-off-by: Junio C Hamano --- git-sh-setup.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/git-sh-setup.sh b/git-sh-setup.sh index 94e26ed5e8..8e427dab31 100644 --- a/git-sh-setup.sh +++ b/git-sh-setup.sh @@ -39,9 +39,15 @@ git_broken_path_fix () { # @@BROKEN_PATH_FIX@@ -die() { - echo >&2 "$@" - exit 1 +die () { + die_with_status 1 "$@" +} + +die_with_status () { + status=$1 + shift + echo >&2 "$*" + exit "$status" } GIT_QUIET= From 15ffb7cde48b73b3d5ce259443db7d2e0ba13750 Mon Sep 17 00:00:00 2001 From: Fredrik Gustafsson Date: Mon, 13 Jun 2011 19:15:26 +0200 Subject: [PATCH 2/3] submodule update: continue when a checkout fails "git submodule update" stops at the first error and gives control back to the user. Only after the user fixes the problematic submodule and runs "git submodule update" again, the second error is found. And the user needs to repeat until all the problems are found and fixed one by one. This is tedious. Instead, the command can remember which submodules it had trouble with, continue updating the ones it can, and report which ones had errors at the end. The user can run "git submodule update", find all the ones that need minor fixing (e.g. working tree was dirty) to fix them in a single pass. Then another "git submodule update" can be run to update all. Note that the problematic submodules are skipped only when they are to be integrated with a safer value of submodule..update option, namely "checkout". Fixing a failure in a submodule that uses "rebase" or "merge" may need an involved conflict resolution by the user, and leaving too many submodules in states that need resolution would not reduce the mental burden on the user. Signed-off-by: Fredrik Gustafsson Mentored-by: Jens Lehmann Mentored-by: Heiko Voigt Signed-off-by: Junio C Hamano --- git-submodule.sh | 50 +++++++++++-- t/t7406-submodule-update.sh | 144 ++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 6 deletions(-) diff --git a/git-submodule.sh b/git-submodule.sh index d189a24c71..eb5eebcd96 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -444,7 +444,8 @@ cmd_update() fi cloned_modules= - module_list "$@" | + module_list "$@" | { + err= while read mode sha1 stage path do if test "$stage" = U @@ -525,17 +526,54 @@ cmd_update() ;; esac - (clear_local_git_env; cd "$path" && $command "$sha1") || - die "Unable to $action '$sha1' in submodule path '$path'" - say "Submodule path '$path': $msg '$sha1'" + if (clear_local_git_env; cd "$path" && $command "$sha1") + then + say "Submodule path '$path': $msg '$sha1'" + else + case $action in + rebase|merge) + die_with_status 2 "Unable to $action '$sha1' in submodule path '$path'" + ;; + *) + err="${err};Failed to $action in submodule path '$path'" + continue + ;; + esac + fi fi if test -n "$recursive" then - (clear_local_git_env; cd "$path" && eval cmd_update "$orig_flags") || - die "Failed to recurse into submodule path '$path'" + (clear_local_git_env; cd "$path" && eval cmd_update "$orig_flags") + res=$? + if test $res -gt 0 + then + if test $res -eq 1 + then + err="${err};Failed to recurse into submodule path '$path'" + continue + else + die_with_status $res "Failed to recurse into submodule path '$path'" + fi + fi fi done + + if test -n "$err" + then + OIFS=$IFS + IFS=';' + for e in $err + do + if test -n "$e" + then + echo >&2 "$e" + fi + done + IFS=$OIFS + exit 1 + fi + } } set_name_rev () { diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh index 4f16fcce2b..807f761147 100755 --- a/t/t7406-submodule-update.sh +++ b/t/t7406-submodule-update.sh @@ -298,4 +298,148 @@ test_expect_success 'submodule update ignores update=rebase config for new submo ) ' +test_expect_success 'submodule update continues after checkout error' ' + (cd super && + git reset --hard HEAD && + git submodule add ../submodule submodule2 && + git submodule init && + git commit -am "new_submodule" && + (cd submodule2 && + git rev-parse --max-count=1 HEAD > ../expect + ) && + (cd submodule && + test_commit "update_submodule" file + ) && + (cd submodule2 && + test_commit "update_submodule2" file + ) && + git add submodule && + git add submodule2 && + git commit -m "two_new_submodule_commits" && + (cd submodule && + echo "" > file + ) && + git checkout HEAD^ && + test_must_fail git submodule update && + (cd submodule2 && + git rev-parse --max-count=1 HEAD > ../actual + ) && + test_cmp expect actual + ) +' +test_expect_success 'submodule update continues after recursive checkout error' ' + (cd super && + git reset --hard HEAD && + git checkout master && + git submodule update && + (cd submodule && + git submodule add ../submodule subsubmodule && + git submodule init && + git commit -m "new_subsubmodule" + ) && + git add submodule && + git commit -m "update_submodule" && + (cd submodule && + (cd subsubmodule && + test_commit "update_subsubmodule" file + ) && + git add subsubmodule && + test_commit "update_submodule_again" file && + (cd subsubmodule && + test_commit "update_subsubmodule_again" file + ) && + test_commit "update_submodule_again_again" file + ) && + (cd submodule2 && + git rev-parse --max-count=1 HEAD > ../expect && + test_commit "update_submodule2_again" file + ) && + git add submodule && + git add submodule2 && + git commit -m "new_commits" && + git checkout HEAD^ && + (cd submodule && + git checkout HEAD^ && + (cd subsubmodule && + echo "" > file + ) + ) && + test_must_fail git submodule update --recursive && + (cd submodule2 && + git rev-parse --max-count=1 HEAD > ../actual + ) && + test_cmp expect actual + ) +' + +test_expect_success 'submodule update exit immediately in case of merge conflict' ' + (cd super && + git checkout master && + git reset --hard HEAD && + (cd submodule && + (cd subsubmodule && + git reset --hard HEAD + ) + ) && + git submodule update --recursive && + (cd submodule && + test_commit "update_submodule_2" file + ) && + (cd submodule2 && + test_commit "update_submodule2_2" file + ) && + git add submodule && + git add submodule2 && + git commit -m "two_new_submodule_commits" && + (cd submodule && + git checkout master && + test_commit "conflict" file && + echo "conflict" > file + ) && + git checkout HEAD^ && + (cd submodule2 && + git rev-parse --max-count=1 HEAD > ../expect + ) && + git config submodule.submodule.update merge && + test_must_fail git submodule update && + (cd submodule2 && + git rev-parse --max-count=1 HEAD > ../actual + ) && + test_cmp expect actual + ) +' +test_expect_success 'submodule update exit immediately after recursive rebase error' ' + (cd super && + git checkout master && + git reset --hard HEAD && + (cd submodule && + git reset --hard HEAD && + git submodule update --recursive + ) && + (cd submodule && + test_commit "update_submodule_3" file + ) && + (cd submodule2 && + test_commit "update_submodule2_3" file + ) && + git add submodule && + git add submodule2 && + git commit -m "two_new_submodule_commits" && + (cd submodule && + git checkout master && + test_commit "conflict2" file && + echo "conflict" > file + ) && + git checkout HEAD^ && + (cd submodule2 && + git rev-parse --max-count=1 HEAD > ../expect + ) && + git config submodule.submodule.update rebase && + test_must_fail git submodule update && + (cd submodule2 && + git rev-parse --max-count=1 HEAD > ../actual + ) && + test_cmp expect actual + ) +' test_done From 877449c136539cf8b9b4ed9cfe33a796b7b93f93 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 13 Jun 2011 12:17:52 -0700 Subject: [PATCH 3/3] git-submodule.sh: clarify the "should we die now" logic Earlier the decision to stop or continue was made on the $action variable that was set by inspecting $update_module variable. The former is a redundant variable and will be removed in another topic. Decide upon inspecting $update_module if a failure should cascade up to cause us immediately stop, and use a variable that means just that, to clarify the logic. Incidentally this also makes the merge with the other topic slightly easier and cleaner to understand. Signed-off-by: Junio C Hamano --- git-submodule.sh | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/git-submodule.sh b/git-submodule.sh index eb5eebcd96..066d6b5716 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -508,16 +508,19 @@ cmd_update() update_module= ;; esac + must_die_on_failure= case "$update_module" in rebase) command="git rebase" action="rebase" msg="rebased onto" + must_die_on_failure=yes ;; merge) command="git merge" action="merge" msg="merged in" + must_die_on_failure=yes ;; *) command="git checkout $subforce -q" @@ -529,16 +532,12 @@ cmd_update() if (clear_local_git_env; cd "$path" && $command "$sha1") then say "Submodule path '$path': $msg '$sha1'" + elif test -n "$must_die_on_failure" + then + die_with_status 2 "Unable to $action '$sha1' in submodule path '$path'" else - case $action in - rebase|merge) - die_with_status 2 "Unable to $action '$sha1' in submodule path '$path'" - ;; - *) - err="${err};Failed to $action in submodule path '$path'" - continue - ;; - esac + err="${err};Failed to $action in submodule path '$path'" + continue fi fi