{"thread":{"id":"47992","subject":"[GSoC][PATCH v4] Make options that expect object ids less chatty if id is invalid","startedAt":"2018-03-06T19:31:45Z","lastAt":"2018-03-19T18:51:04Z","messageCount":5,"participants":["Paul-Sebastian Ungureanu","Junio C Hamano","Martin Ågren","ungureanupaulsebastian@gmail.com"],"isPatch":true,"patchVersion":4,"patchTotal":null},"messages":[{"id":"341145","messageId":"20180306193116.23876-1-ungureanupaulsebastian@gmail.com","threadId":"47992","inReplyTo":null,"subject":"[GSoC][PATCH v4] Make options that expect object ids less chatty if id is invalid","fromName":"Paul-Sebastian Ungureanu","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-03-06T19:31:16Z","receivedAt":"2018-03-06T19:31:45Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Usually, the usage should be shown only if the user does not know what\noptions are available. If the user specifies an invalid value, the user\nis already aware of the available options. In this case, there is no\npoint in displaying the usage anymore.\n\nThis patch applies to \"git tag --contains\", \"git branch --contains\",\n\"git branch --points-at\", \"git for-each-ref --contains\" and many more.\n\nSigned-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n---\n builtin/blame.c               |  1 +\n builtin/shortlog.c            |  1 +\n builtin/update-index.c        |  1 +\n parse-options.c               | 20 ++++----\n parse-options.h               |  1 +\n t/t0040-parse-options.sh      |  2 +-\n t/t3404-rebase-interactive.sh |  6 +--\n t/tcontains.sh                | 92 +++++++++++++++++++++++++++++++++++\n 8 files changed, 110 insertions(+), 14 deletions(-)\n create mode 100755 t/tcontains.sh\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 9dcb367b9..a5fbec8fb 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -728,6 +728,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\t\t    PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);\n \tfor (;;) {\n \t\tswitch (parse_options_step(&ctx, options, blame_opt_usage)) {\n+\t\tcase PARSE_OPT_ERROR:\n \t\tcase PARSE_OPT_HELP:\n \t\t\texit(129);\n \t\tcase PARSE_OPT_DONE:\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex e29875b84..0789e2eea 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -282,6 +282,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)\n \n \tfor (;;) {\n \t\tswitch (parse_options_step(&ctx, options, shortlog_usage)) {\n+\t\tcase PARSE_OPT_ERROR:\n \t\tcase PARSE_OPT_HELP:\n \t\t\texit(129);\n \t\tcase PARSE_OPT_DONE:\ndiff --git a/builtin/update-index.c b/builtin/update-index.c\nindex 58d1c2d28..34adf55a7 100644\n--- a/builtin/update-index.c\n+++ b/builtin/update-index.c\n@@ -1059,6 +1059,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n \t\t\tbreak;\n \t\tswitch (parseopt_state) {\n \t\tcase PARSE_OPT_HELP:\n+\t\tcase PARSE_OPT_ERROR:\n \t\t\texit(129);\n \t\tcase PARSE_OPT_NON_OPTION:\n \t\tcase PARSE_OPT_DONE:\ndiff --git a/parse-options.c b/parse-options.c\nindex d02eb8b01..47c09a82b 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -317,14 +317,16 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,\n \t\treturn get_value(p, options, all_opts, flags ^ opt_flags);\n \t}\n \n-\tif (ambiguous_option)\n-\t\treturn error(\"Ambiguous option: %s \"\n+\tif (ambiguous_option) {\n+\t\terror(\"Ambiguous option: %s \"\n \t\t\t\"(could be --%s%s or --%s%s)\",\n \t\t\targ,\n \t\t\t(ambiguous_flags & OPT_UNSET) ?  \"no-\" : \"\",\n \t\t\tambiguous_option->long_name,\n \t\t\t(abbrev_flags & OPT_UNSET) ?  \"no-\" : \"\",\n \t\t\tabbrev_option->long_name);\n+\t\treturn -3;\n+\t}\n \tif (abbrev_option)\n \t\treturn get_value(p, abbrev_option, all_opts, abbrev_flags);\n \treturn -2;\n@@ -434,7 +436,6 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \t\t       const char * const usagestr[])\n {\n \tint internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);\n-\tint err = 0;\n \n \t/* we must reset ->opt, unknown short option leave it dangling */\n \tctx->opt = NULL;\n@@ -459,7 +460,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \t\t\tctx->opt = arg + 1;\n \t\t\tswitch (parse_short_opt(ctx, options)) {\n \t\t\tcase -1:\n-\t\t\t\tgoto show_usage_error;\n+\t\t\t\treturn PARSE_OPT_ERROR;\n \t\t\tcase -2:\n \t\t\t\tif (ctx->opt)\n \t\t\t\t\tcheck_typos(arg + 1, options);\n@@ -472,7 +473,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \t\t\twhile (ctx->opt) {\n \t\t\t\tswitch (parse_short_opt(ctx, options)) {\n \t\t\t\tcase -1:\n-\t\t\t\t\tgoto show_usage_error;\n+\t\t\t\t\treturn PARSE_OPT_ERROR;\n \t\t\t\tcase -2:\n \t\t\t\t\tif (internal_help && *ctx->opt == 'h')\n \t\t\t\t\t\tgoto show_usage;\n@@ -504,9 +505,11 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \t\t\tgoto show_usage;\n \t\tswitch (parse_long_opt(ctx, arg + 2, options)) {\n \t\tcase -1:\n-\t\t\tgoto show_usage_error;\n+\t\t\treturn PARSE_OPT_ERROR;\n \t\tcase -2:\n \t\t\tgoto unknown;\n+\t\tcase -3:\n+\t\t\tgoto show_usage;\n \t\t}\n \t\tcontinue;\n unknown:\n@@ -517,10 +520,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,\n \t}\n \treturn PARSE_OPT_DONE;\n \n- show_usage_error:\n-\terr = 1;\n  show_usage:\n-\treturn usage_with_options_internal(ctx, usagestr, options, 0, err);\n+\treturn usage_with_options_internal(ctx, usagestr, options, 0, 0);\n }\n \n int parse_options_end(struct parse_opt_ctx_t *ctx)\n@@ -539,6 +540,7 @@ int parse_options(int argc, const char **argv, const char *prefix,\n \tparse_options_start(&ctx, argc, argv, prefix, options, flags);\n \tswitch (parse_options_step(&ctx, options, usagestr)) {\n \tcase PARSE_OPT_HELP:\n+\tcase PARSE_OPT_ERROR:\n \t\texit(129);\n \tcase PARSE_OPT_NON_OPTION:\n \tcase PARSE_OPT_DONE:\ndiff --git a/parse-options.h b/parse-options.h\nindex af711227a..c77bb3b4f 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -188,6 +188,7 @@ enum {\n \tPARSE_OPT_HELP = -1,\n \tPARSE_OPT_DONE,\n \tPARSE_OPT_NON_OPTION,\n+\tPARSE_OPT_ERROR,\n \tPARSE_OPT_UNKNOWN\n };\n \ndiff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh\nindex 0c2fc81d7..04d474c84 100755\n--- a/t/t0040-parse-options.sh\n+++ b/t/t0040-parse-options.sh\n@@ -291,7 +291,7 @@ test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '\n test_expect_success 'OPT_CALLBACK() and callback errors work' '\n \ttest_must_fail test-parse-options --no-length >output 2>output.err &&\n \ttest_i18ncmp expect output &&\n-\ttest_i18ncmp expect.err output.err\n+\ttest_must_be_empty output.err\n '\n \n cat >expect <<\\EOF\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex ef2887bd8..cac8b2bd8 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -919,10 +919,8 @@ test_expect_success 'rebase --exec works without -i ' '\n test_expect_success 'rebase -i --exec without <CMD>' '\n \tgit reset --hard execute &&\n \tset_fake_editor &&\n-\ttest_must_fail git rebase -i --exec 2>tmp &&\n-\tsed -e \"1d\" tmp >actual &&\n-\ttest_must_fail git rebase -h >expected &&\n-\ttest_cmp expected actual &&\n+\ttest_must_fail git rebase -i --exec 2>actual &&\n+\ttest_i18ngrep \"requires a value\" actual &&\n \tgit checkout master\n '\n \ndiff --git a/t/tcontains.sh b/t/tcontains.sh\nnew file mode 100755\nindex 000000000..4856111ff\n--- /dev/null\n+++ b/t/tcontains.sh\n@@ -0,0 +1,92 @@\n+#!/bin/sh\n+\n+test_description='Test \"contains\" argument behavior'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup ' '\n+\tgit init . &&\n+\techo \"this is a test\" >file &&\n+\tgit add -A &&\n+\tgit commit -am \"tag test\" &&\n+\tgit tag \"v1.0\" &&\n+\tgit tag \"v1.1\"\n+'\n+\n+test_expect_success 'tag --contains <existent_tag>' '\n+\tgit tag --contains \"v1.0\" >actual &&\n+\tgrep \"v1.0\" actual &&\n+\tgrep \"v1.1\" actual\n+'\n+\n+test_expect_success 'tag --contains <inexistent_tag>' '\n+\ttest_must_fail git tag --contains \"notag\" 2>actual &&\n+\ttest_i18ngrep \"error\" actual\n+'\n+\n+test_expect_success 'tag --no-contains <existent_tag>' '\n+\tgit tag --no-contains \"v1.1\" >actual &&\n+\ttest_line_count = 0 actual\n+'\n+\n+test_expect_success 'tag --no-contains <inexistent_tag>' '\n+\ttest_must_fail git tag --no-contains \"notag\" 2>actual &&\n+\ttest_i18ngrep \"error\" actual\n+'\n+\n+test_expect_success 'tag usage error' '\n+\ttest_must_fail git tag --noopt 2>actual &&\n+\ttest_i18ngrep \"usage\" actual\n+'\n+\n+test_expect_success 'branch --contains <existent_commit>' '\n+\tgit branch --contains \"master\" >actual &&\n+\ttest_i18ngrep \"master\" actual\n+'\n+\n+test_expect_success 'branch --contains <inexistent_commit>' '\n+\ttest_must_fail git branch --no-contains \"nocommit\" 2>actual &&\n+\ttest_i18ngrep \"error\" actual\n+'\n+\n+test_expect_success 'branch --no-contains <existent_commit>' '\n+\tgit branch --no-contains \"master\" >actual &&\n+\ttest_line_count = 0 actual\n+'\n+\n+test_expect_success 'branch --no-contains <inexistent_commit>' '\n+\ttest_must_fail git branch --no-contains \"nocommit\" 2>actual &&\n+\ttest_i18ngrep \"error\" actual\n+'\n+\n+test_expect_success 'branch usage error' '\n+\ttest_must_fail git branch --noopt 2>actual &&\n+\ttest_i18ngrep \"usage\" actual\n+'\n+\n+test_expect_success 'for-each-ref --contains <existent_object>' '\n+\tgit for-each-ref --contains \"master\" >actual &&\n+\ttest_line_count = 3 actual\n+'\n+\n+test_expect_success 'for-each-ref --contains <inexistent_object>' '\n+\ttest_must_fail git for-each-ref --no-contains \"noobject\" 2>actual &&\n+\ttest_i18ngrep \"error\" actual\n+'\n+\n+test_expect_success 'for-each-ref --no-contains <existent_object>' '\n+\tgit for-each-ref --no-contains \"master\" >actual &&\n+\ttest_line_count = 0 actual\n+'\n+\n+test_expect_success 'for-each-ref --no-contains <inexistent_object>' '\n+\ttest_must_fail git for-each-ref --no-contains \"noobject\" 2>actual &&\n+\ttest_i18ngrep \"error\" actual\n+'\n+\n+test_expect_success 'for-each-ref usage error' '\n+\ttest_must_fail git for-each-ref --noopt 2>actual &&\n+\ttest_i18ngrep \"usage\" actual\n+'\n+\n+test_done\n-- \n2.16.2.346.g16307f54f.dirty\n\n"},{"id":"341159","messageId":"xmqqlgf43nxo.fsf@gitster-ct.c.googlers.com","threadId":"47992","inReplyTo":"20180306193116.23876-1-ungureanupaulsebastian@gmail.com","subject":"Re: [GSoC][PATCH v4] Make options that expect object ids less chatty if id is invalid","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-03-06T21:16:03Z","receivedAt":"2018-03-06T21:16:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com> writes:\n\n> Usually, the usage should be shown only if the user does not know what\n> options are available. If the user specifies an invalid value, the user\n> is already aware of the available options. In this case, there is no\n> point in displaying the usage anymore.\n>\n> This patch applies to \"git tag --contains\", \"git branch --contains\",\n> \"git branch --points-at\", \"git for-each-ref --contains\" and many more.\n>\n> Signed-off-by: Paul-Sebastian Ungureanu <ungureanupaulsebastian@gmail.com>\n> ---\n\nI notice that this version changes the way the case where an\nunbiguous long option is given, compared to the previous one is\nhandled.  And I recall that that single case is what I happened to\nhave noticed during my review of the previous one, in which I was\nnot trying to be exhausitive.  \n\nI kind of find it surprising that the one single case I happened to\nhave noticed is the only one that needed special treatment.  Did you\ngo though all the codepath and made sure that the ones that still\nreturn -1 (not -2 and not -3) to parse_options_step() are all OK (in\nother words, I was just lucky) or does this version change only the\n\"ambiguous\" case, simply because that was the only one I noticed in\nmy review (in other words, this may still not be sufficient)?\n\nJust double checking.\n\nThe changes to existing tests have become a lot less noisy, compared\nto the previous one, which is probably a good thing.\n\nThanks.\n"},{"id":"341161","messageId":"xmqqh8ps3ncl.fsf@gitster-ct.c.googlers.com","threadId":"47992","inReplyTo":"xmqqlgf43nxo.fsf@gitster-ct.c.googlers.com","subject":"Re: [GSoC][PATCH v4] Make options that expect object ids less chatty if id is invalid","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-03-06T21:28:42Z","receivedAt":"2018-03-06T21:28:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I kind of find it surprising that the one single case I happened to\n> have noticed is the only one that needed special treatment.  Did you\n> go though all the codepath and made sure that the ones that still\n> return -1 (not -2 and not -3) to parse_options_step() are all OK (in\n> other words, I was just lucky) or does this version change only the\n> \"ambiguous\" case, simply because that was the only one I noticed in\n> my review (in other words, this may still not be sufficient)?\n>\n> Just double checking.\n>\n> The changes to existing tests have become a lot less noisy, compared\n> to the previous one, which is probably a good thing.\n\nI guess I should stop reading my inbox in reverse order.  In your\nreply to my v3 review you said you studied all the codepaths from\nparse_short_opt() and parse_long_opt() and addition of -3 was needed\nonly for the \"ambiguous\" case, so the answer to my question above is\n\"I was just lucky and happened to have hit the only problematic\ncase\" ;-)\n\nWill revert what is in 'next' and queue this one.\n\nThanks.\n"},{"id":"341308","messageId":"CAN0heSrnERYggyJ8bL1nAV=X2JQYS8aOkc6nWZhnZu3oqhzAfA@mail.gmail.com","threadId":"47992","inReplyTo":"20180306193116.23876-1-ungureanupaulsebastian@gmail.com","subject":"Re: [GSoC][PATCH v4] Make options that expect object ids less chatty if id is invalid","fromName":"Martin Ågren","fromEmail":"martin.agren@gmail.com","sentAt":"2018-03-08T19:27:01Z","receivedAt":"2018-03-08T19:27:07Z","isPatch":true,"sender":{"key":"martin.agren@gmail.com","avatar":null},"body":"Hi Paul-Sebastian\n\nOn 6 March 2018 at 20:31, Paul-Sebastian Ungureanu\n<ungureanupaulsebastian@gmail.com> wrote:\n> Usually, the usage should be shown only if the user does not know what\n> options are available. If the user specifies an invalid value, the user\n> is already aware of the available options. In this case, there is no\n> point in displaying the usage anymore.\n>\n> This patch applies to \"git tag --contains\", \"git branch --contains\",\n> \"git branch --points-at\", \"git for-each-ref --contains\" and many more.\n\nThanks for scratching this itch.\n\n>  8 files changed, 110 insertions(+), 14 deletions(-)\n>  create mode 100755 t/tcontains.sh\n>\n> diff --git a/builtin/blame.c b/builtin/blame.c\n> index 9dcb367b9..a5fbec8fb 100644\n> --- a/builtin/blame.c\n> +++ b/builtin/blame.c\n> @@ -728,6 +728,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n>                             PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);\n>         for (;;) {\n>                 switch (parse_options_step(&ctx, options, blame_opt_usage)) {\n> +               case PARSE_OPT_ERROR:\n>                 case PARSE_OPT_HELP:\n>                         exit(129);\n>                 case PARSE_OPT_DONE:\n> diff --git a/builtin/shortlog.c b/builtin/shortlog.c\n> index e29875b84..0789e2eea 100644\n> --- a/builtin/shortlog.c\n> +++ b/builtin/shortlog.c\n> @@ -282,6 +282,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)\n>\n>         for (;;) {\n>                 switch (parse_options_step(&ctx, options, shortlog_usage)) {\n> +               case PARSE_OPT_ERROR:\n>                 case PARSE_OPT_HELP:\n>                         exit(129);\n>                 case PARSE_OPT_DONE:\n> diff --git a/builtin/update-index.c b/builtin/update-index.c\n> index 58d1c2d28..34adf55a7 100644\n> --- a/builtin/update-index.c\n> +++ b/builtin/update-index.c\n> @@ -1059,6 +1059,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)\n>                         break;\n>                 switch (parseopt_state) {\n>                 case PARSE_OPT_HELP:\n> +               case PARSE_OPT_ERROR:\n>                         exit(129);\n>                 case PARSE_OPT_NON_OPTION:\n>                 case PARSE_OPT_DONE:\n\n> diff --git a/parse-options.c b/parse-options.c\n> index d02eb8b01..47c09a82b 100644\n> --- a/parse-options.c\n> +++ b/parse-options.c\n[...]\n>  int parse_options_end(struct parse_opt_ctx_t *ctx)\n> @@ -539,6 +540,7 @@ int parse_options(int argc, const char **argv, const char *prefix,\n>         parse_options_start(&ctx, argc, argv, prefix, options, flags);\n>         switch (parse_options_step(&ctx, options, usagestr)) {\n>         case PARSE_OPT_HELP:\n> +       case PARSE_OPT_ERROR:\n>                 exit(129);\n>         case PARSE_OPT_NON_OPTION:\n>         case PARSE_OPT_DONE:\n\nThese are very slightly inconsistent with the ordering of\nPARSE_OPT_ERROR and PARSE_OPT_HELP. I don't think it matters much. ;-)\n\n> diff --git a/t/tcontains.sh b/t/tcontains.sh\n> new file mode 100755\n> index 000000000..4856111ff\n> --- /dev/null\n> +++ b/t/tcontains.sh\n\nThis filename is not on the usual form, t1234-foo. As a result, it it is\nnot picked up by `make test`, so isn't actually exercised. I believe you\nselected this name based on the feedback in [1], but I do not think that\nthis (\"tcontains.sh\") was what Junio had in mind. Running the script\nmanually succeeds though. :-)\n\n> @@ -0,0 +1,92 @@\n> +#!/bin/sh\n> +\n> +test_description='Test \"contains\" argument behavior'\n\nOk, that matches the file name.\n\n> +test_expect_success 'setup ' '\n> +       git init . &&\n> +       echo \"this is a test\" >file &&\n> +       git add -A &&\n> +       git commit -am \"tag test\" &&\n> +       git tag \"v1.0\" &&\n> +       git tag \"v1.1\"\n> +'\n\nYou might find `test_commit` helpful. All in all, this could be a\ntwo-liner (this example is white-space mangled though):\n\ntest_expect_success 'setup ' '\n        test_commit \"v1.0\" &&\n        git tag \"v1.1\"\n'\n\n> +test_expect_success 'tag usage error' '\n> +       test_must_fail git tag --noopt 2>actual &&\n> +       test_i18ngrep \"usage\" actual\n> +'\n\nHmm, this one and a couple like it do not match the filename or test\ndescription. I wonder if these are needed at all, or if some checks like\nthese are already done elsewhere (maybe not for \"git tag\", but for some\nother user of the option-parsing machinery).\n\nI hope you find this useful.\n\nMartin\n\n[1] https://public-inbox.org/git/xmqqinar1bq2.fsf@gitster-ct.c.googlers.com/\n"},{"id":"342224","messageId":"1521485443.5987.1.camel@gmail.com","threadId":"47992","inReplyTo":"CAN0heSrnERYggyJ8bL1nAV=X2JQYS8aOkc6nWZhnZu3oqhzAfA@mail.gmail.com","subject":"Re: [GSoC][PATCH v4] Make options that expect object ids less chatty if id is invalid","fromName":"","fromEmail":"ungureanupaulsebastian@gmail.com","sentAt":"2018-03-19T18:50:43Z","receivedAt":"2018-03-19T18:51:04Z","isPatch":true,"sender":{"key":"ungureanupaulsebastian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/24317622?v=4"},"body":"Hello,\n\nThank you for your advice! Soon enough, I wil submit a new patch which\nfixes the issues you mentioned.\n\nBest regards,\nPaul Ungureanu\n"}]}