submodule: avoid auto-discovery in prepare_submodule_repo_env()

The function is used to set up the environment variable used in a subprocess we spawn in a submodule directory. The callers set up a child_process structure, find the working tree path of one submodule and set .dir field to it, and then use start_command() API to spawn the subprocess like "status", "fetch", etc. When this happens, we expect that the ".git" (either a directory or a gitfile that points at the real location) in the current working directory of the subprocess MUST be the repository for the submodule. If this ".git" thing is a corrupt repository, however, because prepare_submodule_repo_env() unsets GIT_DIR and GIT_WORK_TREE, the subprocess will see ".git", thinks it is not a repository, and attempt to find one by going up, likely to end up in finding the repository of the superproject. In some codepaths, this will cause a command run with the "--recurse-submodules" option to recurse forever. By exporting GIT_DIR=.git, disable the auto-discovery logic in the subprocess, which would instead stop it and report an error. The test illustrates existing problems in a few callsites of this function. Without this fix, "git fetch --recurse-submodules", "git status" and "git diff" keep recursing forever. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Sep 1, 2016 at 13:51 UTC 10f5c526561604ba9677dc27643b5c9bfad36458
2 files changed +36
submodule.c
+1
@@ -1160,4 +1160,5 @@ void prepare_submodule_repo_env(struct argv_array *out)
1160 if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
1161 argv_array_push(out, *var);
1162 }
1163 + argv_array_push(out, "GIT_DIR=.git");
1164 }
t/t5526-fetch-submodules.sh
+35
@@ -485,4 +485,39 @@ test_expect_success 'fetching submodules respects parallel settings' '
485 )
486 '
487
488 +test_expect_success 'fetching submodule into a broken repository' '
489 + # Prepare src and src/sub nested in it
490 + git init src &&
491 + (
492 + cd src &&
493 + git init sub &&
494 + git -C sub commit --allow-empty -m "initial in sub" &&
495 + git submodule add -- ./sub sub &&
496 + git commit -m "initial in top"
497 + ) &&
498 +
499 + # Clone the old-fashoned way
500 + git clone src dst &&
501 + git -C dst clone ../src/sub sub &&
502 +
503 + # Make sure that old-fashoned layout is still supported
504 + git -C dst status &&
505 +
506 + # "diff" would find no change
507 + git -C dst diff --exit-code &&
508 +
509 + # Recursive-fetch works fine
510 + git -C dst fetch --recurse-submodules &&
511 +
512 + # Break the receiving submodule
513 + rm -f dst/sub/.git/HEAD &&
514 +
515 + # NOTE: without the fix the following tests will recurse forever!
516 + # They should terminate with an error.
517 +
518 + test_must_fail git -C dst status &&
519 + test_must_fail git -C dst diff &&
520 + test_must_fail git -C dst fetch --recurse-submodules
521 +'
522 +
523 test_done