From: Josh Steadmon <steadmon@xxxxxxxxxx> As noted in 532139940c (add: warn when adding an embedded repository, 2017-06-14), adding embedded repositories results in subpar experience compared to submodules, due to the lack of a corresponding .gitmodules entry, which means later clones of the top-level repository cannot locate the embedded repo. We expect that this situation is usually unintentional, which is why 532139940c added a warning message and advice when users attempt to add an embedded repo. At $dayjob, we have found that even this advice is insufficient to stop users from committing unclonable embedded repos in shared projects. This causes toil for the owners of the top-level project repository as they must clean up the resulting gitlinks. Additionally, these mistakes are often made by partners outside of $dayjob, which means that a simple organization-wide change to the default Git config would be insufficient to prevent these mistakes. Due to this experience, we believe that Git's default behavior should be changed to disallow adding embedded repositories. This commit changes the existing warning into a fatal error while preserving the `--no-warn-embedded-repo` flag as a way to bypass this check. Signed-off-by: Josh Steadmon <steadmon@xxxxxxxxxx> Signed-off-by: Calvin Wan <calvinwan@xxxxxxxxxx> --- Documentation/git-add.txt | 7 ++++--- builtin/add.c | 28 ++++++++++++++++++++-------- t/t7400-submodule-basic.sh | 4 ++-- t/t7412-submodule-absorbgitdirs.sh | 2 +- t/t7414-submodule-mistakes.sh | 21 ++++++++++----------- t/t7450-bad-git-dotfiles.sh | 2 +- 6 files changed, 38 insertions(+), 26 deletions(-) diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index a030d33c6e..b7fb95b061 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -177,10 +177,11 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files. tree or not. --no-warn-embedded-repo:: - By default, `git add` will warn when adding an embedded + By default, `git add` will error out when adding an embedded repository to the index without using `git submodule add` to - create an entry in `.gitmodules`. This option will suppress the - warning (e.g., if you are manually performing operations on + create an entry in `.gitmodules`. This option will allow the + embedded repository to be added and suppress the error. + (e.g., if you are manually performing operations on submodules). --renormalize:: diff --git a/builtin/add.c b/builtin/add.c index 76277df326..795d9251b9 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -421,36 +421,45 @@ static const char embedded_advice[] = N_( "\n" " git rm --cached %s\n" "\n" -"See \"git help submodule\" for more information." +"See \"git help submodule\" for more information.\n" +"\n" +"If you cannot use submodules, you may bypass this check with:\n" +"\n" +" git add --no-warn-embedded-repo %s\n" ); -static void check_embedded_repo(const char *path) +static int check_embedded_repo(const char *path) { + int ret = 0; struct strbuf name = STRBUF_INIT; static int adviced_on_embedded_repo = 0; if (!warn_on_embedded_repo) - return; + goto cleanup; if (!ends_with(path, "/")) - return; + goto cleanup; + + ret = 1; /* Drop trailing slash for aesthetics */ strbuf_addstr(&name, path); strbuf_strip_suffix(&name, "/"); - warning(_("adding embedded git repository: %s"), name.buf); + error(_("cannot add embedded git repository: %s"), name.buf); if (!adviced_on_embedded_repo && advice_enabled(ADVICE_ADD_EMBEDDED_REPO)) { - advise(embedded_advice, name.buf, name.buf); + advise(embedded_advice, name.buf, name.buf, name.buf); adviced_on_embedded_repo = 1; } +cleanup: strbuf_release(&name); + return ret; } static int add_files(struct dir_struct *dir, int flags) { - int i, exit_status = 0; + int i, exit_status = 0, embedded_repo = 0; struct string_list matched_sparse_paths = STRING_LIST_INIT_NODUP; if (dir->ignored_nr) { @@ -476,10 +485,13 @@ static int add_files(struct dir_struct *dir, int flags) die(_("adding files failed")); exit_status = 1; } else { - check_embedded_repo(dir->entries[i]->name); + embedded_repo |= check_embedded_repo(dir->entries[i]->name); } } + if (embedded_repo) + die(_("refusing to add embedded git repositories")); + if (matched_sparse_paths.nr) { advise_on_updating_sparse_paths(&matched_sparse_paths); exit_status = 1; diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index eae6a46ef3..e0bcecba6e 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -118,7 +118,7 @@ test_expect_success 'setup - repository in init subdirectory' ' test_expect_success 'setup - commit with gitlink' ' echo a >a && echo z >z && - git add a init z && + git add --no-warn-embedded-repo a init z && git commit -m "super commit 1" ' @@ -771,7 +771,7 @@ test_expect_success 'set up for relative path tests' ' git init && test_commit foo ) && - git add sub && + git add --no-warn-embedded-repo sub && git config -f .gitmodules submodule.sub.path sub && git config -f .gitmodules submodule.sub.url ../subrepo && cp .git/config pristine-.git-config && diff --git a/t/t7412-submodule-absorbgitdirs.sh b/t/t7412-submodule-absorbgitdirs.sh index 2859695c6d..365953b558 100755 --- a/t/t7412-submodule-absorbgitdirs.sh +++ b/t/t7412-submodule-absorbgitdirs.sh @@ -100,7 +100,7 @@ test_expect_success 'absorb the git dir in a nested submodule' ' test_expect_success 'setup a gitlink with missing .gitmodules entry' ' git init sub2 && test_commit -C sub2 first && - git add sub2 && + git add --no-warn-embedded-repo sub2 && git commit -m superproject ' diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh index 101afff30f..dbdcbe909d 100755 --- a/t/t7414-submodule-mistakes.sh +++ b/t/t7414-submodule-mistakes.sh @@ -10,31 +10,30 @@ test_expect_success 'create embedded repository' ' test_commit -C embed one ' -test_expect_success 'git-add on embedded repository warns' ' - test_when_finished "git rm --cached -f embed" && - git add embed 2>stderr && - test_i18ngrep warning stderr +test_expect_success 'git-add on embedded repository dies' ' + test_must_fail git add embed 2>stderr && + test_i18ngrep fatal stderr ' -test_expect_success '--no-warn-embedded-repo suppresses warning' ' +test_expect_success '--no-warn-embedded-repo suppresses error message' ' test_when_finished "git rm --cached -f embed" && git add --no-warn-embedded-repo embed 2>stderr && - test_i18ngrep ! warning stderr + test_i18ngrep ! fatal stderr ' -test_expect_success 'no warning when updating entry' ' +test_expect_success 'no error message when updating entry' ' test_when_finished "git rm --cached -f embed" && - git add embed && + git add --no-warn-embedded-repo embed && git -C embed commit --allow-empty -m two && git add embed 2>stderr && - test_i18ngrep ! warning stderr + test_i18ngrep ! fatal stderr ' -test_expect_success 'submodule add does not warn' ' +test_expect_success 'submodule add does not issue error message' ' test_when_finished "git rm -rf submodule .gitmodules" && git -c protocol.file.allow=always \ submodule add ./embed submodule 2>stderr && - test_i18ngrep ! warning stderr + test_i18ngrep ! fatal stderr ' test_done diff --git a/t/t7450-bad-git-dotfiles.sh b/t/t7450-bad-git-dotfiles.sh index ba1f569bcb..6fbcf36ae9 100755 --- a/t/t7450-bad-git-dotfiles.sh +++ b/t/t7450-bad-git-dotfiles.sh @@ -307,7 +307,7 @@ test_expect_success 'git dirs of sibling submodules must not be nested' ' EOF git clone . thing1 && git clone . thing2 && - git add .gitmodules thing1 thing2 && + git add --no-warn-embedded-repo .gitmodules thing1 thing2 && test_tick && git commit -m nested ) && -- 2.39.1.581.gbfd45094c4-goog