Merge branch 'jk/warn-add-gitlink'

Using "git add d/i/r" when d/i/r is the top of the working tree of
a separate repository would create a gitlink in the index, which
would appear as a not-quite-initialized submodule to others.  We
learned to give warnings when this happens.

* jk/warn-add-gitlink:
  t: move "git add submodule" into test blocks
  add: warn when adding an embedded repository
This commit is contained in:
Junio C Hamano 2017-06-24 14:28:41 -07:00
commit cda4ba30b1
10 changed files with 113 additions and 12 deletions

View File

@ -348,6 +348,9 @@ advice.*::
rmHints::
In case of failure in the output of linkgit:git-rm[1],
show directions on how to proceed from the current state.
addEmbeddedRepo::
Advice on what to do when you've accidentally added one
git repo inside of another.
--
core.fileMode::

View File

@ -165,6 +165,13 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
be ignored, no matter if they are already present in the work
tree or not.
--no-warn-embedded-repo::
By default, `git add` will warn 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
submodules).
--chmod=(+|-)x::
Override the executable bit of the added files. The executable
bit is only changed in the index, the files on disk are left

View File

@ -16,6 +16,7 @@ int advice_detached_head = 1;
int advice_set_upstream_failure = 1;
int advice_object_name_warning = 1;
int advice_rm_hints = 1;
int advice_add_embedded_repo = 1;
static struct {
const char *name;
@ -36,6 +37,7 @@ static struct {
{ "setupstreamfailure", &advice_set_upstream_failure },
{ "objectnamewarning", &advice_object_name_warning },
{ "rmhints", &advice_rm_hints },
{ "addembeddedrepo", &advice_add_embedded_repo },
/* make this an alias for backward compatibility */
{ "pushnonfastforward", &advice_push_update_rejected }

View File

@ -18,6 +18,7 @@ extern int advice_detached_head;
extern int advice_set_upstream_failure;
extern int advice_object_name_warning;
extern int advice_rm_hints;
extern int advice_add_embedded_repo;
int git_default_advice_config(const char *var, const char *value);
__attribute__((format (printf, 1, 2)))

View File

@ -250,6 +250,7 @@ N_("The following paths are ignored by one of your .gitignore files:\n");
static int verbose, show_only, ignored_too, refresh_only;
static int ignore_add_errors, intent_to_add, ignore_missing;
static int warn_on_embedded_repo = 1;
#define ADDREMOVE_DEFAULT 1
static int addremove = ADDREMOVE_DEFAULT;
@ -283,6 +284,8 @@ static struct option builtin_add_options[] = {
OPT_BOOL( 0 , "ignore-errors", &ignore_add_errors, N_("just skip files which cannot be added because of errors")),
OPT_BOOL( 0 , "ignore-missing", &ignore_missing, N_("check if - even missing - files are ignored in dry run")),
OPT_STRING( 0 , "chmod", &chmod_arg, N_("(+/-)x"), N_("override the executable bit of the listed files")),
OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
N_("warn when adding an embedded repository")),
OPT_END(),
};
@ -296,6 +299,45 @@ static int add_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
static const char embedded_advice[] = N_(
"You've added another git repository inside your current repository.\n"
"Clones of the outer repository will not contain the contents of\n"
"the embedded repository and will not know how to obtain it.\n"
"If you meant to add a submodule, use:\n"
"\n"
" git submodule add <url> %s\n"
"\n"
"If you added this path by mistake, you can remove it from the\n"
"index with:\n"
"\n"
" git rm --cached %s\n"
"\n"
"See \"git help submodule\" for more information."
);
static void check_embedded_repo(const char *path)
{
struct strbuf name = STRBUF_INIT;
if (!warn_on_embedded_repo)
return;
if (!ends_with(path, "/"))
return;
/* Drop trailing slash for aesthetics */
strbuf_addstr(&name, path);
strbuf_strip_suffix(&name, "/");
warning(_("adding embedded git repository: %s"), name.buf);
if (advice_add_embedded_repo) {
advise(embedded_advice, name.buf, name.buf);
/* there may be multiple entries; advise only once */
advice_add_embedded_repo = 0;
}
strbuf_release(&name);
}
static int add_files(struct dir_struct *dir, int flags)
{
int i, exit_status = 0;
@ -308,12 +350,14 @@ static int add_files(struct dir_struct *dir, int flags)
exit_status = 1;
}
for (i = 0; i < dir->nr; i++)
for (i = 0; i < dir->nr; i++) {
check_embedded_repo(dir->entries[i]->name);
if (add_file_to_index(&the_index, dir->entries[i]->name, flags)) {
if (!ignore_add_errors)
die(_("adding files failed"));
exit_status = 1;
}
}
return exit_status;
}

View File

@ -213,7 +213,8 @@ cmd_add()
die "$(eval_gettext "'\$sm_path' already exists in the index and is not a submodule")"
fi
if test -z "$force" && ! git add --dry-run --ignore-missing "$sm_path" > /dev/null 2>&1
if test -z "$force" &&
! git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" > /dev/null 2>&1
then
eval_gettextln "The following path is ignored by one of your .gitignore files:
\$sm_path
@ -267,7 +268,7 @@ or you are unsure what this means choose another name with the '--name' option."
fi
git config submodule."$sm_name".url "$realrepo"
git add $force "$sm_path" ||
git add --no-warn-embedded-repo $force "$sm_path" ||
die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
git config -f .gitmodules submodule."$sm_name".path "$sm_path" &&

View File

@ -430,9 +430,11 @@ test_expect_success 'deleted submodule' '
test_cmp expected actual
'
test_create_repo sm2 &&
head7=$(add_file sm2 foo8 foo9) &&
git add sm2
test_expect_success 'create second submodule' '
test_create_repo sm2 &&
head7=$(add_file sm2 foo8 foo9) &&
git add sm2
'
test_expect_success 'multiple submodules' '
git diff-index -p --submodule=log HEAD >actual &&

View File

@ -643,9 +643,11 @@ test_expect_success 'deleted submodule' '
test_cmp expected actual
'
test_create_repo sm2 &&
head7=$(add_file sm2 foo8 foo9) &&
git add sm2
test_expect_success 'create second submodule' '
test_create_repo sm2 &&
head7=$(add_file sm2 foo8 foo9) &&
git add sm2
'
test_expect_success 'multiple submodules' '
git diff-index -p --submodule=diff HEAD >actual &&

View File

@ -241,9 +241,11 @@ EOF
test_cmp expected actual
"
test_create_repo sm2 &&
head7=$(add_file sm2 foo8 foo9) &&
git add sm2
test_expect_success 'create second submodule' '
test_create_repo sm2 &&
head7=$(add_file sm2 foo8 foo9) &&
git add sm2
'
test_expect_success 'multiple submodules' "
git submodule summary >actual &&

37
t/t7414-submodule-mistakes.sh Executable file
View File

@ -0,0 +1,37 @@
#!/bin/sh
test_description='handling of common mistakes people may make with submodules'
. ./test-lib.sh
test_expect_success 'create embedded repository' '
git init embed &&
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 '--no-warn-embedded-repo suppresses warning' '
test_when_finished "git rm --cached -f embed" &&
git add --no-warn-embedded-repo embed 2>stderr &&
test_i18ngrep ! warning stderr
'
test_expect_success 'no warning when updating entry' '
test_when_finished "git rm --cached -f embed" &&
git add embed &&
git -C embed commit --allow-empty -m two &&
git add embed 2>stderr &&
test_i18ngrep ! warning stderr
'
test_expect_success 'submodule add does not warn' '
test_when_finished "git rm -rf submodule .gitmodules" &&
git submodule add ./embed submodule 2>stderr &&
test_i18ngrep ! warning stderr
'
test_done