Stefan Beller <sbeller@xxxxxxxxxx> writes: > Every once in a while someone complains to the mailing list to have > run into this weird assertion[1]. The usual response from the mailing > list is link to old discussions[2], and acknowledging the problem > stating it is known. > > This patch accomplishes two things: > > 1. Switch assert() to die("BUG") to give a more readable message. > > 2. Take one of the cases where we hit a BUG and turn it into a normal > "there was something wrong with the input" message. > > This assertion triggered for cases where there wasn't a programming > bug, but just bogus input. In particular, if the user asks for a > pathspec that is inside a submodule, we shouldn't assert() or > die("BUG"); we should tell the user their request is bogus. > > The only reason we did not check for it, is the expensive nature > of such a check, so callers avoid setting the flag > PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE. However when we die due > to bogus input, the expense of CPU cycles spent outweighs the user > wondering what went wrong, so run that check unconditionally before > dying with a more generic error message. > > Note: There is a case (e.g. "git -C submodule add .") in which we call > strip_submodule_slash_expensive, as git-add requests it via the flag > PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE, but the assert used to > trigger nevertheless, because the flag PATHSPEC_LITERAL was not set, > such that we executed > > if (item->nowildcard_len < prefixlen) > item->nowildcard_len = prefixlen; > > and prefixlen was not adapted (e.g. it was computed from "submodule/") > So in the die_inside_submodule_path function we also need handle paths, > that were stripped before, i.e. are the exact submodule path. This > is why the conditions in die_inside_submodule_path are slightly > different than in strip_submodule_slash_expensive. > > [1] https://www.google.com/search?q=item-%3Enowildcard_len > [2] http://git.661346.n2.nabble.com/assert-failed-in-submodule-edge-case-td7628687.html > https://www.spinics.net/lists/git/msg249473.html > > Helped-by: Jeff King <peff@xxxxxxxx> > Helped-by: Junio C Hamano <gitster@xxxxxxxxx> > Signed-off-by: Stefan Beller <sbeller@xxxxxxxxxx> > --- For future reference, do not bury a useful fix behind unproven new things. The main purpose of this two-patch series is this change, and it does not have to wait for the change to allow test_commit to notice "-C" you have in another series. Just write it in longhand, and when both topics graduate, send in another patch to update "(cd <dir> && test_commit <others>)" to use the new "test_commit -C <dir> <others>". Thanks. > pathspec.c | 35 +++++++++++++++++++++++++++++++++-- > t/t6134-pathspec-in-submodule.sh | 33 +++++++++++++++++++++++++++++++++ > 2 files changed, 66 insertions(+), 2 deletions(-) > create mode 100755 t/t6134-pathspec-in-submodule.sh > > diff --git a/pathspec.c b/pathspec.c > index d4efcf6662..42cd83c235 100644 > --- a/pathspec.c > +++ b/pathspec.c > @@ -296,6 +296,27 @@ static void strip_submodule_slash_expensive(struct pathspec_item *item) > } > } > > +static void die_inside_submodule_path(struct pathspec_item *item) > +{ > + int i; > + > + for (i = 0; i < active_nr; i++) { > + struct cache_entry *ce = active_cache[i]; > + int ce_len = ce_namelen(ce); > + > + if (!S_ISGITLINK(ce->ce_mode)) > + continue; > + > + if (item->len < ce_len || > + !(item->match[ce_len] == '/' || item->match[ce_len] == '\0') || > + memcmp(ce->name, item->match, ce_len)) > + continue; > + > + die(_("Pathspec '%s' is in submodule '%.*s'"), > + item->original, ce_len, ce->name); > + } > +} > + > /* > * Perform the initialization of a pathspec_item based on a pathspec element. > */ > @@ -391,8 +412,18 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags, > } > > /* sanity checks, pathspec matchers assume these are sane */ > - assert(item->nowildcard_len <= item->len && > - item->prefix <= item->len); > + if (item->nowildcard_len > item->len || > + item->prefix > item->len) { > + /* > + * This case can be triggered by the user pointing us to a > + * pathspec inside a submodule, which is an input error. > + * Detect that here and complain, but fallback in the > + * non-submodule case to a BUG, as we have no idea what > + * would trigger that. > + */ > + die_inside_submodule_path(item); > + die ("BUG: item->nowildcard_len > item->len || item->prefix > item->len)"); > + } > } > > static int pathspec_item_cmp(const void *a_, const void *b_) > diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh > new file mode 100755 > index 0000000000..2900d8d06e > --- /dev/null > +++ b/t/t6134-pathspec-in-submodule.sh > @@ -0,0 +1,33 @@ > +#!/bin/sh > + > +test_description='test case exclude pathspec' > + > +TEST_CREATE_SUBMODULE=yes > +. ./test-lib.sh > + > +test_expect_success 'setup a submodule' ' > + git submodule add ./pretzel.bare sub && > + git commit -a -m "add submodule" && > + git submodule deinit --all > +' > + > +cat <<EOF >expect > +fatal: Pathspec 'sub/a' is in submodule 'sub' > +EOF > + > +test_expect_success 'error message for path inside submodule' ' > + echo a >sub/a && > + test_must_fail git add sub/a 2>actual && > + test_cmp expect actual > +' > + > +cat <<EOF >expect > +fatal: Pathspec '.' is in submodule 'sub' > +EOF > + > +test_expect_success 'error message for path inside submodule from within submodule' ' > + test_must_fail git -C sub add . 2>actual && > + test_cmp expect actual > +' > + > +test_done