{"thread":{"id":"57287","subject":"[PATCH 0/4] Factorize i18n","startedAt":"2022-01-22T18:36:00Z","lastAt":"2022-02-02T17:29:40Z","messageCount":41,"participants":["Jean-Noël Avila via GitGitGadget","Johannes Sixt","Phillip Wood","Jean-Noël AVILA","Junio C Hamano","Jean-Noël Avila","Ævar Arnfjörð Bjarmason"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"446694","messageId":"pull.1123.git.1642876553.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":null,"subject":"[PATCH 0/4] Factorize i18n","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-22T18:35:49Z","receivedAt":"2022-01-22T18:36:00Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"This is a small series of i18n factorizations. Again the idea is to:\n\n * reduce the number of strings to translate\n * make life easier for translators by removing constant parts of the\n   strings to translate\n * uniformize the UI by using a type of message for the a given type of\n   condition.\n\nJean-Noël Avila (4):\n  i18n: factorize more 'incompatible options' messages\n  i18n: factorize \"invalid value\" messages\n  i18n: remove from i18n strings that do not hold translatable parts\n  i18n: transfer variables into placeholders in command synopsis\n\n archive.c                                 |  2 +-\n builtin/am.c                              |  7 ++--\n builtin/bisect--helper.c                  |  6 ++--\n builtin/blame.c                           |  6 ++--\n builtin/commit.c                          | 39 ++++++++++++++---------\n builtin/count-objects.c                   |  2 +-\n builtin/difftool.c                        | 18 +++++++++--\n builtin/fast-export.c                     |  2 +-\n builtin/fetch.c                           |  4 +--\n builtin/grep.c                            |  4 +--\n builtin/hash-object.c                     |  2 +-\n builtin/help.c                            |  4 +--\n builtin/log.c                             | 20 ++++++++++--\n builtin/merge-base.c                      |  4 +--\n builtin/mktag.c                           |  2 +-\n builtin/mktree.c                          |  2 +-\n builtin/notes.c                           |  6 ++--\n builtin/pack-objects.c                    |  2 +-\n builtin/prune-packed.c                    |  2 +-\n builtin/pull.c                            |  6 ++--\n builtin/push.c                            |  2 +-\n builtin/rebase.c                          |  2 +-\n builtin/reflog.c                          |  6 ++--\n builtin/remote.c                          |  2 +-\n builtin/replace.c                         |  2 +-\n builtin/rev-list.c                        |  2 +-\n builtin/send-pack.c                       |  2 +-\n builtin/sparse-checkout.c                 |  8 ++---\n builtin/stripspace.c                      |  4 +--\n builtin/submodule--helper.c               |  2 +-\n builtin/update-server-info.c              |  2 +-\n diff-merges.c                             |  2 +-\n gpg-interface.c                           |  4 +--\n ls-refs.c                                 |  2 +-\n parallel-checkout.c                       |  3 +-\n sequencer.c                               |  2 +-\n setup.c                                   |  2 +-\n submodule-config.c                        |  2 +-\n t/t4150-am.sh                             |  2 +-\n t/t7500-commit-template-squash-signoff.sh |  2 +-\n 40 files changed, 118 insertions(+), 77 deletions(-)\n\n\nbase-commit: 90d242d36e248acfae0033274b524bfa55a947fd\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1123%2Fjnavila%2Ffactorize_i18n-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1123/jnavila/factorize_i18n-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/1123\n-- \ngitgitgadget\n"},{"id":"446695","messageId":"81ff928567fdf274000c660fb791641fc2ceccc9.1642876553.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.git.1642876553.gitgitgadget@gmail.com","subject":"[PATCH 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-22T18:35:50Z","receivedAt":"2022-01-22T18:36:02Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nWhen more than two options are mutually exclusive, print the ones\nwhich are actually on the command line.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/commit.c                          | 39 ++++++++++++++---------\n builtin/difftool.c                        | 18 +++++++++--\n builtin/grep.c                            |  4 +--\n builtin/log.c                             | 20 ++++++++++--\n builtin/merge-base.c                      |  4 +--\n t/t7500-commit-template-squash-signoff.sh |  2 +-\n 6 files changed, 63 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex b9ed0374e30..910f4c912bf 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1243,6 +1243,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\t\t\t      struct wt_status *s)\n {\n \tint f = 0;\n+\tchar * f_options[4];\n \n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \tfinalize_deferred_config(s);\n@@ -1251,7 +1252,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tforce_author = find_author_by_nickname(force_author);\n \n \tif (force_author && renew_authorship)\n-\t\tdie(_(\"Using both --reset-author and --author does not make sense\"));\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--reset-author\", \"--author\");\n \n \tif (logfile || have_option_m || use_message)\n \t\tuse_editor = 0;\n@@ -1268,19 +1269,19 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\t\tdie(_(\"You are in the middle of a rebase -- cannot amend.\"));\n \t}\n \tif (fixup_message && squash_message)\n-\t\tdie(_(\"Options --squash and --fixup cannot be used together\"));\n-\tif (use_message)\n-\t\tf++;\n-\tif (edit_message)\n-\t\tf++;\n-\tif (fixup_message)\n-\t\tf++;\n-\tif (logfile)\n-\t\tf++;\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n+\tf_options[f] = \"-C\";\n+\tf+=\t!!use_message;\n+\tf_options[f] = \"-c\";\n+\tf+=!!edit_message;\n+\tf_options[f] = \"-F\";\n+\tf+=!!logfile;\n+\tf_options[f] = \"--fixup\";\n+\tf+=!!fixup_message;\n \tif (f > 1)\n-\t\tdie(_(\"Only one of -c/-C/-F/--fixup can be used.\"));\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n \tif (have_option_m && (edit_message || use_message || logfile))\n-\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", f_options[0]);\n \tif (f || have_option_m)\n \t\ttemplate_file = NULL;\n \tif (edit_message)\n@@ -1305,9 +1306,17 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \n \tif (patch_interactive)\n \t\tinteractive = 1;\n-\n-\tif (also + only + all + interactive > 1)\n-\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n+\tf = 0;\n+\tf_options[f] = \"-i/--include\";\n+\tf += also;\n+\tf_options[f] = \"-o/--only\";\n+\tf += only;\n+\tf_options[f] = \"-a/--all\";\n+\tf += all;\n+\tf_options[f] = \"--interactive/-p/--patch\";\n+\tf += interactive;\n+\tif (f > 1)\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n \n \tif (fixup_message) {\n \t\t/*\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex c79fbbf67e5..92854bc7737 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -685,6 +685,8 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n \tint use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,\n \t    tool_help = 0, no_index = 0;\n \tstatic char *difftool_cmd = NULL, *extcmd = NULL;\n+\tint f = 0;\n+\tchar *f_options[3];\n \tstruct option builtin_difftool_options[] = {\n \t\tOPT_BOOL('g', \"gui\", &use_gui_tool,\n \t\t\t N_(\"use `diff.guitool` instead of `diff.tool`\")),\n@@ -732,8 +734,20 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n \t} else if (dir_diff)\n \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--dir-diff\", \"--no-index\");\n \n-\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n-\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n+\tif (use_gui_tool) {\n+\t\tf_options[f] = \"--gui\";\n+\t\tf++;\n+\t}\n+\tif (difftool_cmd) {\n+\t\tf_options[f] = \"--tool\";\n+\t\tf++;\n+\t}\n+\tif (extcmd) {\n+\t\tf_options[f] = \"--extcmd\";\n+\t\tf++;\n+\t}\n+\tif (f > 1)\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n \n \tif (use_gui_tool)\n \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 9e34a820ad4..b199781cb27 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -1168,10 +1168,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \t\tsetup_pager();\n \n \tif (!use_index && (untracked || cached))\n-\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--cached/--untracked\", \"--no-index\");\n \n \tif (untracked && cached)\n-\t\tdie(_(\"--untracked cannot be used with --cached\"));\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--untracked\", \"--cached\");\n \n \tif (!use_index || untracked) {\n \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 4b493408cc5..59b4a2fd380 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1759,6 +1759,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstruct strbuf rdiff_title = STRBUF_INIT;\n \tint creation_factor = -1;\n \n+\tint f = 0;\n+\tchar * f_options[4];\n+\n \tconst struct option builtin_format_patch_options[] = {\n \t\tOPT_CALLBACK_F('n', \"numbered\", &numbered, NULL,\n \t\t\t    N_(\"use [PATCH n/m] even with a single patch\"),\n@@ -1978,8 +1981,21 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.show_notes)\n \t\tload_display_notes(&rev.notes_opt);\n \n-\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n-\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n+\tif (use_stdout) {\n+\t\tf_options[f] = \"--stdout\";\n+\t\tf++;\n+\t}\n+\tif (rev.diffopt.close_file) {\n+\t\tf_options[f] = \"--output\";\n+\t\tf++;\n+\t}\n+\tif (output_directory) {\n+\t\tf_options[f] = \"--output-directory\";\n+\t\tf++;\n+\t}\n+\n+\tif (f > 1)\n+\t\tdie(_(\"options '%s'and '%s' cannot be used together\"), f_options[0], f_options[1]);\n \n \tif (use_stdout) {\n \t\tsetup_pager();\ndiff --git a/builtin/merge-base.c b/builtin/merge-base.c\nindex 6719ac198dc..1447f1c493a 100644\n--- a/builtin/merge-base.c\n+++ b/builtin/merge-base.c\n@@ -159,12 +159,12 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)\n \t\tif (argc < 2)\n \t\t\tusage_with_options(merge_base_usage, options);\n \t\tif (show_all)\n-\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--is-ancestor\", \"--all\");\n \t\treturn handle_is_ancestor(argc, argv);\n \t}\n \n \tif (cmdmode == 'r' && show_all)\n-\t\tdie(\"--independent cannot be used with --all\");\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--independent\", \"--all\");\n \n \tif (cmdmode == 'o')\n \t\treturn handle_octopus(argc, argv, show_all);\ndiff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\nindex 91964653a0b..5fcaa0b4f2a 100755\n--- a/t/t7500-commit-template-squash-signoff.sh\n+++ b/t/t7500-commit-template-squash-signoff.sh\n@@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '\n '\n \n test_expect_success '--fixup=reword: -F give error message' '\n-\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n+\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used together\" >expect &&\n \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n \ttest_cmp expect actual\n '\n-- \ngitgitgadget\n\n"},{"id":"446696","messageId":"ca48e82a6b4a2581c815f796a303d411c27a0283.1642876553.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.git.1642876553.gitgitgadget@gmail.com","subject":"[PATCH 2/4] i18n: factorize \"invalid value\" messages","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-22T18:35:51Z","receivedAt":"2022-01-22T18:36:02Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nUse the same message when an invalid value is passed to a command line\noption or a configuration variable.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/am.c           | 7 ++++---\n builtin/blame.c        | 6 +++---\n builtin/fetch.c        | 4 ++--\n builtin/pack-objects.c | 2 +-\n builtin/pull.c         | 6 +++---\n builtin/push.c         | 2 +-\n builtin/send-pack.c    | 2 +-\n diff-merges.c          | 2 +-\n gpg-interface.c        | 4 ++--\n ls-refs.c              | 2 +-\n parallel-checkout.c    | 3 ++-\n sequencer.c            | 2 +-\n setup.c                | 2 +-\n submodule-config.c     | 2 +-\n t/t4150-am.sh          | 2 +-\n 15 files changed, 25 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex b6be1f1cb11..ba1dacbc034 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -199,7 +199,7 @@ static int am_option_parse_empty(const struct option *opt,\n \telse if (!strcmp(arg, \"keep\"))\n \t\t*opt_value = KEEP_EMPTY_COMMIT;\n \telse\n-\t\treturn error(_(\"Invalid value for --empty: %s\"), arg);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n \n \treturn 0;\n }\n@@ -2239,7 +2239,7 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int\n \t * when you add new options\n \t */\n \telse\n-\t\treturn error(_(\"Invalid value for --patch-format: %s\"), arg);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--patch-format\", arg);\n \treturn 0;\n }\n \n@@ -2282,7 +2282,8 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar\n \t\t\t\tbreak;\n \t\t}\n \t\tif (new_value >= ARRAY_SIZE(valid_modes))\n-\t\t\treturn error(_(\"Invalid value for --show-current-patch: %s\"), arg);\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\t\t \"--show-current-patch\", arg);\n \t}\n \n \tif (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 7fafeac4081..ca821420d68 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -721,8 +721,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!strcmp(var, \"color.blame.repeatedlines\")) {\n \t\tif (color_parse_mem(value, strlen(value), repeated_meta_color))\n-\t\t\twarning(_(\"invalid color '%s' in color.blame.repeatedLines\"),\n-\t\t\t\tvalue);\n+\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\t\"color.blame.repeatedLines\", value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"color.blame.highlightrecent\")) {\n@@ -739,7 +739,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t\t\tcoloring_mode &= ~(OUTPUT_COLOR_LINE |\n \t\t\t\t\t    OUTPUT_SHOW_AGE_WITH_COLOR);\n \t\t} else {\n-\t\t\twarning(_(\"invalid value for blame.coloring\"));\n+\t\t\twarning(_(\"invalid value for '%s': '%s'\"), \"blame.coloring\", value);\n \t\t\treturn 0;\n \t\t}\n \t}\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex eaab8056bf9..19ec48f3330 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -758,8 +758,8 @@ static void prepare_format_display(struct ref *ref_map)\n \telse if (!strcasecmp(format, \"compact\"))\n \t\tcompact_format = 1;\n \telse\n-\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n-\t\t    format);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t  \"fetch.output\", format);\n \n \tfor (rm = ref_map; rm; rm = rm->next) {\n \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex ba2006f2212..192c3ca305e 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3504,7 +3504,7 @@ static int option_parse_missing_action(const struct option *opt,\n \t\treturn 0;\n \t}\n \n-\tdie(_(\"invalid value for --missing\"));\n+\tdie(_(\"invalid value for '%s': '%s'\"), \"--missing\", arg);\n \treturn 0;\n }\n \ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 100cbf9fb85..e54a0ccadc6 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -42,9 +42,9 @@ static enum rebase_type parse_config_rebase(const char *key, const char *value,\n \t\treturn v;\n \n \tif (fatal)\n-\t\tdie(_(\"Invalid value for %s: %s\"), key, value);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"), key, value);\n \telse\n-\t\terror(_(\"Invalid value for %s: %s\"), key, value);\n+\t\terror(_(\"invalid value for '%s': '%s'\"), key, value);\n \n \treturn REBASE_INVALID;\n }\n@@ -318,7 +318,7 @@ static const char *config_get_ff(void)\n \tif (!strcmp(value, \"only\"))\n \t\treturn \"--ff-only\";\n \n-\tdie(_(\"Invalid value for pull.ff: %s\"), value);\n+\tdie(_(\"invalid value for '%s': '%s'\"), \"pull.ff\", value);\n }\n \n /**\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 359db90321c..cad997965a7 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -486,7 +486,7 @@ static int git_push_config(const char *k, const char *v, void *cb)\n \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n \t\t\t\t\tset_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);\n \t\t\t\telse\n-\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n+\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n \t\t\t}\n \t\t}\n \t} else if (!strcmp(k, \"push.recursesubmodules\")) {\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex 69c432ef1a6..64962be0168 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -145,7 +145,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)\n \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n \t\t\t\t\targs.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;\n \t\t\t\telse\n-\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n+\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n \t\t\t}\n \t\t}\n \t}\ndiff --git a/diff-merges.c b/diff-merges.c\nindex 5060ccd890b..cd6c102a0d5 100644\n--- a/diff-merges.c\n+++ b/diff-merges.c\n@@ -67,7 +67,7 @@ static void set_diff_merges(struct rev_info *revs, const char *optarg)\n \tdiff_merges_setup_func_t func = func_by_opt(optarg);\n \n \tif (!func)\n-\t\tdie(_(\"unknown value for --diff-merges: %s\"), optarg);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"), \"--diff-merges\", optarg);\n \n \tfunc(revs);\n \ndiff --git a/gpg-interface.c b/gpg-interface.c\nindex b52eb0e2e04..04d751a16a8 100644\n--- a/gpg-interface.c\n+++ b/gpg-interface.c\n@@ -702,7 +702,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n \t\t\treturn config_error_nonbool(var);\n \t\tfmt = get_format_by_name(value);\n \t\tif (!fmt)\n-\t\t\treturn error(\"unsupported value for %s: %s\",\n+\t\t\treturn error(\"invalid value for '%s': '%s'\",\n \t\t\t\t     var, value);\n \t\tuse_format = fmt;\n \t\treturn 0;\n@@ -717,7 +717,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n \t\tfree(trust);\n \n \t\tif (ret)\n-\t\t\treturn error(\"unsupported value for %s: %s\", var,\n+\t\t\treturn error(\"invalid value for '%s': '%s'\", var,\n \t\t\t\t     value);\n \t\treturn 0;\n \t}\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 54078323dcb..ae09dd59478 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -34,7 +34,7 @@ static void ensure_config_read(void)\n \t\t} else if (!strcmp(str, \"ignore\")) {\n \t\t\t/* do nothing */\n \t\t} else {\n-\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n+\t\t\tdie(_(\"invalid value for '%s': '%s'\"), \"lsrefs.unborn\", str);\n \t\t}\n \t}\n \tconfig_read = 1;\ndiff --git a/parallel-checkout.c b/parallel-checkout.c\nindex 8dd7e7bad40..237c02f1d81 100644\n--- a/parallel-checkout.c\n+++ b/parallel-checkout.c\n@@ -39,7 +39,8 @@ void get_parallel_checkout_configs(int *num_workers, int *threshold)\n \n \tif (env_workers && *env_workers) {\n \t\tif (strtol_i(env_workers, 10, num_workers)) {\n-\t\t\tdie(\"invalid value for GIT_TEST_CHECKOUT_WORKERS: '%s'\",\n+\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\"GIT_TEST_CHECKOUT_WORKERS\",\n \t\t\t    env_workers);\n \t\t}\n \t\tif (*num_workers < 1)\ndiff --git a/sequencer.c b/sequencer.c\nindex 6abd72160cc..9b985e8648d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2806,7 +2806,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\treturn error(_(\"invalid key: %s\"), key);\n \n \tif (!error_flag)\n-\t\treturn error(_(\"invalid value for %s: %s\"), key, value);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), key, value);\n \n \treturn 0;\n }\ndiff --git a/setup.c b/setup.c\nindex af3b8c09abe..fe74092a022 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -559,7 +559,7 @@ static enum extension_result handle_extension(const char *var,\n \t\t\treturn config_error_nonbool(var);\n \t\tformat = hash_algo_by_name(value);\n \t\tif (format == GIT_HASH_UNKNOWN)\n-\t\t\treturn error(\"invalid value for 'extensions.objectformat'\");\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"extensions.objectformat\", value);\n \t\tdata->hash_algo = format;\n \t\treturn EXTENSION_OK;\n \t}\ndiff --git a/submodule-config.c b/submodule-config.c\nindex f95344028b5..fb95a026f48 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -496,7 +496,7 @@ static int parse_config(const char *var, const char *value, void *data)\n \t\telse if (parse_submodule_update_strategy(value,\n \t\t\t &submodule->update_strategy) < 0 ||\n \t\t\t submodule->update_strategy.type == SM_UPDATE_COMMAND)\n-\t\t\tdie(_(\"invalid value for %s\"), var);\n+\t\t\tdie(_(\"invalid value for '%s'\"), var);\n \t} else if (!strcmp(item.buf, \"shallow\")) {\n \t\tif (!me->overwrite && submodule->recommend_shallow != -1)\n \t\t\twarn_multiple_config(me->treeish_name, submodule->name,\ndiff --git a/t/t4150-am.sh b/t/t4150-am.sh\nindex 6caff0ca397..159fae8d016 100755\n--- a/t/t4150-am.sh\n+++ b/t/t4150-am.sh\n@@ -1169,7 +1169,7 @@ test_expect_success 'invalid when passing the --empty option alone' '\n \ttest_when_finished \"git am --abort || :\" &&\n \tgit checkout empty-commit^ &&\n \ttest_must_fail git am --empty empty-commit.patch 2>err &&\n-\techo \"error: Invalid value for --empty: empty-commit.patch\" >expected &&\n+\techo \"error: invalid value for '\\''--empty'\\'': '\\''empty-commit.patch'\\''\" >expected &&\n \ttest_cmp expected err\n '\n \n-- \ngitgitgadget\n\n"},{"id":"446697","messageId":"643be0d82de11a286229d81ad96638b155e57a40.1642876553.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.git.1642876553.gitgitgadget@gmail.com","subject":"[PATCH 4/4] i18n: transfer variables into placeholders in command synopsis","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-22T18:35:53Z","receivedAt":"2022-01-22T18:36:05Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nThis applies the style guide for documentation.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/fast-export.c | 2 +-\n builtin/reflog.c      | 4 ++--\n builtin/rev-list.c    | 2 +-\n 3 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 9f1c730e587..510139e9b54 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -26,7 +26,7 @@\n #include \"commit-slab.h\"\n \n static const char *fast_export_usage[] = {\n-\tN_(\"git fast-export [rev-list-opts]\"),\n+\tN_(\"git fast-export [<rev-list-opts>]\"),\n \tNULL\n };\n \ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex ee5ee8d8cf6..343a10d371b 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -17,10 +17,10 @@ static const char reflog_expire_usage[] =\n N_(\"git reflog expire [--expire=<time>] \"\n    \"[--expire-unreachable=<time>] \"\n    \"[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] \"\n-   \"[--verbose] [--all] <refs>...\");\n+   \"[--verbose] [--all] <ref>...\");\n static const char reflog_delete_usage[] =\n N_(\"git reflog delete [--rewrite] [--updateref] \"\n-   \"[--dry-run | -n] [--verbose] <refs>...\");\n+   \"[--dry-run | -n] [--verbose] <ref>...\");\n static const char reflog_exists_usage[] =\n N_(\"git reflog exists <ref>\");\n \ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 777558e9b06..38528c7f156 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -20,7 +20,7 @@\n #include \"packfile.h\"\n \n static const char rev_list_usage[] =\n-\"git rev-list [OPTION] <commit-id>... [ -- paths... ]\\n\"\n+\"git rev-list [<options>] <commit-id>... [-- <path>...]\\n\"\n \"  limiting output:\\n\"\n \"    --max-count=<n>\\n\"\n \"    --max-age=<epoch>\\n\"\n-- \ngitgitgadget\n"},{"id":"446698","messageId":"ead191faad9cfda1929f5a2d1566a8033f3e2297.1642876553.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.git.1642876553.gitgitgadget@gmail.com","subject":"[PATCH 3/4] i18n: remove from i18n strings that do not hold translatable parts","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-22T18:35:52Z","receivedAt":"2022-01-22T18:36:06Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n archive.c                    | 2 +-\n builtin/bisect--helper.c     | 6 +++---\n builtin/count-objects.c      | 2 +-\n builtin/hash-object.c        | 2 +-\n builtin/help.c               | 4 ++--\n builtin/mktag.c              | 2 +-\n builtin/mktree.c             | 2 +-\n builtin/notes.c              | 6 +++---\n builtin/prune-packed.c       | 2 +-\n builtin/rebase.c             | 2 +-\n builtin/reflog.c             | 2 +-\n builtin/remote.c             | 2 +-\n builtin/replace.c            | 2 +-\n builtin/sparse-checkout.c    | 8 ++++----\n builtin/stripspace.c         | 4 ++--\n builtin/submodule--helper.c  | 2 +-\n builtin/update-server-info.c | 2 +-\n 17 files changed, 26 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex d571249cf39..e29d0e00f6c 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -12,7 +12,7 @@\n \n static char const * const archive_usage[] = {\n \tN_(\"git archive [<options>] <tree-ish> [<path>...]\"),\n-\tN_(\"git archive --list\"),\n+\t\"git archive --list\",\n \tN_(\"git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]\"),\n \tN_(\"git archive --remote <repo> [--exec <cmd>] --list\"),\n \tNULL\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 28a2e6a5750..f962dbd4303 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -22,15 +22,15 @@ static GIT_PATH_FUNC(git_path_bisect_run, \"BISECT_RUN\")\n \n static const char * const git_bisect_helper_usage[] = {\n \tN_(\"git bisect--helper --bisect-reset [<commit>]\"),\n-\tN_(\"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]\"),\n+\t\"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]\",\n \tN_(\"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]\"\n \t\t\t\t\t    \" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]\"),\n-\tN_(\"git bisect--helper --bisect-next\"),\n+\t\"git bisect--helper --bisect-next\",\n \tN_(\"git bisect--helper --bisect-state (bad|new) [<rev>]\"),\n \tN_(\"git bisect--helper --bisect-state (good|old) [<rev>...]\"),\n \tN_(\"git bisect--helper --bisect-replay <filename>\"),\n \tN_(\"git bisect--helper --bisect-skip [(<rev>|<range>)...]\"),\n-\tN_(\"git bisect--helper --bisect-visualize\"),\n+\t\"git bisect--helper --bisect-visualize\",\n \tN_(\"git bisect--helper --bisect-run <cmd>...\"),\n \tNULL\n };\ndiff --git a/builtin/count-objects.c b/builtin/count-objects.c\nindex 3fae474f6f1..07b94195962 100644\n--- a/builtin/count-objects.c\n+++ b/builtin/count-objects.c\n@@ -87,7 +87,7 @@ static int print_alternate(struct object_directory *odb, void *data)\n }\n \n static char const * const count_objects_usage[] = {\n-\tN_(\"git count-objects [-v] [-H | --human-readable]\"),\n+\t\"git count-objects [-v] [-H | --human-readable]\",\n \tNULL\n };\n \ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex c7b3ad74c60..04e2442ec7c 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -81,7 +81,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n {\n \tstatic const char * const hash_object_usage[] = {\n \t\tN_(\"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>...\"),\n-\t\tN_(\"git hash-object  --stdin-paths\"),\n+\t\t\"git hash-object  --stdin-paths\",\n \t\tNULL\n \t};\n \tconst char *type = blob_type;\ndiff --git a/builtin/help.c b/builtin/help.c\nindex d387131dd83..b4f2ad3f94e 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -77,8 +77,8 @@ static struct option builtin_help_options[] = {\n static const char * const builtin_help_usage[] = {\n \tN_(\"git help [-a|--all] [--[no-]verbose]]\\n\"\n \t   \"         [[-i|--info] [-m|--man] [-w|--web]] [<command>]\"),\n-\tN_(\"git help [-g|--guides]\"),\n-\tN_(\"git help [-c|--config]\"),\n+\t\"git help [-g|--guides]\",\n+\t\"git help [-c|--config]\",\n \tNULL\n };\n \ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 3b2dbbb37e6..c7b905c614b 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -7,7 +7,7 @@\n #include \"config.h\"\n \n static char const * const builtin_mktag_usage[] = {\n-\tN_(\"git mktag\"),\n+\t\"git mktag\",\n \tNULL\n };\n static int option_strict = 1;\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex ae78ca1c029..8bdaada922a 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -63,7 +63,7 @@ static void write_tree(struct object_id *oid)\n }\n \n static const char *mktree_usage[] = {\n-\tN_(\"git mktree [-z] [--missing] [--batch]\"),\n+\t\"git mktree [-z] [--missing] [--batch]\",\n \tNULL\n };\n \ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 05d60483e82..f99593a1853 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -32,8 +32,8 @@ static const char * const git_notes_usage[] = {\n \tN_(\"git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]\"),\n \tN_(\"git notes [--ref <notes-ref>] show [<object>]\"),\n \tN_(\"git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>\"),\n-\tN_(\"git notes merge --commit [-v | -q]\"),\n-\tN_(\"git notes merge --abort [-v | -q]\"),\n+\t\"git notes merge --commit [-v | -q]\",\n+\t\"git notes merge --abort [-v | -q]\",\n \tN_(\"git notes [--ref <notes-ref>] remove [<object>...]\"),\n \tN_(\"git notes [--ref <notes-ref>] prune [-n] [-v]\"),\n \tN_(\"git notes [--ref <notes-ref>] get-ref\"),\n@@ -89,7 +89,7 @@ static const char * const git_notes_prune_usage[] = {\n };\n \n static const char * const git_notes_get_ref_usage[] = {\n-\tN_(\"git notes get-ref\"),\n+\t\"git notes get-ref\",\n \tNULL\n };\n \ndiff --git a/builtin/prune-packed.c b/builtin/prune-packed.c\nindex b7b9281a8ce..da3273a268b 100644\n--- a/builtin/prune-packed.c\n+++ b/builtin/prune-packed.c\n@@ -3,7 +3,7 @@\n #include \"prune-packed.h\"\n \n static const char * const prune_packed_usage[] = {\n-\tN_(\"git prune-packed [-n | --dry-run] [-q | --quiet]\"),\n+\t\"git prune-packed [-n | --dry-run] [-q | --quiet]\",\n \tNULL\n };\n \ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 36490d06c8a..64796c6a785 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -36,7 +36,7 @@ static char const * const builtin_rebase_usage[] = {\n \t\t\"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]\"),\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] \"\n \t\t\"--root [<branch>]\"),\n-\tN_(\"git rebase --continue | --abort | --skip | --edit-todo\"),\n+\t\"git rebase --continue | --abort | --skip | --edit-todo\",\n \tNULL\n };\n \ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex a4b1dd27e13..ee5ee8d8cf6 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -800,7 +800,7 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)\n  */\n \n static const char reflog_usage[] =\n-N_(\"git reflog [ show | expire | delete | exists ]\");\n+\"git reflog [ show | expire | delete | exists ]\";\n \n int cmd_reflog(int argc, const char **argv, const char *prefix)\n {\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 299c466116d..6f27ddc47bd 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -14,7 +14,7 @@\n #include \"commit-reach.h\"\n \n static const char * const builtin_remote_usage[] = {\n-\tN_(\"git remote [-v | --verbose]\"),\n+\t\"git remote [-v | --verbose]\",\n \tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n \tN_(\"git remote rename <old> <new>\"),\n \tN_(\"git remote remove <name>\"),\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 6ff1734d587..ac92337c0ec 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -22,7 +22,7 @@ static const char * const git_replace_usage[] = {\n \tN_(\"git replace [-f] <object> <replacement>\"),\n \tN_(\"git replace [-f] --edit <object>\"),\n \tN_(\"git replace [-f] --graft <commit> [<parent>...]\"),\n-\tN_(\"git replace [-f] --convert-graft-file\"),\n+\t\"git replace [-f] --convert-graft-file\",\n \tN_(\"git replace -d <object>...\"),\n \tN_(\"git replace [--format=<format>] [-l [<pattern>]]\"),\n \tNULL\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 679c1070368..771c9869a1c 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -43,7 +43,7 @@ static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)\n }\n \n static char const * const builtin_sparse_checkout_list_usage[] = {\n-\tN_(\"git sparse-checkout list\"),\n+\t\"git sparse-checkout list\",\n \tNULL\n };\n \n@@ -419,7 +419,7 @@ static int update_modes(int *cone_mode, int *sparse_index)\n }\n \n static char const * const builtin_sparse_checkout_init_usage[] = {\n-\tN_(\"git sparse-checkout init [--cone] [--[no-]sparse-index]\"),\n+\t\"git sparse-checkout init [--cone] [--[no-]sparse-index]\",\n \tNULL\n };\n \n@@ -762,7 +762,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)\n }\n \n static char const * const builtin_sparse_checkout_reapply_usage[] = {\n-\tN_(\"git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]\"),\n+\t\"git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]\",\n \tNULL\n };\n \n@@ -800,7 +800,7 @@ static int sparse_checkout_reapply(int argc, const char **argv)\n }\n \n static char const * const builtin_sparse_checkout_disable_usage[] = {\n-\tN_(\"git sparse-checkout disable\"),\n+\t\"git sparse-checkout disable\",\n \tNULL\n };\n \ndiff --git a/builtin/stripspace.c b/builtin/stripspace.c\nindex be33eb83c1b..1e34cf2bebd 100644\n--- a/builtin/stripspace.c\n+++ b/builtin/stripspace.c\n@@ -15,8 +15,8 @@ static void comment_lines(struct strbuf *buf)\n }\n \n static const char * const stripspace_usage[] = {\n-\tN_(\"git stripspace [-s | --strip-comments]\"),\n-\tN_(\"git stripspace [-c | --comment-lines]\"),\n+\t\"git stripspace [-s | --strip-comments]\",\n+\t\"git stripspace [-c | --comment-lines]\",\n \tNULL\n };\n \ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c5d3fc3817f..b80aa9898ac 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2883,7 +2883,7 @@ static int module_config(int argc, const char **argv, const char *prefix)\n \tconst char *const git_submodule_helper_usage[] = {\n \t\tN_(\"git submodule--helper config <name> [<value>]\"),\n \t\tN_(\"git submodule--helper config --unset <name>\"),\n-\t\tN_(\"git submodule--helper config --check-writeable\"),\n+\t\t\"git submodule--helper config --check-writeable\",\n \t\tNULL\n \t};\n \ndiff --git a/builtin/update-server-info.c b/builtin/update-server-info.c\nindex 4321a344567..880fffec587 100644\n--- a/builtin/update-server-info.c\n+++ b/builtin/update-server-info.c\n@@ -4,7 +4,7 @@\n #include \"parse-options.h\"\n \n static const char * const update_server_info_usage[] = {\n-\tN_(\"git update-server-info [--force]\"),\n+\t\"git update-server-info [--force]\",\n \tNULL\n };\n \n-- \ngitgitgadget\n\n"},{"id":"446736","messageId":"ba203623-87b1-f3eb-9498-52c4ea4ba07e@kdbg.org","threadId":"57287","inReplyTo":"81ff928567fdf274000c660fb791641fc2ceccc9.1642876553.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-01-24T07:14:20Z","receivedAt":"2022-01-24T07:14:24Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 22.01.22 um 19:35 schrieb Jean-Noël Avila via GitGitGadget:\n> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n> \n> When more than two options are mutually exclusive, print the ones\n> which are actually on the command line.\n\nReading this, I expect that all mutually exclusive options are listed in\nthe error messages. But the updated code lists only the first and second\noption even if more are on the command line. What is the justification\nfor that? Just \"make the work for translators easier\"? I am not 100%\nsure that this is the right balance. Wouldn't it be helpful for users to\nget to know which set of options is incompatible, or is an error message\nnot the right place for this kind of education? Don't know...\n\n> \n> Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>\n> ---\n>  builtin/commit.c                          | 39 ++++++++++++++---------\n>  builtin/difftool.c                        | 18 +++++++++--\n>  builtin/grep.c                            |  4 +--\n>  builtin/log.c                             | 20 ++++++++++--\n>  builtin/merge-base.c                      |  4 +--\n>  t/t7500-commit-template-squash-signoff.sh |  2 +-\n>  6 files changed, 63 insertions(+), 24 deletions(-)\n> \n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index b9ed0374e30..910f4c912bf 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1243,6 +1243,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>  \t\t\t\t      struct wt_status *s)\n>  {\n>  \tint f = 0;\n> +\tchar * f_options[4];\n>  \n>  \targc = parse_options(argc, argv, prefix, options, usage, 0);\n>  \tfinalize_deferred_config(s);\n> @@ -1251,7 +1252,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>  \t\tforce_author = find_author_by_nickname(force_author);\n>  \n>  \tif (force_author && renew_authorship)\n> -\t\tdie(_(\"Using both --reset-author and --author does not make sense\"));\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--reset-author\", \"--author\");\n>  \n>  \tif (logfile || have_option_m || use_message)\n>  \t\tuse_editor = 0;\n> @@ -1268,19 +1269,19 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>  \t\t\tdie(_(\"You are in the middle of a rebase -- cannot amend.\"));\n>  \t}\n>  \tif (fixup_message && squash_message)\n> -\t\tdie(_(\"Options --squash and --fixup cannot be used together\"));\n> -\tif (use_message)\n> -\t\tf++;\n> -\tif (edit_message)\n> -\t\tf++;\n> -\tif (fixup_message)\n> -\t\tf++;\n> -\tif (logfile)\n> -\t\tf++;\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n> +\tf_options[f] = \"-C\";\n> +\tf+=\t!!use_message;\n> +\tf_options[f] = \"-c\";\n> +\tf+=!!edit_message;\n> +\tf_options[f] = \"-F\";\n> +\tf+=!!logfile;\n> +\tf_options[f] = \"--fixup\";\n> +\tf+=!!fixup_message;\n\nThe format style violations are quite obvious here. But see below for a\nsuggestion to write this differently.\n\n>  \tif (f > 1)\n> -\t\tdie(_(\"Only one of -c/-C/-F/--fixup can be used.\"));\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n>  \tif (have_option_m && (edit_message || use_message || logfile))\n> -\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", f_options[0]);\n>  \tif (f || have_option_m)\n>  \t\ttemplate_file = NULL;\n>  \tif (edit_message)\n> @@ -1305,9 +1306,17 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>  \n>  \tif (patch_interactive)\n>  \t\tinteractive = 1;\n> -\n> -\tif (also + only + all + interactive > 1)\n> -\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n> +\tf = 0;\n> +\tf_options[f] = \"-i/--include\";\n> +\tf += also;\n> +\tf_options[f] = \"-o/--only\";\n> +\tf += only;\n> +\tf_options[f] = \"-a/--all\";\n> +\tf += all;\n> +\tf_options[f] = \"--interactive/-p/--patch\";\n\nInteresting. -p and --interactive do different things, but can be used\ntogether (-p overrides --interactive). The original error message\nsuggests that this is not the case. Just a marginal note...\n\n> +\tf += interactive;\n> +\tif (f > 1)\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n>  \n>  \tif (fixup_message) {\n>  \t\t/*\n> diff --git a/builtin/difftool.c b/builtin/difftool.c\n> index c79fbbf67e5..92854bc7737 100644\n> --- a/builtin/difftool.c\n> +++ b/builtin/difftool.c\n> @@ -685,6 +685,8 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n>  \tint use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,\n>  \t    tool_help = 0, no_index = 0;\n>  \tstatic char *difftool_cmd = NULL, *extcmd = NULL;\n> +\tint f = 0;\n> +\tchar *f_options[3];\n>  \tstruct option builtin_difftool_options[] = {\n>  \t\tOPT_BOOL('g', \"gui\", &use_gui_tool,\n>  \t\t\t N_(\"use `diff.guitool` instead of `diff.tool`\")),\n> @@ -732,8 +734,20 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n>  \t} else if (dir_diff)\n>  \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--dir-diff\", \"--no-index\");\n>  \n> -\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n> -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n> +\tif (use_gui_tool) {\n> +\t\tf_options[f] = \"--gui\";\n> +\t\tf++;\n> +\t}\n> +\tif (difftool_cmd) {\n> +\t\tf_options[f] = \"--tool\";\n> +\t\tf++;\n> +\t}\n> +\tif (extcmd) {\n> +\t\tf_options[f] = \"--extcmd\";\n> +\t\tf++;\n> +\t}\n> +\tif (f > 1)\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n\nI prefer this if-cascade over the \"f += truth_value\" style, because I\nfind it is easier to understand. If you write it as\n\n\tif (extcmd)\n\t\tf_options[f++] = \"--extcmd\";\n\nyou get each branch down to two lines. (But then others may disagree\nwith the easy-to-understand argument due to the f++ buried in the\nexpression. Unsure...)\n\n>  \n>  \tif (use_gui_tool)\n>  \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\n> diff --git a/builtin/grep.c b/builtin/grep.c\n> index 9e34a820ad4..b199781cb27 100644\n> --- a/builtin/grep.c\n> +++ b/builtin/grep.c\n> @@ -1168,10 +1168,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>  \t\tsetup_pager();\n>  \n>  \tif (!use_index && (untracked || cached))\n> -\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--cached/--untracked\", \"--no-index\");\n\nHuh? \"--cached/--untracked\"? Which one was used on the command line? But...\n\n>  \n>  \tif (untracked && cached)\n> -\t\tdie(_(\"--untracked cannot be used with --cached\"));\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--untracked\", \"--cached\");\n>  \n>  \tif (!use_index || untracked) {\n>  \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\n\n... doesn't this logic imply that --cached, --untracked, and --no-index\nare three mutually exclusive options?\n\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 4b493408cc5..59b4a2fd380 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -1759,6 +1759,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \tstruct strbuf rdiff_title = STRBUF_INIT;\n>  \tint creation_factor = -1;\n>  \n> +\tint f = 0;\n> +\tchar * f_options[4];\n> +\n\nStyle: char *f_options[4]; don't needlessly separate these new variables\nfrom the others by an empty line.\n\n>  \tconst struct option builtin_format_patch_options[] = {\n>  \t\tOPT_CALLBACK_F('n', \"numbered\", &numbered, NULL,\n>  \t\t\t    N_(\"use [PATCH n/m] even with a single patch\"),\n> @@ -1978,8 +1981,21 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \tif (rev.show_notes)\n>  \t\tload_display_notes(&rev.notes_opt);\n>  \n> -\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n> -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n> +\tif (use_stdout) {\n> +\t\tf_options[f] = \"--stdout\";\n> +\t\tf++;\n> +\t}\n> +\tif (rev.diffopt.close_file) {\n> +\t\tf_options[f] = \"--output\";\n> +\t\tf++;\n> +\t}\n> +\tif (output_directory) {\n> +\t\tf_options[f] = \"--output-directory\";\n> +\t\tf++;\n> +\t}\n> +\n> +\tif (f > 1)\n> +\t\tdie(_(\"options '%s'and '%s' cannot be used together\"), f_options[0], f_options[1]);\n>  \n>  \tif (use_stdout) {\n>  \t\tsetup_pager();\n> diff --git a/builtin/merge-base.c b/builtin/merge-base.c\n> index 6719ac198dc..1447f1c493a 100644\n> --- a/builtin/merge-base.c\n> +++ b/builtin/merge-base.c\n> @@ -159,12 +159,12 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)\n>  \t\tif (argc < 2)\n>  \t\t\tusage_with_options(merge_base_usage, options);\n>  \t\tif (show_all)\n> -\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n> +\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--is-ancestor\", \"--all\");\n>  \t\treturn handle_is_ancestor(argc, argv);\n>  \t}\n>  \n>  \tif (cmdmode == 'r' && show_all)\n> -\t\tdie(\"--independent cannot be used with --all\");\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--independent\", \"--all\");\n>  \n>  \tif (cmdmode == 'o')\n>  \t\treturn handle_octopus(argc, argv, show_all);\n> diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\n> index 91964653a0b..5fcaa0b4f2a 100755\n> --- a/t/t7500-commit-template-squash-signoff.sh\n> +++ b/t/t7500-commit-template-squash-signoff.sh\n> @@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '\n>  '\n>  \n>  test_expect_success '--fixup=reword: -F give error message' '\n> -\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n> +\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used together\" >expect &&\n>  \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n>  \ttest_cmp expect actual\n>  '\n\nA general comment: To me, it looks like you didn't want to change the\nvariable name 'f' in the first hunk above, and then just named the array\n'f_options' to go with 'f'. Perhaps `exclusive_opt` (not plural!) for\nthe array. Now, renaming 'f' to something longer makes the code a bit\nunwieldy. Therefore, let me suggest yet another approach: factor out\nfunctions check_exclusive_opts3(), check_exclusive_opts4(), to be used like\n\n\tcheck_exclusive_opts3(use_stdout, \"--stdout\",\n\t\trev.diffopt.close_file, \"--output\",\n\t\toutput_directory, \"--output-directory\");\n\nI am not yet proposing check_exclusive_opts2(), but others may think it\nis an improvement, too, (if they think that such functions are an\nimprovement in the first place).\n\n-- Hannes\n"},{"id":"446744","messageId":"80593381-2ecc-5de5-76b7-0e6c6452559f@gmail.com","threadId":"57287","inReplyTo":"ba203623-87b1-f3eb-9498-52c4ea4ba07e@kdbg.org","subject":"Re: [PATCH 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-01-24T11:06:19Z","receivedAt":"2022-01-24T11:06:24Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 24/01/2022 07:14, Johannes Sixt wrote:\n> Am 22.01.22 um 19:35 schrieb Jean-Noël Avila via GitGitGadget:\n>> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n>>\n>> When more than two options are mutually exclusive, print the ones\n>> which are actually on the command line.\n> \n> Reading this, I expect that all mutually exclusive options are listed in\n> the error messages. But the updated code lists only the first and second\n> option even if more are on the command line. What is the justification\n> for that? Just \"make the work for translators easier\"? I am not 100%\n> sure that this is the right balance. Wouldn't it be helpful for users to\n> get to know which set of options is incompatible, or is an error message\n> not the right place for this kind of education? Don't know...\n\nThat was my feeling as well when reading this patch, I think the loss of \ninformation in the error messages is a shame.\n\n>> Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>\n>> ---\n>>   builtin/commit.c                          | 39 ++++++++++++++---------\n>>   builtin/difftool.c                        | 18 +++++++++--\n>>   builtin/grep.c                            |  4 +--\n>>   builtin/log.c                             | 20 ++++++++++--\n>>   builtin/merge-base.c                      |  4 +--\n>>   t/t7500-commit-template-squash-signoff.sh |  2 +-\n>>   6 files changed, 63 insertions(+), 24 deletions(-)\n>>\n>> diff --git a/builtin/commit.c b/builtin/commit.c\n>> index b9ed0374e30..910f4c912bf 100644\n>> --- a/builtin/commit.c\n>> +++ b/builtin/commit.c\n>> @@ -1243,6 +1243,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>>   \t\t\t\t      struct wt_status *s)\n>>   {\n>>   \tint f = 0;\n>> +\tchar * f_options[4];\n>>   \n>>   \targc = parse_options(argc, argv, prefix, options, usage, 0);\n>>   \tfinalize_deferred_config(s);\n>> @@ -1251,7 +1252,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>>   \t\tforce_author = find_author_by_nickname(force_author);\n>>   \n>>   \tif (force_author && renew_authorship)\n>> -\t\tdie(_(\"Using both --reset-author and --author does not make sense\"));\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--reset-author\", \"--author\");\n>>   \n>>   \tif (logfile || have_option_m || use_message)\n>>   \t\tuse_editor = 0;\n>> @@ -1268,19 +1269,19 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>>   \t\t\tdie(_(\"You are in the middle of a rebase -- cannot amend.\"));\n>>   \t}\n>>   \tif (fixup_message && squash_message)\n>> -\t\tdie(_(\"Options --squash and --fixup cannot be used together\"));\n>> -\tif (use_message)\n>> -\t\tf++;\n>> -\tif (edit_message)\n>> -\t\tf++;\n>> -\tif (fixup_message)\n>> -\t\tf++;\n>> -\tif (logfile)\n>> -\t\tf++;\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n>> +\tf_options[f] = \"-C\";\n>> +\tf+=\t!!use_message;\n>> +\tf_options[f] = \"-c\";\n>> +\tf+=!!edit_message;\n>> +\tf_options[f] = \"-F\";\n>> +\tf+=!!logfile;\n>> +\tf_options[f] = \"--fixup\";\n>> +\tf+=!!fixup_message;\n\nThis feels like an out of bounds access waiting to happen when someone \nadds a new option but forgets to increase the size of f_options above\n\nBest Wishes\n\nPhillip\n\n> The format style violations are quite obvious here. But see below for a\n> suggestion to write this differently.\n> \n>>   \tif (f > 1)\n>> -\t\tdie(_(\"Only one of -c/-C/-F/--fixup can be used.\"));\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n>>   \tif (have_option_m && (edit_message || use_message || logfile))\n>> -\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", f_options[0]);\n>>   \tif (f || have_option_m)\n>>   \t\ttemplate_file = NULL;\n>>   \tif (edit_message)\n>> @@ -1305,9 +1306,17 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>>   \n>>   \tif (patch_interactive)\n>>   \t\tinteractive = 1;\n>> -\n>> -\tif (also + only + all + interactive > 1)\n>> -\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n>> +\tf = 0;\n>> +\tf_options[f] = \"-i/--include\";\n>> +\tf += also;\n>> +\tf_options[f] = \"-o/--only\";\n>> +\tf += only;\n>> +\tf_options[f] = \"-a/--all\";\n>> +\tf += all;\n>> +\tf_options[f] = \"--interactive/-p/--patch\";\n> \n> Interesting. -p and --interactive do different things, but can be used\n> together (-p overrides --interactive). The original error message\n> suggests that this is not the case. Just a marginal note...\n> \n>> +\tf += interactive;\n>> +\tif (f > 1)\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n>>   \n>>   \tif (fixup_message) {\n>>   \t\t/*\n>> diff --git a/builtin/difftool.c b/builtin/difftool.c\n>> index c79fbbf67e5..92854bc7737 100644\n>> --- a/builtin/difftool.c\n>> +++ b/builtin/difftool.c\n>> @@ -685,6 +685,8 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n>>   \tint use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,\n>>   \t    tool_help = 0, no_index = 0;\n>>   \tstatic char *difftool_cmd = NULL, *extcmd = NULL;\n>> +\tint f = 0;\n>> +\tchar *f_options[3];\n>>   \tstruct option builtin_difftool_options[] = {\n>>   \t\tOPT_BOOL('g', \"gui\", &use_gui_tool,\n>>   \t\t\t N_(\"use `diff.guitool` instead of `diff.tool`\")),\n>> @@ -732,8 +734,20 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n>>   \t} else if (dir_diff)\n>>   \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--dir-diff\", \"--no-index\");\n>>   \n>> -\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n>> -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n>> +\tif (use_gui_tool) {\n>> +\t\tf_options[f] = \"--gui\";\n>> +\t\tf++;\n>> +\t}\n>> +\tif (difftool_cmd) {\n>> +\t\tf_options[f] = \"--tool\";\n>> +\t\tf++;\n>> +\t}\n>> +\tif (extcmd) {\n>> +\t\tf_options[f] = \"--extcmd\";\n>> +\t\tf++;\n>> +\t}\n>> +\tif (f > 1)\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n> \n> I prefer this if-cascade over the \"f += truth_value\" style, because I\n> find it is easier to understand. If you write it as\n> \n> \tif (extcmd)\n> \t\tf_options[f++] = \"--extcmd\";\n> \n> you get each branch down to two lines. (But then others may disagree\n> with the easy-to-understand argument due to the f++ buried in the\n> expression. Unsure...)\n> \n>>   \n>>   \tif (use_gui_tool)\n>>   \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\n>> diff --git a/builtin/grep.c b/builtin/grep.c\n>> index 9e34a820ad4..b199781cb27 100644\n>> --- a/builtin/grep.c\n>> +++ b/builtin/grep.c\n>> @@ -1168,10 +1168,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>>   \t\tsetup_pager();\n>>   \n>>   \tif (!use_index && (untracked || cached))\n>> -\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--cached/--untracked\", \"--no-index\");\n> \n> Huh? \"--cached/--untracked\"? Which one was used on the command line? But...\n> \n>>   \n>>   \tif (untracked && cached)\n>> -\t\tdie(_(\"--untracked cannot be used with --cached\"));\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--untracked\", \"--cached\");\n>>   \n>>   \tif (!use_index || untracked) {\n>>   \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\n> \n> ... doesn't this logic imply that --cached, --untracked, and --no-index\n> are three mutually exclusive options?\n> \n>> diff --git a/builtin/log.c b/builtin/log.c\n>> index 4b493408cc5..59b4a2fd380 100644\n>> --- a/builtin/log.c\n>> +++ b/builtin/log.c\n>> @@ -1759,6 +1759,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>>   \tstruct strbuf rdiff_title = STRBUF_INIT;\n>>   \tint creation_factor = -1;\n>>   \n>> +\tint f = 0;\n>> +\tchar * f_options[4];\n>> +\n> \n> Style: char *f_options[4]; don't needlessly separate these new variables\n> from the others by an empty line.\n> \n>>   \tconst struct option builtin_format_patch_options[] = {\n>>   \t\tOPT_CALLBACK_F('n', \"numbered\", &numbered, NULL,\n>>   \t\t\t    N_(\"use [PATCH n/m] even with a single patch\"),\n>> @@ -1978,8 +1981,21 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>>   \tif (rev.show_notes)\n>>   \t\tload_display_notes(&rev.notes_opt);\n>>   \n>> -\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n>> -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n>> +\tif (use_stdout) {\n>> +\t\tf_options[f] = \"--stdout\";\n>> +\t\tf++;\n>> +\t}\n>> +\tif (rev.diffopt.close_file) {\n>> +\t\tf_options[f] = \"--output\";\n>> +\t\tf++;\n>> +\t}\n>> +\tif (output_directory) {\n>> +\t\tf_options[f] = \"--output-directory\";\n>> +\t\tf++;\n>> +\t}\n>> +\n>> +\tif (f > 1)\n>> +\t\tdie(_(\"options '%s'and '%s' cannot be used together\"), f_options[0], f_options[1]);\n>>   \n>>   \tif (use_stdout) {\n>>   \t\tsetup_pager();\n>> diff --git a/builtin/merge-base.c b/builtin/merge-base.c\n>> index 6719ac198dc..1447f1c493a 100644\n>> --- a/builtin/merge-base.c\n>> +++ b/builtin/merge-base.c\n>> @@ -159,12 +159,12 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)\n>>   \t\tif (argc < 2)\n>>   \t\t\tusage_with_options(merge_base_usage, options);\n>>   \t\tif (show_all)\n>> -\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n>> +\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--is-ancestor\", \"--all\");\n>>   \t\treturn handle_is_ancestor(argc, argv);\n>>   \t}\n>>   \n>>   \tif (cmdmode == 'r' && show_all)\n>> -\t\tdie(\"--independent cannot be used with --all\");\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--independent\", \"--all\");\n>>   \n>>   \tif (cmdmode == 'o')\n>>   \t\treturn handle_octopus(argc, argv, show_all);\n>> diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\n>> index 91964653a0b..5fcaa0b4f2a 100755\n>> --- a/t/t7500-commit-template-squash-signoff.sh\n>> +++ b/t/t7500-commit-template-squash-signoff.sh\n>> @@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '\n>>   '\n>>   \n>>   test_expect_success '--fixup=reword: -F give error message' '\n>> -\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n>> +\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used together\" >expect &&\n>>   \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n>>   \ttest_cmp expect actual\n>>   '\n> \n> A general comment: To me, it looks like you didn't want to change the\n> variable name 'f' in the first hunk above, and then just named the array\n> 'f_options' to go with 'f'. Perhaps `exclusive_opt` (not plural!) for\n> the array. Now, renaming 'f' to something longer makes the code a bit\n> unwieldy. Therefore, let me suggest yet another approach: factor out\n> functions check_exclusive_opts3(), check_exclusive_opts4(), to be used like\n> \n> \tcheck_exclusive_opts3(use_stdout, \"--stdout\",\n> \t\trev.diffopt.close_file, \"--output\",\n> \t\toutput_directory, \"--output-directory\");\n> \n> I am not yet proposing check_exclusive_opts2(), but others may think it\n> is an improvement, too, (if they think that such functions are an\n> improvement in the first place).\n> \n> -- Hannes\n\n"},{"id":"446745","messageId":"b072a986-aeb5-ca10-0888-33f26c9e4b17@gmail.com","threadId":"57287","inReplyTo":"ca48e82a6b4a2581c815f796a303d411c27a0283.1642876553.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/4] i18n: factorize \"invalid value\" messages","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-01-24T11:09:45Z","receivedAt":"2022-01-24T11:09:50Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Jean-Noël\n\nOn 22/01/2022 18:35, Jean-Noël Avila via GitGitGadget wrote:\n> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n> \n> Use the same message when an invalid value is passed to a command line\n> option or a configuration variable.\n\nI think the factorization works well here. I think in the previous \nseries you mentioned the possibility of creating macros for standard \nmessages, reading this patch that or some standard functions like \nerror_invalid_value() seem like a good idea to keep the messages \nconsistent in the future.\n\nBest Wishes\n\nPhillip\n\n> Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>\n> ---\n>   builtin/am.c           | 7 ++++---\n>   builtin/blame.c        | 6 +++---\n>   builtin/fetch.c        | 4 ++--\n>   builtin/pack-objects.c | 2 +-\n>   builtin/pull.c         | 6 +++---\n>   builtin/push.c         | 2 +-\n>   builtin/send-pack.c    | 2 +-\n>   diff-merges.c          | 2 +-\n>   gpg-interface.c        | 4 ++--\n>   ls-refs.c              | 2 +-\n>   parallel-checkout.c    | 3 ++-\n>   sequencer.c            | 2 +-\n>   setup.c                | 2 +-\n>   submodule-config.c     | 2 +-\n>   t/t4150-am.sh          | 2 +-\n>   15 files changed, 25 insertions(+), 23 deletions(-)\n> \n> diff --git a/builtin/am.c b/builtin/am.c\n> index b6be1f1cb11..ba1dacbc034 100644\n> --- a/builtin/am.c\n> +++ b/builtin/am.c\n> @@ -199,7 +199,7 @@ static int am_option_parse_empty(const struct option *opt,\n>   \telse if (!strcmp(arg, \"keep\"))\n>   \t\t*opt_value = KEEP_EMPTY_COMMIT;\n>   \telse\n> -\t\treturn error(_(\"Invalid value for --empty: %s\"), arg);\n> +\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n>   \n>   \treturn 0;\n>   }\n> @@ -2239,7 +2239,7 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int\n>   \t * when you add new options\n>   \t */\n>   \telse\n> -\t\treturn error(_(\"Invalid value for --patch-format: %s\"), arg);\n> +\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--patch-format\", arg);\n>   \treturn 0;\n>   }\n>   \n> @@ -2282,7 +2282,8 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar\n>   \t\t\t\tbreak;\n>   \t\t}\n>   \t\tif (new_value >= ARRAY_SIZE(valid_modes))\n> -\t\t\treturn error(_(\"Invalid value for --show-current-patch: %s\"), arg);\n> +\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n> +\t\t\t\t\t\t \"--show-current-patch\", arg);\n>   \t}\n>   \n>   \tif (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)\n> diff --git a/builtin/blame.c b/builtin/blame.c\n> index 7fafeac4081..ca821420d68 100644\n> --- a/builtin/blame.c\n> +++ b/builtin/blame.c\n> @@ -721,8 +721,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n>   \t}\n>   \tif (!strcmp(var, \"color.blame.repeatedlines\")) {\n>   \t\tif (color_parse_mem(value, strlen(value), repeated_meta_color))\n> -\t\t\twarning(_(\"invalid color '%s' in color.blame.repeatedLines\"),\n> -\t\t\t\tvalue);\n> +\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n> +\t\t\t\t\t\"color.blame.repeatedLines\", value);\n>   \t\treturn 0;\n>   \t}\n>   \tif (!strcmp(var, \"color.blame.highlightrecent\")) {\n> @@ -739,7 +739,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n>   \t\t\tcoloring_mode &= ~(OUTPUT_COLOR_LINE |\n>   \t\t\t\t\t    OUTPUT_SHOW_AGE_WITH_COLOR);\n>   \t\t} else {\n> -\t\t\twarning(_(\"invalid value for blame.coloring\"));\n> +\t\t\twarning(_(\"invalid value for '%s': '%s'\"), \"blame.coloring\", value);\n>   \t\t\treturn 0;\n>   \t\t}\n>   \t}\n> diff --git a/builtin/fetch.c b/builtin/fetch.c\n> index eaab8056bf9..19ec48f3330 100644\n> --- a/builtin/fetch.c\n> +++ b/builtin/fetch.c\n> @@ -758,8 +758,8 @@ static void prepare_format_display(struct ref *ref_map)\n>   \telse if (!strcasecmp(format, \"compact\"))\n>   \t\tcompact_format = 1;\n>   \telse\n> -\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n> -\t\t    format);\n> +\t\tdie(_(\"invalid value for '%s': '%s'\"),\n> +\t\t\t  \"fetch.output\", format);\n>   \n>   \tfor (rm = ref_map; rm; rm = rm->next) {\n>   \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n> diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\n> index ba2006f2212..192c3ca305e 100644\n> --- a/builtin/pack-objects.c\n> +++ b/builtin/pack-objects.c\n> @@ -3504,7 +3504,7 @@ static int option_parse_missing_action(const struct option *opt,\n>   \t\treturn 0;\n>   \t}\n>   \n> -\tdie(_(\"invalid value for --missing\"));\n> +\tdie(_(\"invalid value for '%s': '%s'\"), \"--missing\", arg);\n>   \treturn 0;\n>   }\n>   \n> diff --git a/builtin/pull.c b/builtin/pull.c\n> index 100cbf9fb85..e54a0ccadc6 100644\n> --- a/builtin/pull.c\n> +++ b/builtin/pull.c\n> @@ -42,9 +42,9 @@ static enum rebase_type parse_config_rebase(const char *key, const char *value,\n>   \t\treturn v;\n>   \n>   \tif (fatal)\n> -\t\tdie(_(\"Invalid value for %s: %s\"), key, value);\n> +\t\tdie(_(\"invalid value for '%s': '%s'\"), key, value);\n>   \telse\n> -\t\terror(_(\"Invalid value for %s: %s\"), key, value);\n> +\t\terror(_(\"invalid value for '%s': '%s'\"), key, value);\n>   \n>   \treturn REBASE_INVALID;\n>   }\n> @@ -318,7 +318,7 @@ static const char *config_get_ff(void)\n>   \tif (!strcmp(value, \"only\"))\n>   \t\treturn \"--ff-only\";\n>   \n> -\tdie(_(\"Invalid value for pull.ff: %s\"), value);\n> +\tdie(_(\"invalid value for '%s': '%s'\"), \"pull.ff\", value);\n>   }\n>   \n>   /**\n> diff --git a/builtin/push.c b/builtin/push.c\n> index 359db90321c..cad997965a7 100644\n> --- a/builtin/push.c\n> +++ b/builtin/push.c\n> @@ -486,7 +486,7 @@ static int git_push_config(const char *k, const char *v, void *cb)\n>   \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n>   \t\t\t\t\tset_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);\n>   \t\t\t\telse\n> -\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n> +\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n>   \t\t\t}\n>   \t\t}\n>   \t} else if (!strcmp(k, \"push.recursesubmodules\")) {\n> diff --git a/builtin/send-pack.c b/builtin/send-pack.c\n> index 69c432ef1a6..64962be0168 100644\n> --- a/builtin/send-pack.c\n> +++ b/builtin/send-pack.c\n> @@ -145,7 +145,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)\n>   \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n>   \t\t\t\t\targs.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;\n>   \t\t\t\telse\n> -\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n> +\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n>   \t\t\t}\n>   \t\t}\n>   \t}\n> diff --git a/diff-merges.c b/diff-merges.c\n> index 5060ccd890b..cd6c102a0d5 100644\n> --- a/diff-merges.c\n> +++ b/diff-merges.c\n> @@ -67,7 +67,7 @@ static void set_diff_merges(struct rev_info *revs, const char *optarg)\n>   \tdiff_merges_setup_func_t func = func_by_opt(optarg);\n>   \n>   \tif (!func)\n> -\t\tdie(_(\"unknown value for --diff-merges: %s\"), optarg);\n> +\t\tdie(_(\"invalid value for '%s': '%s'\"), \"--diff-merges\", optarg);\n>   \n>   \tfunc(revs);\n>   \n> diff --git a/gpg-interface.c b/gpg-interface.c\n> index b52eb0e2e04..04d751a16a8 100644\n> --- a/gpg-interface.c\n> +++ b/gpg-interface.c\n> @@ -702,7 +702,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n>   \t\t\treturn config_error_nonbool(var);\n>   \t\tfmt = get_format_by_name(value);\n>   \t\tif (!fmt)\n> -\t\t\treturn error(\"unsupported value for %s: %s\",\n> +\t\t\treturn error(\"invalid value for '%s': '%s'\",\n>   \t\t\t\t     var, value);\n>   \t\tuse_format = fmt;\n>   \t\treturn 0;\n> @@ -717,7 +717,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n>   \t\tfree(trust);\n>   \n>   \t\tif (ret)\n> -\t\t\treturn error(\"unsupported value for %s: %s\", var,\n> +\t\t\treturn error(\"invalid value for '%s': '%s'\", var,\n>   \t\t\t\t     value);\n>   \t\treturn 0;\n>   \t}\n> diff --git a/ls-refs.c b/ls-refs.c\n> index 54078323dcb..ae09dd59478 100644\n> --- a/ls-refs.c\n> +++ b/ls-refs.c\n> @@ -34,7 +34,7 @@ static void ensure_config_read(void)\n>   \t\t} else if (!strcmp(str, \"ignore\")) {\n>   \t\t\t/* do nothing */\n>   \t\t} else {\n> -\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n> +\t\t\tdie(_(\"invalid value for '%s': '%s'\"), \"lsrefs.unborn\", str);\n>   \t\t}\n>   \t}\n>   \tconfig_read = 1;\n> diff --git a/parallel-checkout.c b/parallel-checkout.c\n> index 8dd7e7bad40..237c02f1d81 100644\n> --- a/parallel-checkout.c\n> +++ b/parallel-checkout.c\n> @@ -39,7 +39,8 @@ void get_parallel_checkout_configs(int *num_workers, int *threshold)\n>   \n>   \tif (env_workers && *env_workers) {\n>   \t\tif (strtol_i(env_workers, 10, num_workers)) {\n> -\t\t\tdie(\"invalid value for GIT_TEST_CHECKOUT_WORKERS: '%s'\",\n> +\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n> +\t\t\t\t\"GIT_TEST_CHECKOUT_WORKERS\",\n>   \t\t\t    env_workers);\n>   \t\t}\n>   \t\tif (*num_workers < 1)\n> diff --git a/sequencer.c b/sequencer.c\n> index 6abd72160cc..9b985e8648d 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -2806,7 +2806,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n>   \t\treturn error(_(\"invalid key: %s\"), key);\n>   \n>   \tif (!error_flag)\n> -\t\treturn error(_(\"invalid value for %s: %s\"), key, value);\n> +\t\treturn error(_(\"invalid value for '%s': '%s'\"), key, value);\n>   \n>   \treturn 0;\n>   }\n> diff --git a/setup.c b/setup.c\n> index af3b8c09abe..fe74092a022 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -559,7 +559,7 @@ static enum extension_result handle_extension(const char *var,\n>   \t\t\treturn config_error_nonbool(var);\n>   \t\tformat = hash_algo_by_name(value);\n>   \t\tif (format == GIT_HASH_UNKNOWN)\n> -\t\t\treturn error(\"invalid value for 'extensions.objectformat'\");\n> +\t\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"extensions.objectformat\", value);\n>   \t\tdata->hash_algo = format;\n>   \t\treturn EXTENSION_OK;\n>   \t}\n> diff --git a/submodule-config.c b/submodule-config.c\n> index f95344028b5..fb95a026f48 100644\n> --- a/submodule-config.c\n> +++ b/submodule-config.c\n> @@ -496,7 +496,7 @@ static int parse_config(const char *var, const char *value, void *data)\n>   \t\telse if (parse_submodule_update_strategy(value,\n>   \t\t\t &submodule->update_strategy) < 0 ||\n>   \t\t\t submodule->update_strategy.type == SM_UPDATE_COMMAND)\n> -\t\t\tdie(_(\"invalid value for %s\"), var);\n> +\t\t\tdie(_(\"invalid value for '%s'\"), var);\n>   \t} else if (!strcmp(item.buf, \"shallow\")) {\n>   \t\tif (!me->overwrite && submodule->recommend_shallow != -1)\n>   \t\t\twarn_multiple_config(me->treeish_name, submodule->name,\n> diff --git a/t/t4150-am.sh b/t/t4150-am.sh\n> index 6caff0ca397..159fae8d016 100755\n> --- a/t/t4150-am.sh\n> +++ b/t/t4150-am.sh\n> @@ -1169,7 +1169,7 @@ test_expect_success 'invalid when passing the --empty option alone' '\n>   \ttest_when_finished \"git am --abort || :\" &&\n>   \tgit checkout empty-commit^ &&\n>   \ttest_must_fail git am --empty empty-commit.patch 2>err &&\n> -\techo \"error: Invalid value for --empty: empty-commit.patch\" >expected &&\n> +\techo \"error: invalid value for '\\''--empty'\\'': '\\''empty-commit.patch'\\''\" >expected &&\n>   \ttest_cmp expected err\n>   '\n>   \n\n"},{"id":"446871","messageId":"2894278.sRLLl5rxgE@cayenne","threadId":"57287","inReplyTo":"80593381-2ecc-5de5-76b7-0e6c6452559f@gmail.com","subject":"Re: [PATCH 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Jean-Noël AVILA","fromEmail":"jn.avila@free.fr","sentAt":"2022-01-25T20:52:06Z","receivedAt":"2022-01-25T20:53:00Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"Le lundi 24 janvier 2022, 12:06:19 CET Phillip Wood a écrit :\n> On 24/01/2022 07:14, Johannes Sixt wrote:\n> > Am 22.01.22 um 19:35 schrieb Jean-Noël Avila via GitGitGadget:\n> >> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n> >>\n> >> When more than two options are mutually exclusive, print the ones\n> >> which are actually on the command line.\n> > \n> > Reading this, I expect that all mutually exclusive options are listed in\n> > the error messages. But the updated code lists only the first and second\n> > option even if more are on the command line. What is the justification\n> > for that? Just \"make the work for translators easier\"? I am not 100%\n> > sure that this is the right balance. Wouldn't it be helpful for users to\n> > get to know which set of options is incompatible, or is an error message\n> > not the right place for this kind of education? Don't know...\n> \n> That was my feeling as well when reading this patch, I think the loss of \n> information in the error messages is a shame.\n\nThe enhancement aims at being more precise as to which options actually \npresent on \nthe command line are mutually exclusive, instead of letting the user sort out \nwhere the clash comes from. Personal taste, but the \"--foo/--bar\" is poor user \ninteraction: better a partial but precise message than a (hopefully) complete \nbut generic one.\n\n\n\n> >> @@ -1268,19 +1269,19 @@ static int parse_and_validate_options(int argc, \nconst char *argv[],\n> >>   \t\t\tdie(_(\"You are in the middle of a rebase -- \ncannot amend.\"));\n> >>   \t}\n> >>   \tif (fixup_message && squash_message)\n> >> -\t\tdie(_(\"Options --squash and --fixup cannot be used \ntogether\"));\n> >> -\tif (use_message)\n> >> -\t\tf++;\n> >> -\tif (edit_message)\n> >> -\t\tf++;\n> >> -\tif (fixup_message)\n> >> -\t\tf++;\n> >> -\tif (logfile)\n> >> -\t\tf++;\n> >> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \n\"--squash\", \"--fixup\");\n> >> +\tf_options[f] = \"-C\";\n> >> +\tf+=\t!!use_message;\n> >> +\tf_options[f] = \"-c\";\n> >> +\tf+=!!edit_message;\n> >> +\tf_options[f] = \"-F\";\n> >> +\tf+=!!logfile;\n> >> +\tf_options[f] = \"--fixup\";\n> >> +\tf+=!!fixup_message;\n> \n> This feels like an out of bounds access waiting to happen when someone \n> adds a new option but forgets to increase the size of f_options above\n\nThat's one of the advantages of C99: declaring the variables near their use \nsite so that you can keep them in you brain while reading the code.\n\n> \n> Best Wishes\n> \n> Phillip\n> \n> > \n> > I prefer this if-cascade over the \"f += truth_value\" style, because I\n> > find it is easier to understand. If you write it as\n> > \n> > \tif (extcmd)\n> > \t\tf_options[f++] = \"--extcmd\";\n> > \n> > you get each branch down to two lines. (But then others may disagree\n> > with the easy-to-understand argument due to the f++ buried in the\n> > expression. Unsure...)\n\n\n\n\n> > \n> >>   \n> >>   \tif (use_gui_tool)\n> >>   \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\n> >> diff --git a/builtin/grep.c b/builtin/grep.c\n> >> index 9e34a820ad4..b199781cb27 100644\n> >> --- a/builtin/grep.c\n> >> +++ b/builtin/grep.c\n> >> @@ -1168,10 +1168,10 @@ int cmd_grep(int argc, const char **argv, const \nchar *prefix)\n> >>   \t\tsetup_pager();\n> >>   \n> >>   \tif (!use_index && (untracked || cached))\n> >> -\t\tdie(_(\"--cached or --untracked cannot be used with --\nno-index\"));\n> >> +\t\tdie(_(\"options '%s' and '%s' cannot be used \ntogether\"),\"--cached/--untracked\", \"--no-index\");\n> > \n> > Huh? \"--cached/--untracked\"? Which one was used on the command line? \nBut...\n> > \n> >>   \n> >>   \tif (untracked && cached)\n> >> -\t\tdie(_(\"--untracked cannot be used with --cached\"));\n> >> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \n\"--untracked\", \"--cached\");\n> >>   \n> >>   \tif (!use_index || untracked) {\n> >>   \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!\nopt_exclude;\n> > \n> > ... doesn't this logic imply that --cached, --untracked, and --no-index\n> > are three mutually exclusive options?\n> > \n> >> diff --git a/builtin/log.c b/builtin/log.c\n> >> index 4b493408cc5..59b4a2fd380 100644\n> >> --- a/builtin/log.c\n> >> +++ b/builtin/log.c\n> >> @@ -1759,6 +1759,9 @@ int cmd_format_patch(int argc, const char **argv, \nconst char *prefix)\n> >>   \tstruct strbuf rdiff_title = STRBUF_INIT;\n> >>   \tint creation_factor = -1;\n> >>   \n> >> +\tint f = 0;\n> >> +\tchar * f_options[4];\n> >> +\n> > \n> > Style: char *f_options[4]; don't needlessly separate these new variables\n> > from the others by an empty line.\n> > \n> >>   \tconst struct option builtin_format_patch_options[] = {\n> >>   \t\tOPT_CALLBACK_F('n', \"numbered\", &numbered, NULL,\n> >>   \t\t\t    N_(\"use [PATCH n/m] even with a single \npatch\"),\n> >> @@ -1978,8 +1981,21 @@ int cmd_format_patch(int argc, const char **argv, \nconst char *prefix)\n> >>   \tif (rev.show_notes)\n> >>   \t\tload_display_notes(&rev.notes_opt);\n> >>   \n> >> -\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n> >> -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used \ntogether\"), \"--stdout\", \"--output\", \"--output-directory\");\n> >> +\tif (use_stdout) {\n> >> +\t\tf_options[f] = \"--stdout\";\n> >> +\t\tf++;\n> >> +\t}\n> >> +\tif (rev.diffopt.close_file) {\n> >> +\t\tf_options[f] = \"--output\";\n> >> +\t\tf++;\n> >> +\t}\n> >> +\tif (output_directory) {\n> >> +\t\tf_options[f] = \"--output-directory\";\n> >> +\t\tf++;\n> >> +\t}\n> >> +\n> >> +\tif (f > 1)\n> >> +\t\tdie(_(\"options '%s'and '%s' cannot be used together\"), \nf_options[0], f_options[1]);\n> >>   \n> >>   \tif (use_stdout) {\n> >>   \t\tsetup_pager();\n> >> diff --git a/builtin/merge-base.c b/builtin/merge-base.c\n> >> index 6719ac198dc..1447f1c493a 100644\n> >> --- a/builtin/merge-base.c\n> >> +++ b/builtin/merge-base.c\n> >> @@ -159,12 +159,12 @@ int cmd_merge_base(int argc, const char **argv, \nconst char *prefix)\n> >>   \t\tif (argc < 2)\n> >>   \t\t\tusage_with_options(merge_base_usage, \noptions);\n> >>   \t\tif (show_all)\n> >> -\t\t\tdie(\"--is-ancestor cannot be used with --\nall\");\n> >> +\t\t\tdie(_(\"options '%s' and '%s' cannot be used \ntogether\"),\"--is-ancestor\", \"--all\");\n> >>   \t\treturn handle_is_ancestor(argc, argv);\n> >>   \t}\n> >>   \n> >>   \tif (cmdmode == 'r' && show_all)\n> >> -\t\tdie(\"--independent cannot be used with --all\");\n> >> +\t\tdie(_(\"options '%s' and '%s' cannot be used \ntogether\"),\"--independent\", \"--all\");\n> >>   \n> >>   \tif (cmdmode == 'o')\n> >>   \t\treturn handle_octopus(argc, argv, show_all);\n> >> diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-\ntemplate-squash-signoff.sh\n> >> index 91964653a0b..5fcaa0b4f2a 100755\n> >> --- a/t/t7500-commit-template-squash-signoff.sh\n> >> +++ b/t/t7500-commit-template-squash-signoff.sh\n> >> @@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with \npathsec' '\n> >>   '\n> >>   \n> >>   test_expect_success '--fixup=reword: -F give error message' '\n> >> -\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n> >> +\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used \ntogether\" >expect &&\n> >>   \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n> >>   \ttest_cmp expect actual\n> >>   '\n> > \n> > A general comment: To me, it looks like you didn't want to change the\n> > variable name 'f' in the first hunk above, and then just named the array\n> > 'f_options' to go with 'f'. Perhaps `exclusive_opt` (not plural!) for\n> > the array. Now, renaming 'f' to something longer makes the code a bit\n> > unwieldy. Therefore, let me suggest yet another approach: factor out\n> > functions check_exclusive_opts3(), check_exclusive_opts4(), to be used \nlike\n> > \n> > \tcheck_exclusive_opts3(use_stdout, \"--stdout\",\n> > \t\trev.diffopt.close_file, \"--output\",\n> > \t\toutput_directory, \"--output-directory\");\n> > \n> > I am not yet proposing check_exclusive_opts2(), but others may think it\n> > is an improvement, too, (if they think that such functions are an\n> > improvement in the first place).\n> > \n> > -- Hannes\n> \n\nWill factorize away such exclusive options, but I'm not sure where. Should it \nbe in git-compat-util.h?\n\n\n"},{"id":"446874","messageId":"5740b0c5-7df8-5947-a4c9-0ece2bf17891@kdbg.org","threadId":"57287","inReplyTo":"2894278.sRLLl5rxgE@cayenne","subject":"Re: [PATCH 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-01-25T21:26:37Z","receivedAt":"2022-01-25T21:26:41Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 25.01.22 um 21:52 schrieb Jean-Noël AVILA:\n> Le lundi 24 janvier 2022, 12:06:19 CET Phillip Wood a écrit :\n>> On 24/01/2022 07:14, Johannes Sixt wrote:\n>>> A general comment: To me, it looks like you didn't want to change the\n>>> variable name 'f' in the first hunk above, and then just named the array\n>>> 'f_options' to go with 'f'. Perhaps `exclusive_opt` (not plural!) for\n>>> the array. Now, renaming 'f' to something longer makes the code a bit\n>>> unwieldy. Therefore, let me suggest yet another approach: factor out\n>>> functions check_exclusive_opts3(), check_exclusive_opts4(), to be used \n> like\n>>>\n>>> \tcheck_exclusive_opts3(use_stdout, \"--stdout\",\n>>> \t\trev.diffopt.close_file, \"--output\",\n>>> \t\toutput_directory, \"--output-directory\");\n>>>\n>>> I am not yet proposing check_exclusive_opts2(), but others may think it\n>>> is an improvement, too, (if they think that such functions are an\n>>> improvement in the first place).\n> \n> Will factorize away such exclusive options, but I'm not sure where. Should it \n> be in git-compat-util.h?\n\nHow about parse-options.*? There are already some helper functions near\nthe end of parse-options.c. Perhaps the names I suggested must be\nrevised, though.\n\n-- Hannes\n"},{"id":"447240","messageId":"pull.1123.v2.git.1643408643.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.git.1642876553.gitgitgadget@gmail.com","subject":"[PATCH v2 0/4] Factorize i18n","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-28T22:23:59Z","receivedAt":"2022-01-28T22:24:12Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"This is a small series of i18n factorizations. Again the idea is to:\n\n * reduce the number of strings to translate\n * make life easier for translators by removing constant parts of the\n   strings to translate\n * uniformize the UI by using a type of message for the a given type of\n   condition.\n\nChanges since V1:\n\n * factorize checks of more than 2 incompatible options into dedicated\n   functions which list all the incompatible options present on the command\n   line\n\nJean-Noël Avila (4):\n  i18n: factorize more 'incompatible options' messages\n  i18n: factorize \"invalid value\" messages\n  i18n: remove from i18n strings that do not hold translatable parts\n  i18n: transfer variables into placeholders in command synopsis\n\n archive.c                                 |  2 +-\n builtin/am.c                              |  7 ++--\n builtin/bisect--helper.c                  |  6 +--\n builtin/blame.c                           |  6 +--\n builtin/commit.c                          | 39 +++++++++---------\n builtin/count-objects.c                   |  2 +-\n builtin/difftool.c                        |  5 ++-\n builtin/fast-export.c                     |  2 +-\n builtin/fetch.c                           |  4 +-\n builtin/grep.c                            |  8 ++--\n builtin/hash-object.c                     |  2 +-\n builtin/help.c                            |  4 +-\n builtin/log.c                             |  5 ++-\n builtin/merge-base.c                      |  4 +-\n builtin/mktag.c                           |  2 +-\n builtin/mktree.c                          |  2 +-\n builtin/notes.c                           |  6 +--\n builtin/pack-objects.c                    |  2 +-\n builtin/prune-packed.c                    |  2 +-\n builtin/pull.c                            |  6 +--\n builtin/push.c                            |  2 +-\n builtin/rebase.c                          |  2 +-\n builtin/reflog.c                          |  6 +--\n builtin/remote.c                          |  2 +-\n builtin/replace.c                         |  2 +-\n builtin/rev-list.c                        |  2 +-\n builtin/send-pack.c                       |  2 +-\n builtin/sparse-checkout.c                 |  8 ++--\n builtin/stripspace.c                      |  4 +-\n builtin/submodule--helper.c               |  2 +-\n builtin/update-server-info.c              |  2 +-\n diff-merges.c                             |  2 +-\n gpg-interface.c                           |  4 +-\n ls-refs.c                                 |  2 +-\n parallel-checkout.c                       |  3 +-\n parse-options.c                           | 50 +++++++++++++++++++++++\n parse-options.h                           |  9 ++++\n sequencer.c                               |  2 +-\n setup.c                                   |  2 +-\n submodule-config.c                        |  2 +-\n t/t4150-am.sh                             |  2 +-\n t/t7500-commit-template-squash-signoff.sh |  2 +-\n 42 files changed, 145 insertions(+), 85 deletions(-)\n\n\nbase-commit: 90d242d36e248acfae0033274b524bfa55a947fd\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1123%2Fjnavila%2Ffactorize_i18n-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1123/jnavila/factorize_i18n-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/1123\n\nRange-diff vs v1:\n\n 1:  81ff928567f ! 1:  844e01391e1 i18n: factorize more 'incompatible options' messages\n     @@ Metadata\n       ## Commit message ##\n          i18n: factorize more 'incompatible options' messages\n      \n     +    Find more incompatible options to factorize.\n     +\n          When more than two options are mutually exclusive, print the ones\n          which are actually on the command line.\n      \n     @@ Commit message\n      \n       ## builtin/commit.c ##\n      @@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *argv[],\n     + \t\t\t\t      struct commit *current_head,\n       \t\t\t\t      struct wt_status *s)\n       {\n     - \tint f = 0;\n     -+\tchar * f_options[4];\n     - \n     +-\tint f = 0;\n     +-\n       \targc = parse_options(argc, argv, prefix, options, usage, 0);\n       \tfinalize_deferred_config(s);\n     + \n      @@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *argv[],\n       \t\tforce_author = find_author_by_nickname(force_author);\n       \n     @@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *ar\n      -\t\tf++;\n      -\tif (logfile)\n      -\t\tf++;\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n     -+\tf_options[f] = \"-C\";\n     -+\tf+=\t!!use_message;\n     -+\tf_options[f] = \"-c\";\n     -+\tf+=!!edit_message;\n     -+\tf_options[f] = \"-F\";\n     -+\tf+=!!logfile;\n     -+\tf_options[f] = \"--fixup\";\n     -+\tf+=!!fixup_message;\n     - \tif (f > 1)\n     +-\tif (f > 1)\n      -\t\tdie(_(\"Only one of -c/-C/-F/--fixup can be used.\"));\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n     - \tif (have_option_m && (edit_message || use_message || logfile))\n     +-\tif (have_option_m && (edit_message || use_message || logfile))\n      -\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", f_options[0]);\n     - \tif (f || have_option_m)\n     +-\tif (f || have_option_m)\n     ++\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n     ++\tdie_if_incompatible_opt4(!!use_message, \"-C\",\n     ++\t\t\t\t\t\t\t !!edit_message, \"-c\",\n     ++\t\t\t\t\t\t\t !!logfile, \"-F\",\n     ++\t\t\t\t\t\t\t !!fixup_message, \"--fixup\");\n     ++\tif (have_option_m) {\n     ++\t\tif (edit_message)\n     ++\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-c\");\n     ++\t\telse if  (use_message)\n     ++\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-C\");\n     ++\t\telse if (logfile)\n     ++\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-F\");\n     ++\t}\n     ++\tif (use_message || edit_message || logfile ||fixup_message || have_option_m)\n       \t\ttemplate_file = NULL;\n       \tif (edit_message)\n     + \t\tuse_message = edit_message;\n      @@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *argv[],\n     - \n       \tif (patch_interactive)\n       \t\tinteractive = 1;\n     --\n     + \n      -\tif (also + only + all + interactive > 1)\n      -\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n     -+\tf = 0;\n     -+\tf_options[f] = \"-i/--include\";\n     -+\tf += also;\n     -+\tf_options[f] = \"-o/--only\";\n     -+\tf += only;\n     -+\tf_options[f] = \"-a/--all\";\n     -+\tf += all;\n     -+\tf_options[f] = \"--interactive/-p/--patch\";\n     -+\tf += interactive;\n     -+\tif (f > 1)\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n     - \n     +-\n     ++\tdie_if_incompatible_opt4(also, \"-i/--include\",\n     ++\t\t\t\t\t\t\t only, \"-o/--only\",\n     ++\t\t\t\t\t\t\t all, \"-a/--all\",\n     ++\t\t\t\t\t\t\t interactive, \"--interactive/-p/--patch\");\n       \tif (fixup_message) {\n       \t\t/*\n     + \t\t * We limit --fixup's suboptions to only alpha characters.\n      \n       ## builtin/difftool.c ##\n     -@@ builtin/difftool.c: int cmd_difftool(int argc, const char **argv, const char *prefix)\n     - \tint use_gui_tool = 0, dir_diff = 0, prompt = -1, symlinks = 0,\n     - \t    tool_help = 0, no_index = 0;\n     - \tstatic char *difftool_cmd = NULL, *extcmd = NULL;\n     -+\tint f = 0;\n     -+\tchar *f_options[3];\n     - \tstruct option builtin_difftool_options[] = {\n     - \t\tOPT_BOOL('g', \"gui\", &use_gui_tool,\n     - \t\t\t N_(\"use `diff.guitool` instead of `diff.tool`\")),\n      @@ builtin/difftool.c: int cmd_difftool(int argc, const char **argv, const char *prefix)\n       \t} else if (dir_diff)\n       \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--dir-diff\", \"--no-index\");\n       \n      -\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n      -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n     -+\tif (use_gui_tool) {\n     -+\t\tf_options[f] = \"--gui\";\n     -+\t\tf++;\n     -+\t}\n     -+\tif (difftool_cmd) {\n     -+\t\tf_options[f] = \"--tool\";\n     -+\t\tf++;\n     -+\t}\n     -+\tif (extcmd) {\n     -+\t\tf_options[f] = \"--extcmd\";\n     -+\t\tf++;\n     -+\t}\n     -+\tif (f > 1)\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), f_options[0], f_options[1]);\n     ++\tdie_if_incompatible_opt3(use_gui_tool, \"--gui\",\n     ++\t\t\t\t\t\t\t !!difftool_cmd, \"--tool\",\n     ++\t\t\t\t\t\t\t !!extcmd, \"--extcmd\");\n       \n       \tif (use_gui_tool)\n       \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\n      \n       ## builtin/grep.c ##\n      @@ builtin/grep.c: int cmd_grep(int argc, const char **argv, const char *prefix)\n     + \tif (!show_in_pager && !opt.status_only)\n       \t\tsetup_pager();\n       \n     - \tif (!use_index && (untracked || cached))\n     +-\tif (!use_index && (untracked || cached))\n      -\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--cached/--untracked\", \"--no-index\");\n     - \n     - \tif (untracked && cached)\n     +-\n     +-\tif (untracked && cached)\n      -\t\tdie(_(\"--untracked cannot be used with --cached\"));\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--untracked\", \"--cached\");\n     ++\tdie_if_incompatible_opt3(!use_index, \"--no-index\",\n     ++\t\t\t\t\t\t\t untracked, \"--untracked\",\n     ++\t\t\t\t\t\t\t cached, \"--cached\");\n       \n       \tif (!use_index || untracked) {\n       \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\n      \n       ## builtin/log.c ##\n     -@@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)\n     - \tstruct strbuf rdiff_title = STRBUF_INIT;\n     - \tint creation_factor = -1;\n     - \n     -+\tint f = 0;\n     -+\tchar * f_options[4];\n     -+\n     - \tconst struct option builtin_format_patch_options[] = {\n     - \t\tOPT_CALLBACK_F('n', \"numbered\", &numbered, NULL,\n     - \t\t\t    N_(\"use [PATCH n/m] even with a single patch\"),\n      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)\n       \tif (rev.show_notes)\n       \t\tload_display_notes(&rev.notes_opt);\n       \n      -\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n      -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n     -+\tif (use_stdout) {\n     -+\t\tf_options[f] = \"--stdout\";\n     -+\t\tf++;\n     -+\t}\n     -+\tif (rev.diffopt.close_file) {\n     -+\t\tf_options[f] = \"--output\";\n     -+\t\tf++;\n     -+\t}\n     -+\tif (output_directory) {\n     -+\t\tf_options[f] = \"--output-directory\";\n     -+\t\tf++;\n     -+\t}\n     -+\n     -+\tif (f > 1)\n     -+\t\tdie(_(\"options '%s'and '%s' cannot be used together\"), f_options[0], f_options[1]);\n     ++\tdie_if_incompatible_opt3(use_stdout, \"--stdout\",\n     ++\t\t\t\t\t\t\t rev.diffopt.close_file, \"--output\",\n     ++\t\t\t\t\t\t\t !!output_directory, \"--output-directory\");\n       \n       \tif (use_stdout) {\n       \t\tsetup_pager();\n     @@ builtin/merge-base.c: int cmd_merge_base(int argc, const char **argv, const char\n       \tif (cmdmode == 'o')\n       \t\treturn handle_octopus(argc, argv, show_all);\n      \n     + ## parse-options.c ##\n     +@@ parse-options.c: void NORETURN usage_msg_opt(const char *msg,\n     + \tdie_message(\"%s\\n\", msg); /* The extra \\n is intentional */\n     + \tusage_with_options(usagestr, options);\n     + }\n     ++\n     ++void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n     ++\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n     ++\t\t\t\t\t\t\t  int opt3, const char *opt3_name)\n     ++{\n     ++\tint count = 0;\n     ++\tconst char *options[3];\n     ++\n     ++\tif (opt1)\n     ++\t\toptions[count++] = opt1_name;\n     ++\tif (opt2)\n     ++\t\toptions[count++] = opt2_name;\n     ++\tif (opt3)\n     ++\t\toptions[count++] = opt3_name;\n     ++\tif (count > 2)\n     ++\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name);\n     ++\telse if (count > 1)\n     ++\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n     ++}\n     ++\n     ++void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n     ++\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n     ++\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n     ++\t\t\t\t\t\t\t  int opt4, const char *opt4_name)\n     ++{\n     ++\tint count = 0;\n     ++\tconst char *options[4];\n     ++\n     ++\tif (opt1)\n     ++\t\toptions[count++] = opt1_name;\n     ++\tif (opt2)\n     ++\t\toptions[count++] = opt2_name;\n     ++\tif (opt3)\n     ++\t\toptions[count++] = opt3_name;\n     ++\tif (opt4)\n     ++\t\toptions[count++] = opt4_name;\n     ++\tswitch (count) {\n     ++\tcase 4:\n     ++\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name, opt4_name);\n     ++\t\tbreak;\n     ++\tcase 3:\n     ++\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), options[0], options[1], options[2]);\n     ++\t\tbreak;\n     ++\tcase 2:\n     ++\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n     ++\t\tbreak;\n     ++\tdefault:\n     ++\t\tbreak;\n     ++\t}\n     ++}\n     +\n     + ## parse-options.h ##\n     +@@ parse-options.h: int parse_opt_tracking_mode(const struct option *, const char *, int);\n     + #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n     + #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n     + \n     ++void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n     ++\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n     ++\t\t\t\t\t\t\t  int opt3, const char *opt3_name);\n     ++\n     ++void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n     ++\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n     ++\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n     ++\t\t\t\t\t\t\t  int opt4, const char *opt4_name);\n     ++\n     + #endif\n     +\n       ## t/t7500-commit-template-squash-signoff.sh ##\n      @@ t/t7500-commit-template-squash-signoff.sh: test_expect_success '--fixup=reword: give error with pathsec' '\n       '\n 2:  ca48e82a6b4 = 2:  4c860698d69 i18n: factorize \"invalid value\" messages\n 3:  ead191faad9 = 3:  376f8b56908 i18n: remove from i18n strings that do not hold translatable parts\n 4:  643be0d82de = 4:  8b0057f6180 i18n: transfer variables into placeholders in command synopsis\n\n-- \ngitgitgadget\n"},{"id":"447241","messageId":"844e01391e1198960072844536d736f51573cac6.1643408644.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v2.git.1643408643.gitgitgadget@gmail.com","subject":"[PATCH v2 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-28T22:24:00Z","receivedAt":"2022-01-28T22:24:13Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nFind more incompatible options to factorize.\n\nWhen more than two options are mutually exclusive, print the ones\nwhich are actually on the command line.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/commit.c                          | 39 +++++++++---------\n builtin/difftool.c                        |  5 ++-\n builtin/grep.c                            |  8 ++--\n builtin/log.c                             |  5 ++-\n builtin/merge-base.c                      |  4 +-\n parse-options.c                           | 50 +++++++++++++++++++++++\n parse-options.h                           |  9 ++++\n t/t7500-commit-template-squash-signoff.sh |  2 +-\n 8 files changed, 90 insertions(+), 32 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex b9ed0374e30..1966f965008 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1242,8 +1242,6 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\t\t\t      struct commit *current_head,\n \t\t\t\t      struct wt_status *s)\n {\n-\tint f = 0;\n-\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \tfinalize_deferred_config(s);\n \n@@ -1251,7 +1249,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tforce_author = find_author_by_nickname(force_author);\n \n \tif (force_author && renew_authorship)\n-\t\tdie(_(\"Using both --reset-author and --author does not make sense\"));\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--reset-author\", \"--author\");\n \n \tif (logfile || have_option_m || use_message)\n \t\tuse_editor = 0;\n@@ -1268,20 +1266,20 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\t\tdie(_(\"You are in the middle of a rebase -- cannot amend.\"));\n \t}\n \tif (fixup_message && squash_message)\n-\t\tdie(_(\"Options --squash and --fixup cannot be used together\"));\n-\tif (use_message)\n-\t\tf++;\n-\tif (edit_message)\n-\t\tf++;\n-\tif (fixup_message)\n-\t\tf++;\n-\tif (logfile)\n-\t\tf++;\n-\tif (f > 1)\n-\t\tdie(_(\"Only one of -c/-C/-F/--fixup can be used.\"));\n-\tif (have_option_m && (edit_message || use_message || logfile))\n-\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n-\tif (f || have_option_m)\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n+\tdie_if_incompatible_opt4(!!use_message, \"-C\",\n+\t\t\t\t\t\t\t !!edit_message, \"-c\",\n+\t\t\t\t\t\t\t !!logfile, \"-F\",\n+\t\t\t\t\t\t\t !!fixup_message, \"--fixup\");\n+\tif (have_option_m) {\n+\t\tif (edit_message)\n+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-c\");\n+\t\telse if  (use_message)\n+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-C\");\n+\t\telse if (logfile)\n+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-F\");\n+\t}\n+\tif (use_message || edit_message || logfile ||fixup_message || have_option_m)\n \t\ttemplate_file = NULL;\n \tif (edit_message)\n \t\tuse_message = edit_message;\n@@ -1306,9 +1304,10 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \tif (patch_interactive)\n \t\tinteractive = 1;\n \n-\tif (also + only + all + interactive > 1)\n-\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n-\n+\tdie_if_incompatible_opt4(also, \"-i/--include\",\n+\t\t\t\t\t\t\t only, \"-o/--only\",\n+\t\t\t\t\t\t\t all, \"-a/--all\",\n+\t\t\t\t\t\t\t interactive, \"--interactive/-p/--patch\");\n \tif (fixup_message) {\n \t\t/*\n \t\t * We limit --fixup's suboptions to only alpha characters.\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex c79fbbf67e5..ae487785735 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -732,8 +732,9 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n \t} else if (dir_diff)\n \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--dir-diff\", \"--no-index\");\n \n-\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n-\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n+\tdie_if_incompatible_opt3(use_gui_tool, \"--gui\",\n+\t\t\t\t\t\t\t !!difftool_cmd, \"--tool\",\n+\t\t\t\t\t\t\t !!extcmd, \"--extcmd\");\n \n \tif (use_gui_tool)\n \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 9e34a820ad4..cdf52667710 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -1167,11 +1167,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \tif (!show_in_pager && !opt.status_only)\n \t\tsetup_pager();\n \n-\tif (!use_index && (untracked || cached))\n-\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n-\n-\tif (untracked && cached)\n-\t\tdie(_(\"--untracked cannot be used with --cached\"));\n+\tdie_if_incompatible_opt3(!use_index, \"--no-index\",\n+\t\t\t\t\t\t\t untracked, \"--untracked\",\n+\t\t\t\t\t\t\t cached, \"--cached\");\n \n \tif (!use_index || untracked) {\n \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 4b493408cc5..048b2c37470 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1978,8 +1978,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.show_notes)\n \t\tload_display_notes(&rev.notes_opt);\n \n-\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n-\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n+\tdie_if_incompatible_opt3(use_stdout, \"--stdout\",\n+\t\t\t\t\t\t\t rev.diffopt.close_file, \"--output\",\n+\t\t\t\t\t\t\t !!output_directory, \"--output-directory\");\n \n \tif (use_stdout) {\n \t\tsetup_pager();\ndiff --git a/builtin/merge-base.c b/builtin/merge-base.c\nindex 6719ac198dc..1447f1c493a 100644\n--- a/builtin/merge-base.c\n+++ b/builtin/merge-base.c\n@@ -159,12 +159,12 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)\n \t\tif (argc < 2)\n \t\t\tusage_with_options(merge_base_usage, options);\n \t\tif (show_all)\n-\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--is-ancestor\", \"--all\");\n \t\treturn handle_is_ancestor(argc, argv);\n \t}\n \n \tif (cmdmode == 'r' && show_all)\n-\t\tdie(\"--independent cannot be used with --all\");\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--independent\", \"--all\");\n \n \tif (cmdmode == 'o')\n \t\treturn handle_octopus(argc, argv, show_all);\ndiff --git a/parse-options.c b/parse-options.c\nindex a8283037be9..fb9e1976ab3 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -1079,3 +1079,53 @@ void NORETURN usage_msg_opt(const char *msg,\n \tdie_message(\"%s\\n\", msg); /* The extra \\n is intentional */\n \tusage_with_options(usagestr, options);\n }\n+\n+void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n+\t\t\t\t\t\t\t  int opt3, const char *opt3_name)\n+{\n+\tint count = 0;\n+\tconst char *options[3];\n+\n+\tif (opt1)\n+\t\toptions[count++] = opt1_name;\n+\tif (opt2)\n+\t\toptions[count++] = opt2_name;\n+\tif (opt3)\n+\t\toptions[count++] = opt3_name;\n+\tif (count > 2)\n+\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name);\n+\telse if (count > 1)\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n+}\n+\n+void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n+\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n+\t\t\t\t\t\t\t  int opt4, const char *opt4_name)\n+{\n+\tint count = 0;\n+\tconst char *options[4];\n+\n+\tif (opt1)\n+\t\toptions[count++] = opt1_name;\n+\tif (opt2)\n+\t\toptions[count++] = opt2_name;\n+\tif (opt3)\n+\t\toptions[count++] = opt3_name;\n+\tif (opt4)\n+\t\toptions[count++] = opt4_name;\n+\tswitch (count) {\n+\tcase 4:\n+\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name, opt4_name);\n+\t\tbreak;\n+\tcase 3:\n+\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), options[0], options[1], options[2]);\n+\t\tbreak;\n+\tcase 2:\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\ndiff --git a/parse-options.h b/parse-options.h\nindex e22846d3b7b..cf393839ac4 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -339,4 +339,13 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);\n #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n \n+void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n+\t\t\t\t\t\t\t  int opt3, const char *opt3_name);\n+\n+void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n+\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n+\t\t\t\t\t\t\t  int opt4, const char *opt4_name);\n+\n #endif\ndiff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\nindex 91964653a0b..5fcaa0b4f2a 100755\n--- a/t/t7500-commit-template-squash-signoff.sh\n+++ b/t/t7500-commit-template-squash-signoff.sh\n@@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '\n '\n \n test_expect_success '--fixup=reword: -F give error message' '\n-\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n+\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used together\" >expect &&\n \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n \ttest_cmp expect actual\n '\n-- \ngitgitgadget\n\n"},{"id":"447242","messageId":"4c860698d69106f9c66b7c32c8169796dca8e511.1643408644.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v2.git.1643408643.gitgitgadget@gmail.com","subject":"[PATCH v2 2/4] i18n: factorize \"invalid value\" messages","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-28T22:24:01Z","receivedAt":"2022-01-28T22:24:14Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nUse the same message when an invalid value is passed to a command line\noption or a configuration variable.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/am.c           | 7 ++++---\n builtin/blame.c        | 6 +++---\n builtin/fetch.c        | 4 ++--\n builtin/pack-objects.c | 2 +-\n builtin/pull.c         | 6 +++---\n builtin/push.c         | 2 +-\n builtin/send-pack.c    | 2 +-\n diff-merges.c          | 2 +-\n gpg-interface.c        | 4 ++--\n ls-refs.c              | 2 +-\n parallel-checkout.c    | 3 ++-\n sequencer.c            | 2 +-\n setup.c                | 2 +-\n submodule-config.c     | 2 +-\n t/t4150-am.sh          | 2 +-\n 15 files changed, 25 insertions(+), 23 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex b6be1f1cb11..ba1dacbc034 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -199,7 +199,7 @@ static int am_option_parse_empty(const struct option *opt,\n \telse if (!strcmp(arg, \"keep\"))\n \t\t*opt_value = KEEP_EMPTY_COMMIT;\n \telse\n-\t\treturn error(_(\"Invalid value for --empty: %s\"), arg);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n \n \treturn 0;\n }\n@@ -2239,7 +2239,7 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int\n \t * when you add new options\n \t */\n \telse\n-\t\treturn error(_(\"Invalid value for --patch-format: %s\"), arg);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--patch-format\", arg);\n \treturn 0;\n }\n \n@@ -2282,7 +2282,8 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar\n \t\t\t\tbreak;\n \t\t}\n \t\tif (new_value >= ARRAY_SIZE(valid_modes))\n-\t\t\treturn error(_(\"Invalid value for --show-current-patch: %s\"), arg);\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\t\t \"--show-current-patch\", arg);\n \t}\n \n \tif (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 7fafeac4081..ca821420d68 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -721,8 +721,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!strcmp(var, \"color.blame.repeatedlines\")) {\n \t\tif (color_parse_mem(value, strlen(value), repeated_meta_color))\n-\t\t\twarning(_(\"invalid color '%s' in color.blame.repeatedLines\"),\n-\t\t\t\tvalue);\n+\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\t\"color.blame.repeatedLines\", value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"color.blame.highlightrecent\")) {\n@@ -739,7 +739,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t\t\tcoloring_mode &= ~(OUTPUT_COLOR_LINE |\n \t\t\t\t\t    OUTPUT_SHOW_AGE_WITH_COLOR);\n \t\t} else {\n-\t\t\twarning(_(\"invalid value for blame.coloring\"));\n+\t\t\twarning(_(\"invalid value for '%s': '%s'\"), \"blame.coloring\", value);\n \t\t\treturn 0;\n \t\t}\n \t}\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex eaab8056bf9..19ec48f3330 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -758,8 +758,8 @@ static void prepare_format_display(struct ref *ref_map)\n \telse if (!strcasecmp(format, \"compact\"))\n \t\tcompact_format = 1;\n \telse\n-\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n-\t\t    format);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t  \"fetch.output\", format);\n \n \tfor (rm = ref_map; rm; rm = rm->next) {\n \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex ba2006f2212..192c3ca305e 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3504,7 +3504,7 @@ static int option_parse_missing_action(const struct option *opt,\n \t\treturn 0;\n \t}\n \n-\tdie(_(\"invalid value for --missing\"));\n+\tdie(_(\"invalid value for '%s': '%s'\"), \"--missing\", arg);\n \treturn 0;\n }\n \ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 100cbf9fb85..e54a0ccadc6 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -42,9 +42,9 @@ static enum rebase_type parse_config_rebase(const char *key, const char *value,\n \t\treturn v;\n \n \tif (fatal)\n-\t\tdie(_(\"Invalid value for %s: %s\"), key, value);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"), key, value);\n \telse\n-\t\terror(_(\"Invalid value for %s: %s\"), key, value);\n+\t\terror(_(\"invalid value for '%s': '%s'\"), key, value);\n \n \treturn REBASE_INVALID;\n }\n@@ -318,7 +318,7 @@ static const char *config_get_ff(void)\n \tif (!strcmp(value, \"only\"))\n \t\treturn \"--ff-only\";\n \n-\tdie(_(\"Invalid value for pull.ff: %s\"), value);\n+\tdie(_(\"invalid value for '%s': '%s'\"), \"pull.ff\", value);\n }\n \n /**\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 359db90321c..cad997965a7 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -486,7 +486,7 @@ static int git_push_config(const char *k, const char *v, void *cb)\n \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n \t\t\t\t\tset_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);\n \t\t\t\telse\n-\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n+\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n \t\t\t}\n \t\t}\n \t} else if (!strcmp(k, \"push.recursesubmodules\")) {\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex 69c432ef1a6..64962be0168 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -145,7 +145,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)\n \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n \t\t\t\t\targs.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;\n \t\t\t\telse\n-\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n+\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n \t\t\t}\n \t\t}\n \t}\ndiff --git a/diff-merges.c b/diff-merges.c\nindex 5060ccd890b..cd6c102a0d5 100644\n--- a/diff-merges.c\n+++ b/diff-merges.c\n@@ -67,7 +67,7 @@ static void set_diff_merges(struct rev_info *revs, const char *optarg)\n \tdiff_merges_setup_func_t func = func_by_opt(optarg);\n \n \tif (!func)\n-\t\tdie(_(\"unknown value for --diff-merges: %s\"), optarg);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"), \"--diff-merges\", optarg);\n \n \tfunc(revs);\n \ndiff --git a/gpg-interface.c b/gpg-interface.c\nindex b52eb0e2e04..04d751a16a8 100644\n--- a/gpg-interface.c\n+++ b/gpg-interface.c\n@@ -702,7 +702,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n \t\t\treturn config_error_nonbool(var);\n \t\tfmt = get_format_by_name(value);\n \t\tif (!fmt)\n-\t\t\treturn error(\"unsupported value for %s: %s\",\n+\t\t\treturn error(\"invalid value for '%s': '%s'\",\n \t\t\t\t     var, value);\n \t\tuse_format = fmt;\n \t\treturn 0;\n@@ -717,7 +717,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n \t\tfree(trust);\n \n \t\tif (ret)\n-\t\t\treturn error(\"unsupported value for %s: %s\", var,\n+\t\t\treturn error(\"invalid value for '%s': '%s'\", var,\n \t\t\t\t     value);\n \t\treturn 0;\n \t}\ndiff --git a/ls-refs.c b/ls-refs.c\nindex 54078323dcb..ae09dd59478 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -34,7 +34,7 @@ static void ensure_config_read(void)\n \t\t} else if (!strcmp(str, \"ignore\")) {\n \t\t\t/* do nothing */\n \t\t} else {\n-\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n+\t\t\tdie(_(\"invalid value for '%s': '%s'\"), \"lsrefs.unborn\", str);\n \t\t}\n \t}\n \tconfig_read = 1;\ndiff --git a/parallel-checkout.c b/parallel-checkout.c\nindex 8dd7e7bad40..237c02f1d81 100644\n--- a/parallel-checkout.c\n+++ b/parallel-checkout.c\n@@ -39,7 +39,8 @@ void get_parallel_checkout_configs(int *num_workers, int *threshold)\n \n \tif (env_workers && *env_workers) {\n \t\tif (strtol_i(env_workers, 10, num_workers)) {\n-\t\t\tdie(\"invalid value for GIT_TEST_CHECKOUT_WORKERS: '%s'\",\n+\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\"GIT_TEST_CHECKOUT_WORKERS\",\n \t\t\t    env_workers);\n \t\t}\n \t\tif (*num_workers < 1)\ndiff --git a/sequencer.c b/sequencer.c\nindex 6abd72160cc..9b985e8648d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2806,7 +2806,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\treturn error(_(\"invalid key: %s\"), key);\n \n \tif (!error_flag)\n-\t\treturn error(_(\"invalid value for %s: %s\"), key, value);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), key, value);\n \n \treturn 0;\n }\ndiff --git a/setup.c b/setup.c\nindex af3b8c09abe..fe74092a022 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -559,7 +559,7 @@ static enum extension_result handle_extension(const char *var,\n \t\t\treturn config_error_nonbool(var);\n \t\tformat = hash_algo_by_name(value);\n \t\tif (format == GIT_HASH_UNKNOWN)\n-\t\t\treturn error(\"invalid value for 'extensions.objectformat'\");\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"extensions.objectformat\", value);\n \t\tdata->hash_algo = format;\n \t\treturn EXTENSION_OK;\n \t}\ndiff --git a/submodule-config.c b/submodule-config.c\nindex f95344028b5..fb95a026f48 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -496,7 +496,7 @@ static int parse_config(const char *var, const char *value, void *data)\n \t\telse if (parse_submodule_update_strategy(value,\n \t\t\t &submodule->update_strategy) < 0 ||\n \t\t\t submodule->update_strategy.type == SM_UPDATE_COMMAND)\n-\t\t\tdie(_(\"invalid value for %s\"), var);\n+\t\t\tdie(_(\"invalid value for '%s'\"), var);\n \t} else if (!strcmp(item.buf, \"shallow\")) {\n \t\tif (!me->overwrite && submodule->recommend_shallow != -1)\n \t\t\twarn_multiple_config(me->treeish_name, submodule->name,\ndiff --git a/t/t4150-am.sh b/t/t4150-am.sh\nindex 6caff0ca397..159fae8d016 100755\n--- a/t/t4150-am.sh\n+++ b/t/t4150-am.sh\n@@ -1169,7 +1169,7 @@ test_expect_success 'invalid when passing the --empty option alone' '\n \ttest_when_finished \"git am --abort || :\" &&\n \tgit checkout empty-commit^ &&\n \ttest_must_fail git am --empty empty-commit.patch 2>err &&\n-\techo \"error: Invalid value for --empty: empty-commit.patch\" >expected &&\n+\techo \"error: invalid value for '\\''--empty'\\'': '\\''empty-commit.patch'\\''\" >expected &&\n \ttest_cmp expected err\n '\n \n-- \ngitgitgadget\n\n"},{"id":"447243","messageId":"376f8b5690813e0a1df5fc0afc18d620bcd88ca3.1643408644.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v2.git.1643408643.gitgitgadget@gmail.com","subject":"[PATCH v2 3/4] i18n: remove from i18n strings that do not hold translatable parts","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-28T22:24:02Z","receivedAt":"2022-01-28T22:24:16Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n archive.c                    | 2 +-\n builtin/bisect--helper.c     | 6 +++---\n builtin/count-objects.c      | 2 +-\n builtin/hash-object.c        | 2 +-\n builtin/help.c               | 4 ++--\n builtin/mktag.c              | 2 +-\n builtin/mktree.c             | 2 +-\n builtin/notes.c              | 6 +++---\n builtin/prune-packed.c       | 2 +-\n builtin/rebase.c             | 2 +-\n builtin/reflog.c             | 2 +-\n builtin/remote.c             | 2 +-\n builtin/replace.c            | 2 +-\n builtin/sparse-checkout.c    | 8 ++++----\n builtin/stripspace.c         | 4 ++--\n builtin/submodule--helper.c  | 2 +-\n builtin/update-server-info.c | 2 +-\n 17 files changed, 26 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex d571249cf39..e29d0e00f6c 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -12,7 +12,7 @@\n \n static char const * const archive_usage[] = {\n \tN_(\"git archive [<options>] <tree-ish> [<path>...]\"),\n-\tN_(\"git archive --list\"),\n+\t\"git archive --list\",\n \tN_(\"git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]\"),\n \tN_(\"git archive --remote <repo> [--exec <cmd>] --list\"),\n \tNULL\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 28a2e6a5750..f962dbd4303 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -22,15 +22,15 @@ static GIT_PATH_FUNC(git_path_bisect_run, \"BISECT_RUN\")\n \n static const char * const git_bisect_helper_usage[] = {\n \tN_(\"git bisect--helper --bisect-reset [<commit>]\"),\n-\tN_(\"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]\"),\n+\t\"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]\",\n \tN_(\"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]\"\n \t\t\t\t\t    \" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]\"),\n-\tN_(\"git bisect--helper --bisect-next\"),\n+\t\"git bisect--helper --bisect-next\",\n \tN_(\"git bisect--helper --bisect-state (bad|new) [<rev>]\"),\n \tN_(\"git bisect--helper --bisect-state (good|old) [<rev>...]\"),\n \tN_(\"git bisect--helper --bisect-replay <filename>\"),\n \tN_(\"git bisect--helper --bisect-skip [(<rev>|<range>)...]\"),\n-\tN_(\"git bisect--helper --bisect-visualize\"),\n+\t\"git bisect--helper --bisect-visualize\",\n \tN_(\"git bisect--helper --bisect-run <cmd>...\"),\n \tNULL\n };\ndiff --git a/builtin/count-objects.c b/builtin/count-objects.c\nindex 3fae474f6f1..07b94195962 100644\n--- a/builtin/count-objects.c\n+++ b/builtin/count-objects.c\n@@ -87,7 +87,7 @@ static int print_alternate(struct object_directory *odb, void *data)\n }\n \n static char const * const count_objects_usage[] = {\n-\tN_(\"git count-objects [-v] [-H | --human-readable]\"),\n+\t\"git count-objects [-v] [-H | --human-readable]\",\n \tNULL\n };\n \ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex c7b3ad74c60..04e2442ec7c 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -81,7 +81,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n {\n \tstatic const char * const hash_object_usage[] = {\n \t\tN_(\"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>...\"),\n-\t\tN_(\"git hash-object  --stdin-paths\"),\n+\t\t\"git hash-object  --stdin-paths\",\n \t\tNULL\n \t};\n \tconst char *type = blob_type;\ndiff --git a/builtin/help.c b/builtin/help.c\nindex d387131dd83..b4f2ad3f94e 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -77,8 +77,8 @@ static struct option builtin_help_options[] = {\n static const char * const builtin_help_usage[] = {\n \tN_(\"git help [-a|--all] [--[no-]verbose]]\\n\"\n \t   \"         [[-i|--info] [-m|--man] [-w|--web]] [<command>]\"),\n-\tN_(\"git help [-g|--guides]\"),\n-\tN_(\"git help [-c|--config]\"),\n+\t\"git help [-g|--guides]\",\n+\t\"git help [-c|--config]\",\n \tNULL\n };\n \ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 3b2dbbb37e6..c7b905c614b 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -7,7 +7,7 @@\n #include \"config.h\"\n \n static char const * const builtin_mktag_usage[] = {\n-\tN_(\"git mktag\"),\n+\t\"git mktag\",\n \tNULL\n };\n static int option_strict = 1;\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex ae78ca1c029..8bdaada922a 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -63,7 +63,7 @@ static void write_tree(struct object_id *oid)\n }\n \n static const char *mktree_usage[] = {\n-\tN_(\"git mktree [-z] [--missing] [--batch]\"),\n+\t\"git mktree [-z] [--missing] [--batch]\",\n \tNULL\n };\n \ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 05d60483e82..f99593a1853 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -32,8 +32,8 @@ static const char * const git_notes_usage[] = {\n \tN_(\"git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]\"),\n \tN_(\"git notes [--ref <notes-ref>] show [<object>]\"),\n \tN_(\"git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>\"),\n-\tN_(\"git notes merge --commit [-v | -q]\"),\n-\tN_(\"git notes merge --abort [-v | -q]\"),\n+\t\"git notes merge --commit [-v | -q]\",\n+\t\"git notes merge --abort [-v | -q]\",\n \tN_(\"git notes [--ref <notes-ref>] remove [<object>...]\"),\n \tN_(\"git notes [--ref <notes-ref>] prune [-n] [-v]\"),\n \tN_(\"git notes [--ref <notes-ref>] get-ref\"),\n@@ -89,7 +89,7 @@ static const char * const git_notes_prune_usage[] = {\n };\n \n static const char * const git_notes_get_ref_usage[] = {\n-\tN_(\"git notes get-ref\"),\n+\t\"git notes get-ref\",\n \tNULL\n };\n \ndiff --git a/builtin/prune-packed.c b/builtin/prune-packed.c\nindex b7b9281a8ce..da3273a268b 100644\n--- a/builtin/prune-packed.c\n+++ b/builtin/prune-packed.c\n@@ -3,7 +3,7 @@\n #include \"prune-packed.h\"\n \n static const char * const prune_packed_usage[] = {\n-\tN_(\"git prune-packed [-n | --dry-run] [-q | --quiet]\"),\n+\t\"git prune-packed [-n | --dry-run] [-q | --quiet]\",\n \tNULL\n };\n \ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 36490d06c8a..64796c6a785 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -36,7 +36,7 @@ static char const * const builtin_rebase_usage[] = {\n \t\t\"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]\"),\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] \"\n \t\t\"--root [<branch>]\"),\n-\tN_(\"git rebase --continue | --abort | --skip | --edit-todo\"),\n+\t\"git rebase --continue | --abort | --skip | --edit-todo\",\n \tNULL\n };\n \ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex a4b1dd27e13..ee5ee8d8cf6 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -800,7 +800,7 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)\n  */\n \n static const char reflog_usage[] =\n-N_(\"git reflog [ show | expire | delete | exists ]\");\n+\"git reflog [ show | expire | delete | exists ]\";\n \n int cmd_reflog(int argc, const char **argv, const char *prefix)\n {\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 299c466116d..6f27ddc47bd 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -14,7 +14,7 @@\n #include \"commit-reach.h\"\n \n static const char * const builtin_remote_usage[] = {\n-\tN_(\"git remote [-v | --verbose]\"),\n+\t\"git remote [-v | --verbose]\",\n \tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n \tN_(\"git remote rename <old> <new>\"),\n \tN_(\"git remote remove <name>\"),\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 6ff1734d587..ac92337c0ec 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -22,7 +22,7 @@ static const char * const git_replace_usage[] = {\n \tN_(\"git replace [-f] <object> <replacement>\"),\n \tN_(\"git replace [-f] --edit <object>\"),\n \tN_(\"git replace [-f] --graft <commit> [<parent>...]\"),\n-\tN_(\"git replace [-f] --convert-graft-file\"),\n+\t\"git replace [-f] --convert-graft-file\",\n \tN_(\"git replace -d <object>...\"),\n \tN_(\"git replace [--format=<format>] [-l [<pattern>]]\"),\n \tNULL\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 679c1070368..771c9869a1c 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -43,7 +43,7 @@ static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)\n }\n \n static char const * const builtin_sparse_checkout_list_usage[] = {\n-\tN_(\"git sparse-checkout list\"),\n+\t\"git sparse-checkout list\",\n \tNULL\n };\n \n@@ -419,7 +419,7 @@ static int update_modes(int *cone_mode, int *sparse_index)\n }\n \n static char const * const builtin_sparse_checkout_init_usage[] = {\n-\tN_(\"git sparse-checkout init [--cone] [--[no-]sparse-index]\"),\n+\t\"git sparse-checkout init [--cone] [--[no-]sparse-index]\",\n \tNULL\n };\n \n@@ -762,7 +762,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)\n }\n \n static char const * const builtin_sparse_checkout_reapply_usage[] = {\n-\tN_(\"git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]\"),\n+\t\"git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]\",\n \tNULL\n };\n \n@@ -800,7 +800,7 @@ static int sparse_checkout_reapply(int argc, const char **argv)\n }\n \n static char const * const builtin_sparse_checkout_disable_usage[] = {\n-\tN_(\"git sparse-checkout disable\"),\n+\t\"git sparse-checkout disable\",\n \tNULL\n };\n \ndiff --git a/builtin/stripspace.c b/builtin/stripspace.c\nindex be33eb83c1b..1e34cf2bebd 100644\n--- a/builtin/stripspace.c\n+++ b/builtin/stripspace.c\n@@ -15,8 +15,8 @@ static void comment_lines(struct strbuf *buf)\n }\n \n static const char * const stripspace_usage[] = {\n-\tN_(\"git stripspace [-s | --strip-comments]\"),\n-\tN_(\"git stripspace [-c | --comment-lines]\"),\n+\t\"git stripspace [-s | --strip-comments]\",\n+\t\"git stripspace [-c | --comment-lines]\",\n \tNULL\n };\n \ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c5d3fc3817f..b80aa9898ac 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2883,7 +2883,7 @@ static int module_config(int argc, const char **argv, const char *prefix)\n \tconst char *const git_submodule_helper_usage[] = {\n \t\tN_(\"git submodule--helper config <name> [<value>]\"),\n \t\tN_(\"git submodule--helper config --unset <name>\"),\n-\t\tN_(\"git submodule--helper config --check-writeable\"),\n+\t\t\"git submodule--helper config --check-writeable\",\n \t\tNULL\n \t};\n \ndiff --git a/builtin/update-server-info.c b/builtin/update-server-info.c\nindex 4321a344567..880fffec587 100644\n--- a/builtin/update-server-info.c\n+++ b/builtin/update-server-info.c\n@@ -4,7 +4,7 @@\n #include \"parse-options.h\"\n \n static const char * const update_server_info_usage[] = {\n-\tN_(\"git update-server-info [--force]\"),\n+\t\"git update-server-info [--force]\",\n \tNULL\n };\n \n-- \ngitgitgadget\n\n"},{"id":"447244","messageId":"8b0057f61807eca7da268670bc6796a0bc91bfc3.1643408644.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v2.git.1643408643.gitgitgadget@gmail.com","subject":"[PATCH v2 4/4] i18n: transfer variables into placeholders in command synopsis","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-28T22:24:03Z","receivedAt":"2022-01-28T22:24:20Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nThis applies the style guide for documentation.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/fast-export.c | 2 +-\n builtin/reflog.c      | 4 ++--\n builtin/rev-list.c    | 2 +-\n 3 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 9f1c730e587..510139e9b54 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -26,7 +26,7 @@\n #include \"commit-slab.h\"\n \n static const char *fast_export_usage[] = {\n-\tN_(\"git fast-export [rev-list-opts]\"),\n+\tN_(\"git fast-export [<rev-list-opts>]\"),\n \tNULL\n };\n \ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex ee5ee8d8cf6..343a10d371b 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -17,10 +17,10 @@ static const char reflog_expire_usage[] =\n N_(\"git reflog expire [--expire=<time>] \"\n    \"[--expire-unreachable=<time>] \"\n    \"[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] \"\n-   \"[--verbose] [--all] <refs>...\");\n+   \"[--verbose] [--all] <ref>...\");\n static const char reflog_delete_usage[] =\n N_(\"git reflog delete [--rewrite] [--updateref] \"\n-   \"[--dry-run | -n] [--verbose] <refs>...\");\n+   \"[--dry-run | -n] [--verbose] <ref>...\");\n static const char reflog_exists_usage[] =\n N_(\"git reflog exists <ref>\");\n \ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 777558e9b06..38528c7f156 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -20,7 +20,7 @@\n #include \"packfile.h\"\n \n static const char rev_list_usage[] =\n-\"git rev-list [OPTION] <commit-id>... [ -- paths... ]\\n\"\n+\"git rev-list [<options>] <commit-id>... [-- <path>...]\\n\"\n \"  limiting output:\\n\"\n \"    --max-count=<n>\\n\"\n \"    --max-age=<epoch>\\n\"\n-- \ngitgitgadget\n"},{"id":"447247","messageId":"7f46e276-b669-e8fe-21fd-6b43f5bfb33b@kdbg.org","threadId":"57287","inReplyTo":"844e01391e1198960072844536d736f51573cac6.1643408644.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-01-28T23:21:51Z","receivedAt":"2022-01-28T23:21:54Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 28.01.22 um 23:24 schrieb Jean-Noël Avila via GitGitGadget:\n> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n> \n> Find more incompatible options to factorize.\n> \n> When more than two options are mutually exclusive, print the ones\n> which are actually on the command line.\n> \n> Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>\n> ---\n>  builtin/commit.c                          | 39 +++++++++---------\n>  builtin/difftool.c                        |  5 ++-\n>  builtin/grep.c                            |  8 ++--\n>  builtin/log.c                             |  5 ++-\n>  builtin/merge-base.c                      |  4 +-\n>  parse-options.c                           | 50 +++++++++++++++++++++++\n>  parse-options.h                           |  9 ++++\n>  t/t7500-commit-template-squash-signoff.sh |  2 +-\n>  8 files changed, 90 insertions(+), 32 deletions(-)\n> \n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index b9ed0374e30..1966f965008 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1242,8 +1242,6 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>  \t\t\t\t      struct commit *current_head,\n>  \t\t\t\t      struct wt_status *s)\n>  {\n> -\tint f = 0;\n> -\n>  \targc = parse_options(argc, argv, prefix, options, usage, 0);\n>  \tfinalize_deferred_config(s);\n>  \n> @@ -1251,7 +1249,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>  \t\tforce_author = find_author_by_nickname(force_author);\n>  \n>  \tif (force_author && renew_authorship)\n> -\t\tdie(_(\"Using both --reset-author and --author does not make sense\"));\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--reset-author\", \"--author\");\n>  \n>  \tif (logfile || have_option_m || use_message)\n>  \t\tuse_editor = 0;\n> @@ -1268,20 +1266,20 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>  \t\t\tdie(_(\"You are in the middle of a rebase -- cannot amend.\"));\n>  \t}\n>  \tif (fixup_message && squash_message)\n> -\t\tdie(_(\"Options --squash and --fixup cannot be used together\"));\n> -\tif (use_message)\n> -\t\tf++;\n> -\tif (edit_message)\n> -\t\tf++;\n> -\tif (fixup_message)\n> -\t\tf++;\n> -\tif (logfile)\n> -\t\tf++;\n> -\tif (f > 1)\n> -\t\tdie(_(\"Only one of -c/-C/-F/--fixup can be used.\"));\n> -\tif (have_option_m && (edit_message || use_message || logfile))\n> -\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n> -\tif (f || have_option_m)\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n> +\tdie_if_incompatible_opt4(!!use_message, \"-C\",\n> +\t\t\t\t\t\t\t !!edit_message, \"-c\",\n> +\t\t\t\t\t\t\t !!logfile, \"-F\",\n> +\t\t\t\t\t\t\t !!fixup_message, \"--fixup\");\nPlease check the tab width setting of your editor; indentations here and\nin other calls and function declarations below are too wide. Looks like\nyou have it at 4 instead of 8.\n\n> +\tif (have_option_m) {\n> +\t\tif (edit_message)\n> +\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-c\");\n> +\t\telse if  (use_message)\n> +\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-C\");\n> +\t\telse if (logfile)\n> +\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-F\");\n> +\t}\n\nThis conditional could have been another die_if_incompatible_opt4()\ncall, although it would only check a conflict of -m with at most one of\n-c, -C, or -F.\n\n> +\tif (use_message || edit_message || logfile ||fixup_message || have_option_m)\n\nMissing space before fixup_message.\n\n>  \t\ttemplate_file = NULL;\n>  \tif (edit_message)\n>  \t\tuse_message = edit_message;\n\nThe rewrite of the logic in this hunk looks correct.\n\n> @@ -1306,9 +1304,10 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>  \tif (patch_interactive)\n>  \t\tinteractive = 1;\n>  \n> -\tif (also + only + all + interactive > 1)\n> -\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n> -\n> +\tdie_if_incompatible_opt4(also, \"-i/--include\",\n> +\t\t\t\t\t\t\t only, \"-o/--only\",\n> +\t\t\t\t\t\t\t all, \"-a/--all\",\n> +\t\t\t\t\t\t\t interactive, \"--interactive/-p/--patch\");\n>  \tif (fixup_message) {\n>  \t\t/*\n>  \t\t * We limit --fixup's suboptions to only alpha characters.\n> diff --git a/builtin/difftool.c b/builtin/difftool.c\n> index c79fbbf67e5..ae487785735 100644\n> --- a/builtin/difftool.c\n> +++ b/builtin/difftool.c\n> @@ -732,8 +732,9 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n>  \t} else if (dir_diff)\n>  \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--dir-diff\", \"--no-index\");\n>  \n> -\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n> -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n> +\tdie_if_incompatible_opt3(use_gui_tool, \"--gui\",\n> +\t\t\t\t\t\t\t !!difftool_cmd, \"--tool\",\n> +\t\t\t\t\t\t\t !!extcmd, \"--extcmd\");\n>  \n>  \tif (use_gui_tool)\n>  \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\n> diff --git a/builtin/grep.c b/builtin/grep.c\n> index 9e34a820ad4..cdf52667710 100644\n> --- a/builtin/grep.c\n> +++ b/builtin/grep.c\n> @@ -1167,11 +1167,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>  \tif (!show_in_pager && !opt.status_only)\n>  \t\tsetup_pager();\n>  \n> -\tif (!use_index && (untracked || cached))\n> -\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n> -\n> -\tif (untracked && cached)\n> -\t\tdie(_(\"--untracked cannot be used with --cached\"));\n> +\tdie_if_incompatible_opt3(!use_index, \"--no-index\",\n> +\t\t\t\t\t\t\t untracked, \"--untracked\",\n> +\t\t\t\t\t\t\t cached, \"--cached\");\n>  \n>  \tif (!use_index || untracked) {\n>  \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 4b493408cc5..048b2c37470 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -1978,8 +1978,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \tif (rev.show_notes)\n>  \t\tload_display_notes(&rev.notes_opt);\n>  \n> -\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n> -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n> +\tdie_if_incompatible_opt3(use_stdout, \"--stdout\",\n> +\t\t\t\t\t\t\t rev.diffopt.close_file, \"--output\",\n> +\t\t\t\t\t\t\t !!output_directory, \"--output-directory\");\n>  \n>  \tif (use_stdout) {\n>  \t\tsetup_pager();\n> diff --git a/builtin/merge-base.c b/builtin/merge-base.c\n> index 6719ac198dc..1447f1c493a 100644\n> --- a/builtin/merge-base.c\n> +++ b/builtin/merge-base.c\n> @@ -159,12 +159,12 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)\n>  \t\tif (argc < 2)\n>  \t\t\tusage_with_options(merge_base_usage, options);\n>  \t\tif (show_all)\n> -\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n> +\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--is-ancestor\", \"--all\");\n>  \t\treturn handle_is_ancestor(argc, argv);\n>  \t}\n>  \n>  \tif (cmdmode == 'r' && show_all)\n> -\t\tdie(\"--independent cannot be used with --all\");\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--independent\", \"--all\");\n>  \n>  \tif (cmdmode == 'o')\n>  \t\treturn handle_octopus(argc, argv, show_all);\n\nThese rewrites are good. Note the unusually wide indentation in the new\nfunction calls, though.\n\n> diff --git a/parse-options.c b/parse-options.c\n> index a8283037be9..fb9e1976ab3 100644\n> --- a/parse-options.c\n> +++ b/parse-options.c\n> @@ -1079,3 +1079,53 @@ void NORETURN usage_msg_opt(const char *msg,\n>  \tdie_message(\"%s\\n\", msg); /* The extra \\n is intentional */\n>  \tusage_with_options(usagestr, options);\n>  }\n> +\n> +void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n> +\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n> +\t\t\t\t\t\t\t  int opt3, const char *opt3_name)\n> +{\n> +\tint count = 0;\n> +\tconst char *options[3];\n> +\n> +\tif (opt1)\n> +\t\toptions[count++] = opt1_name;\n> +\tif (opt2)\n> +\t\toptions[count++] = opt2_name;\n> +\tif (opt3)\n> +\t\toptions[count++] = opt3_name;\n> +\tif (count > 2)\n> +\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name);\n> +\telse if (count > 1)\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n> +}\n> +\n> +void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n> +\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n> +\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n> +\t\t\t\t\t\t\t  int opt4, const char *opt4_name)\n> +{\n> +\tint count = 0;\n> +\tconst char *options[4];\n> +\n> +\tif (opt1)\n> +\t\toptions[count++] = opt1_name;\n> +\tif (opt2)\n> +\t\toptions[count++] = opt2_name;\n> +\tif (opt3)\n> +\t\toptions[count++] = opt3_name;\n> +\tif (opt4)\n> +\t\toptions[count++] = opt4_name;\n> +\tswitch (count) {\n> +\tcase 4:\n> +\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name, opt4_name);\n> +\t\tbreak;\n> +\tcase 3:\n> +\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), options[0], options[1], options[2]);\n> +\t\tbreak;\n> +\tcase 2:\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tbreak;\n> +\t}\n> +}\n\nGenerally, this is good. I wonder whether we have to expect compiler\nwarnings about unreachable break statements after the die() calls.\n\nA bit of code duplication could be avoided if die_if_incompatible_opt3()\nforwarded with an additional pair 0, \"\" to die_if_incompatible_opt4().\n\n> diff --git a/parse-options.h b/parse-options.h\n> index e22846d3b7b..cf393839ac4 100644\n> --- a/parse-options.h\n> +++ b/parse-options.h\n> @@ -339,4 +339,13 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);\n>  #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n>  #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n>  \n> +void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n> +\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n> +\t\t\t\t\t\t\t  int opt3, const char *opt3_name);\n> +\n> +void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n> +\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n> +\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n> +\t\t\t\t\t\t\t  int opt4, const char *opt4_name);\n> +\n>  #endif\n\nI would have placed these declarations near usage_msg_opt further above\nin the header file.\n\n> diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\n> index 91964653a0b..5fcaa0b4f2a 100755\n> --- a/t/t7500-commit-template-squash-signoff.sh\n> +++ b/t/t7500-commit-template-squash-signoff.sh\n> @@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '\n>  '\n>  \n>  test_expect_success '--fixup=reword: -F give error message' '\n> -\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n> +\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used together\" >expect &&\n>  \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n>  \ttest_cmp expect actual\n>  '\n\n-- Hannes\n"},{"id":"447248","messageId":"xmqqee4rtnts.fsf@gitster.g","threadId":"57287","inReplyTo":"7f46e276-b669-e8fe-21fd-6b43f5bfb33b@kdbg.org","subject":"Re: [PATCH v2 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-01-28T23:58:23Z","receivedAt":"2022-01-28T23:58:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n>> +void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n>> +\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n>> +\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n>> +\t\t\t\t\t\t\t  int opt4, const char *opt4_name)\n>> +{\n>> +\tint count = 0;\n>> +\tconst char *options[4];\n>> +\n>> +\tif (opt1)\n>> +\t\toptions[count++] = opt1_name;\n>> +\tif (opt2)\n>> +\t\toptions[count++] = opt2_name;\n>> +\tif (opt3)\n>> +\t\toptions[count++] = opt3_name;\n>> +\tif (opt4)\n>> +\t\toptions[count++] = opt4_name;\n>> +\tswitch (count) {\n>> +\tcase 4:\n>> +\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name, opt4_name);\n>> +\t\tbreak;\n>> +\tcase 3:\n>> +\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), options[0], options[1], options[2]);\n>> +\t\tbreak;\n>> +\tcase 2:\n>> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n>> +\t\tbreak;\n>> +\tdefault:\n>> +\t\tbreak;\n>> +\t}\n>> +}\n>\n> Generally, this is good. I wonder whether we have to expect compiler\n> warnings about unreachable break statements after the die() calls.\n>\n> A bit of code duplication could be avoided if die_if_incompatible_opt3()\n> forwarded with an additional pair 0, \"\" to die_if_incompatible_opt4().\n\nI wondered if a single varargs function\n\n    void die_if_incompatible_optN(const char *name1, int opt1, ...);\n\nthat takes a name=NULL terminated sequence of <name, opt> would\nwork, but\n\n (1) it would require the caller to always spell out the terminating\n     NULL, which may be ugly.\n\n (2) it would tempt people to programatically generate message for N\n     options, which leads to sentence lego, which is the exact\n     opposite of what this series wants to achieve.\n\nIn any case, I do agree with you that the callers of _opt3()\nvariants can just pass (0, \"unused\") in the 4-th slot.  If this were\nmade varargs, then it only needs to pass NULL at the end, so that\nmight be an improvement, though.\n\nAlso, isn't \"if\" in the name of the function misleading?  as far as\nI can tell, this function is not \"check if these options are\ncompatible and die if some are incompatible with each other\", but\nthe caller is convinced that these options are incompatible before\nit decides to call this function and there is no \"if\" in what this\nfunction can do.\n\nvoid die_for_incompatible_opts(const char *name1, int opt1, ...)\n{\n\tconst char *name[4];\n\tint count = 0;\n        va_list params;\n\n        va_start(params, name1);\n\n        if (opt1)\n\t        name[count++] = name1;\n        while (count < ARRAY_SIZE(name)) {\n                const char *n = va_arg(params, const char *);\n\n                if (!n)\n\t\t\tbreak;\n                if (va_arg(params, int))\n\t                name[count++] = n;\n        }\n        va_end(params);\n\n        switch (count) {\n\tdefault:\n\t\tBUG(\"die-for-incompatible-opts can only take up to %d args\",\n\t\t    ARRAY_SIZE(name));\n\tcase 4:\n\t\tdie(_(\"options '%s', '%s', '%s', and '%s'\"\n\t\t      \" cannot be used together\"),\n\t\t    name[0], name[1], name[2], name[3]);\n\t\tbreak;\n\tcase 3:\n\t\tdie(_(\"options '%s', '%s', and '%s'\"\n\t\t      \" cannot be used together\"),\n\t\t    name[0], name[1], name[2]);\n\t\tbreak;\n\t...\n\t}\n}\n\nmight be easier to extend when somebody discovers there needs the\n\"opt5\" variant. \n"},{"id":"447276","messageId":"f7468ea3-d9f6-96be-168d-de610d535e72@kdbg.org","threadId":"57287","inReplyTo":"xmqqee4rtnts.fsf@gitster.g","subject":"Re: [PATCH v2 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-01-29T08:08:34Z","receivedAt":"2022-01-29T08:08:40Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 29.01.22 um 00:58 schrieb Junio C Hamano:\n> Johannes Sixt <j6t@kdbg.org> writes:\n>> A bit of code duplication could be avoided if die_if_incompatible_opt3()\n>> forwarded with an additional pair 0, \"\" to die_if_incompatible_opt4().\n> \n> I wondered if a single varargs function\n> \n>     void die_if_incompatible_optN(const char *name1, int opt1, ...);\n> \n> that takes a name=NULL terminated sequence of <name, opt> would\n> work, but\n> \n>  (1) it would require the caller to always spell out the terminating\n>      NULL, which may be ugly.\n> \n>  (2) it would tempt people to programatically generate message for N\n>      options, which leads to sentence lego, which is the exact\n>      opposite of what this series wants to achieve.\n\nThe reason I did not suggest a varargs version is because\n\n  (3) it is not typesafe.\n\nA varargs implementation could be used as an implementation helper, but\nshould not be a public interface.\n\n> In any case, I do agree with you that the callers of _opt3()\n> variants can just pass (0, \"unused\") in the 4-th slot.  If this were\n> made varargs, then it only needs to pass NULL at the end, so that\n> might be an improvement, though.\n> \n> Also, isn't \"if\" in the name of the function misleading?  as far as\n> I can tell, this function is not \"check if these options are\n> compatible and die if some are incompatible with each other\", but\n> the caller is convinced that these options are incompatible before\n> it decides to call this function and there is no \"if\" in what this\n> function can do.\n\nGood point.\n\n> void die_for_incompatible_opts(const char *name1, int opt1, ...)\n> {\n> \tconst char *name[4];\n> \tint count = 0;\n>         va_list params;\n> \n>         va_start(params, name1);\n> \n>         if (opt1)\n> \t        name[count++] = name1;\n>         while (count < ARRAY_SIZE(name)) {\n>                 const char *n = va_arg(params, const char *);\n> \n>                 if (!n)\n> \t\t\tbreak;\n>                 if (va_arg(params, int))\n> \t                name[count++] = n;\n>         }\n>         va_end(params);\n> \n>         switch (count) {\n> \tdefault:\n> \t\tBUG(\"die-for-incompatible-opts can only take up to %d args\",\n> \t\t    ARRAY_SIZE(name));\n\nThe problems here are: (1) this case triggers only if there is an actual\ninvocation with all 5+ incompatible options, and (2) at that time, the\nout-of-bounds write to the name array has already happened.\n\nI know this implementation is just a rough show-case. But since it's\nbeen written by a very proficient mind, I'm now convinced: it's not\nsufficiently easy to write the varargs version, so let's not go there.\n\n> \tcase 4:\n> \t\tdie(_(\"options '%s', '%s', '%s', and '%s'\"\n> \t\t      \" cannot be used together\"),\n> \t\t    name[0], name[1], name[2], name[3]);\n> \t\tbreak;\n> \tcase 3:\n> \t\tdie(_(\"options '%s', '%s', and '%s'\"\n> \t\t      \" cannot be used together\"),\n> \t\t    name[0], name[1], name[2]);\n> \t\tbreak;\n> \t...\n> \t}\n> }\n> \n> might be easier to extend when somebody discovers there needs the\n> \"opt5\" variant. \n\n-- Hannes\n"},{"id":"447279","messageId":"8789935.eFtBeim4Pn@cayenne","threadId":"57287","inReplyTo":"f7468ea3-d9f6-96be-168d-de610d535e72@kdbg.org","subject":"Re: [PATCH v2 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Jean-Noël AVILA","fromEmail":"jn.avila@free.fr","sentAt":"2022-01-29T10:41:16Z","receivedAt":"2022-01-29T10:41:24Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"Le samedi 29 janvier 2022, 09:08:34 CET Johannes Sixt a écrit :\n> Am 29.01.22 um 00:58 schrieb Junio C Hamano:\n> > Johannes Sixt <j6t@kdbg.org> writes:\n> >> A bit of code duplication could be avoided if die_if_incompatible_opt3()\n> >> forwarded with an additional pair 0, \"\" to die_if_incompatible_opt4().\n> > \n> > I wondered if a single varargs function\n> > \n> >     void die_if_incompatible_optN(const char *name1, int opt1, ...);\n> > \n> > that takes a name=NULL terminated sequence of <name, opt> would\n> > work, but\n> > \n> >  (1) it would require the caller to always spell out the terminating\n> >      NULL, which may be ugly.\n> > \n> >  (2) it would tempt people to programatically generate message for N\n> >      options, which leads to sentence lego, which is the exact\n> >      opposite of what this series wants to achieve.\n\nThis is the second part of the functions, and they need a finite number of args \nto check in order to use the correct i18n string. This function cannot be made \ngeneric for any number of options to check.\n\n\n> \n> The reason I did not suggest a varargs version is because\n> \n>   (3) it is not typesafe.\n> \n> A varargs implementation could be used as an implementation helper, but\n> should not be a public interface.\n> \n> > In any case, I do agree with you that the callers of _opt3()\n> > variants can just pass (0, \"unused\") in the 4-th slot.  If this were\n> > made varargs, then it only needs to pass NULL at the end, so that\n> > might be an improvement, though.\n\nI still prefer to keep a public function signature which is explicit. Using \nvarargs for alternating types is really not type safe.\n\nIn such case, declaring the functions with less options as inline calls to the \none with the greatest number is a good balance.\n\n> > \n> > Also, isn't \"if\" in the name of the function misleading?  as far as\n> > I can tell, this function is not \"check if these options are\n> > compatible and die if some are incompatible with each other\", but\n> > the caller is convinced that these options are incompatible before\n> > it decides to call this function and there is no \"if\" in what this\n> > function can do.\n> \n> Good point.\n> \n\nI don't get the point: the function checks if incompatible options are present \non the command line and dies in such case. The caller does not know whether \nthe function will make the program die, it really depends on the result of the \ncheck performed in the function. In case there's no or only one of the \noptions, the function does nothing.\n\nJN\n\n\n"},{"id":"447280","messageId":"656d02d6-df00-bb75-584f-cf450a4f868b@kdbg.org","threadId":"57287","inReplyTo":"8789935.eFtBeim4Pn@cayenne","subject":"Re: [PATCH v2 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-01-29T13:18:40Z","receivedAt":"2022-01-29T13:18:46Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 29.01.22 um 11:41 schrieb Jean-Noël AVILA:\n> Le samedi 29 janvier 2022, 09:08:34 CET Johannes Sixt a écrit :\n>> Am 29.01.22 um 00:58 schrieb Junio C Hamano:\n>>> Also, isn't \"if\" in the name of the function misleading?  as far as\n>>> I can tell, this function is not \"check if these options are\n>>> compatible and die if some are incompatible with each other\", but\n>>> the caller is convinced that these options are incompatible before\n>>> it decides to call this function and there is no \"if\" in what this\n>>> function can do.\n>>\n>> Good point.\n> \n> I don't get the point: the function checks if incompatible options are present \n> on the command line and dies in such case. The caller does not know whether \n> the function will make the program die, it really depends on the result of the \n> check performed in the function. In case there's no or only one of the \n> options, the function does nothing.\n\nThe function call reads like\n\n     die if options are incompatible\n\nThat sounds as if the caller were asking \"are these options\nincompatible?\" But that is not what it wants to ask because it already\nknows that the options are incompatible. It actually wants to ask \"is\nthere any problem with these incompatible options?\" The name Junio\nsuggested is die_for_incompatible_opts().\n\n-- Hannes\n"},{"id":"447317","messageId":"pull.1123.v3.git.1643580113.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v2.git.1643408643.gitgitgadget@gmail.com","subject":"[PATCH v3 0/4] Factorize i18n","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-30T22:01:49Z","receivedAt":"2022-01-30T22:01:58Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"This is a small series of i18n factorizations. Again the idea is to:\n\n * reduce the number of strings to translate\n * make life easier for translators by removing constant parts of the\n   strings to translate\n * uniformize the UI by using a type of message for the a given type of\n   condition.\n\nChanges since V1:\n\n * factorize checks of more than 2 incompatible options into dedicated\n   functions which list all the incompatible options present on the command\n   line\n\nChanges since V2:\n\n * switch to tab size 8\n * rename die_if* to die_for*\n * make 3 arg check a call to 4 arg check\n * switch another part to die_for_incompatible_opt4\n\nJean-Noël Avila (4):\n  i18n: factorize more 'incompatible options' messages\n  i18n: factorize \"invalid value\" messages\n  i18n: remove from i18n strings that do not hold translatable parts\n  i18n: transfer variables into placeholders in command synopsis\n\n archive.c                                 |  2 +-\n builtin/am.c                              |  8 ++++--\n builtin/bisect--helper.c                  |  6 ++--\n builtin/blame.c                           |  7 +++--\n builtin/commit.c                          | 35 ++++++++++-------------\n builtin/count-objects.c                   |  2 +-\n builtin/difftool.c                        |  5 ++--\n builtin/fast-export.c                     |  2 +-\n builtin/fetch.c                           |  4 +--\n builtin/grep.c                            |  8 ++----\n builtin/hash-object.c                     |  2 +-\n builtin/help.c                            |  4 +--\n builtin/log.c                             |  5 ++--\n builtin/merge-base.c                      |  6 ++--\n builtin/mktag.c                           |  2 +-\n builtin/mktree.c                          |  2 +-\n builtin/notes.c                           |  6 ++--\n builtin/pack-objects.c                    |  2 +-\n builtin/prune-packed.c                    |  2 +-\n builtin/pull.c                            |  6 ++--\n builtin/push.c                            |  2 +-\n builtin/rebase.c                          |  2 +-\n builtin/reflog.c                          |  6 ++--\n builtin/remote.c                          |  2 +-\n builtin/replace.c                         |  2 +-\n builtin/rev-list.c                        |  2 +-\n builtin/send-pack.c                       |  2 +-\n builtin/sparse-checkout.c                 |  8 +++---\n builtin/stripspace.c                      |  4 +--\n builtin/submodule--helper.c               |  2 +-\n builtin/update-server-info.c              |  2 +-\n diff-merges.c                             |  2 +-\n gpg-interface.c                           |  6 ++--\n ls-refs.c                                 |  3 +-\n parallel-checkout.c                       |  4 +--\n parse-options.c                           | 34 ++++++++++++++++++++++\n parse-options.h                           | 17 +++++++++++\n sequencer.c                               |  2 +-\n setup.c                                   |  3 +-\n submodule-config.c                        |  2 +-\n t/t4150-am.sh                             |  2 +-\n t/t7500-commit-template-squash-signoff.sh |  2 +-\n 42 files changed, 140 insertions(+), 87 deletions(-)\n\n\nbase-commit: 5d01301f2b865aa8dba1654d3f447ce9d21db0b5\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1123%2Fjnavila%2Ffactorize_i18n-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1123/jnavila/factorize_i18n-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/1123\n\nRange-diff vs v2:\n\n 1:  844e01391e1 ! 1:  aac3f9821c7 i18n: factorize more 'incompatible options' messages\n     @@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *ar\n      -\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n      -\tif (f || have_option_m)\n      +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n     -+\tdie_if_incompatible_opt4(!!use_message, \"-C\",\n     -+\t\t\t\t\t\t\t !!edit_message, \"-c\",\n     -+\t\t\t\t\t\t\t !!logfile, \"-F\",\n     -+\t\t\t\t\t\t\t !!fixup_message, \"--fixup\");\n     -+\tif (have_option_m) {\n     -+\t\tif (edit_message)\n     -+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-c\");\n     -+\t\telse if  (use_message)\n     -+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-C\");\n     -+\t\telse if (logfile)\n     -+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-F\");\n     -+\t}\n     ++\tdie_for_incompatible_opt4(!!use_message, \"-C\",\n     ++\t\t\t\t  !!edit_message, \"-c\",\n     ++\t\t\t\t  !!logfile, \"-F\",\n     ++\t\t\t\t  !!fixup_message, \"--fixup\");\n     ++\tdie_for_incompatible_opt4(have_option_m, \"-m\",\n     ++\t\t\t\t  !!edit_message, \"-c\",\n     ++\t\t\t\t  !!use_message, \"-C\",\n     ++\t\t\t\t  !!logfile, \"-F\");\n      +\tif (use_message || edit_message || logfile ||fixup_message || have_option_m)\n       \t\ttemplate_file = NULL;\n       \tif (edit_message)\n     @@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *ar\n      -\tif (also + only + all + interactive > 1)\n      -\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n      -\n     -+\tdie_if_incompatible_opt4(also, \"-i/--include\",\n     -+\t\t\t\t\t\t\t only, \"-o/--only\",\n     -+\t\t\t\t\t\t\t all, \"-a/--all\",\n     -+\t\t\t\t\t\t\t interactive, \"--interactive/-p/--patch\");\n     ++\tdie_for_incompatible_opt4(also, \"-i/--include\",\n     ++\t\t\t\t  only, \"-o/--only\",\n     ++\t\t\t\t  all, \"-a/--all\",\n     ++\t\t\t\t  interactive, \"--interactive/-p/--patch\");\n       \tif (fixup_message) {\n       \t\t/*\n       \t\t * We limit --fixup's suboptions to only alpha characters.\n     @@ builtin/difftool.c: int cmd_difftool(int argc, const char **argv, const char *pr\n       \n      -\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n      -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n     -+\tdie_if_incompatible_opt3(use_gui_tool, \"--gui\",\n     -+\t\t\t\t\t\t\t !!difftool_cmd, \"--tool\",\n     -+\t\t\t\t\t\t\t !!extcmd, \"--extcmd\");\n     ++\tdie_for_incompatible_opt3(use_gui_tool, \"--gui\",\n     ++\t\t\t\t  !!difftool_cmd, \"--tool\",\n     ++\t\t\t\t  !!extcmd, \"--extcmd\");\n       \n       \tif (use_gui_tool)\n       \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\n     @@ builtin/grep.c: int cmd_grep(int argc, const char **argv, const char *prefix)\n      -\n      -\tif (untracked && cached)\n      -\t\tdie(_(\"--untracked cannot be used with --cached\"));\n     -+\tdie_if_incompatible_opt3(!use_index, \"--no-index\",\n     -+\t\t\t\t\t\t\t untracked, \"--untracked\",\n     -+\t\t\t\t\t\t\t cached, \"--cached\");\n     ++\tdie_for_incompatible_opt3(!use_index, \"--no-index\",\n     ++\t\t\t\t  untracked, \"--untracked\",\n     ++\t\t\t\t  cached, \"--cached\");\n       \n       \tif (!use_index || untracked) {\n       \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\n     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre\n       \n      -\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n      -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n     -+\tdie_if_incompatible_opt3(use_stdout, \"--stdout\",\n     -+\t\t\t\t\t\t\t rev.diffopt.close_file, \"--output\",\n     -+\t\t\t\t\t\t\t !!output_directory, \"--output-directory\");\n     ++\tdie_for_incompatible_opt3(use_stdout, \"--stdout\",\n     ++\t\t\t\t  rev.diffopt.close_file, \"--output\",\n     ++\t\t\t\t  !!output_directory, \"--output-directory\");\n       \n       \tif (use_stdout) {\n       \t\tsetup_pager();\n     @@ builtin/merge-base.c: int cmd_merge_base(int argc, const char **argv, const char\n       \t\t\tusage_with_options(merge_base_usage, options);\n       \t\tif (show_all)\n      -\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n     -+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--is-ancestor\", \"--all\");\n     ++\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n     ++\t\t\t    \"--is-ancestor\", \"--all\");\n       \t\treturn handle_is_ancestor(argc, argv);\n       \t}\n       \n       \tif (cmdmode == 'r' && show_all)\n      -\t\tdie(\"--independent cannot be used with --all\");\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--independent\", \"--all\");\n     ++\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n     ++\t\t    \"--independent\", \"--all\");\n       \n       \tif (cmdmode == 'o')\n       \t\treturn handle_octopus(argc, argv, show_all);\n     @@ parse-options.c: void NORETURN usage_msg_opt(const char *msg,\n       \tusage_with_options(usagestr, options);\n       }\n      +\n     -+void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n     -+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n     -+\t\t\t\t\t\t\t  int opt3, const char *opt3_name)\n     -+{\n     -+\tint count = 0;\n     -+\tconst char *options[3];\n     -+\n     -+\tif (opt1)\n     -+\t\toptions[count++] = opt1_name;\n     -+\tif (opt2)\n     -+\t\toptions[count++] = opt2_name;\n     -+\tif (opt3)\n     -+\t\toptions[count++] = opt3_name;\n     -+\tif (count > 2)\n     -+\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name);\n     -+\telse if (count > 1)\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n     -+}\n     -+\n     -+void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n     -+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n     -+\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n     -+\t\t\t\t\t\t\t  int opt4, const char *opt4_name)\n     ++void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n     ++\t\t\t       int opt2, const char *opt2_name,\n     ++\t\t\t       int opt3, const char *opt3_name,\n     ++\t\t\t       int opt4, const char *opt4_name)\n      +{\n      +\tint count = 0;\n      +\tconst char *options[4];\n     @@ parse-options.c: void NORETURN usage_msg_opt(const char *msg,\n      +\t\toptions[count++] = opt4_name;\n      +\tswitch (count) {\n      +\tcase 4:\n     -+\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name, opt4_name);\n     ++\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"),\n     ++\t\t    opt1_name, opt2_name, opt3_name, opt4_name);\n      +\t\tbreak;\n      +\tcase 3:\n     -+\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), options[0], options[1], options[2]);\n     ++\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"),\n     ++\t\t    options[0], options[1], options[2]);\n      +\t\tbreak;\n      +\tcase 2:\n     -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n     ++\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n     ++\t\t    options[0], options[1]);\n      +\t\tbreak;\n      +\tdefault:\n      +\t\tbreak;\n     @@ parse-options.c: void NORETURN usage_msg_opt(const char *msg,\n      +}\n      \n       ## parse-options.h ##\n     +@@ parse-options.h: NORETURN void usage_msg_opt(const char *msg,\n     + \t\t\t    const char * const *usagestr,\n     + \t\t\t    const struct option *options);\n     + \n     ++void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n     ++\t\t\t       int opt2, const char *opt2_name,\n     ++\t\t\t       int opt3, const char *opt3_name,\n     ++\t\t\t       int opt4, const char *opt4_name);\n     ++\n     ++\n     ++inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,\n     ++\t\t\t\t      int opt2, const char *opt2_name,\n     ++\t\t\t\t      int opt3, const char *opt3_name)\n     ++{\n     ++\tdie_for_incompatible_opt4(opt1, opt1_name,\n     ++\t\t\t\t  opt2, opt2_name,\n     ++\t\t\t\t  opt3, opt3_name,\n     ++\t\t\t\t  0, \"\");\n     ++}\n     ++\n     + /*\n     +  * Use these assertions for callbacks that expect to be called with NONEG and\n     +  * NOARG respectively, and do not otherwise handle the \"unset\" and \"arg\"\n      @@ parse-options.h: int parse_opt_tracking_mode(const struct option *, const char *, int);\n       #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n       #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n       \n     -+void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n     -+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n     -+\t\t\t\t\t\t\t  int opt3, const char *opt3_name);\n     -+\n     -+void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n     -+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n     -+\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n     -+\t\t\t\t\t\t\t  int opt4, const char *opt4_name);\n      +\n       #endif\n      \n 2:  4c860698d69 ! 2:  078336ae418 i18n: factorize \"invalid value\" messages\n     @@ builtin/am.c: static int parse_opt_patchformat(const struct option *opt, const c\n       \t */\n       \telse\n      -\t\treturn error(_(\"Invalid value for --patch-format: %s\"), arg);\n     -+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--patch-format\", arg);\n     ++\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n     ++\t\t\t     \"--patch-format\", arg);\n       \treturn 0;\n       }\n       \n     @@ builtin/am.c: static int parse_opt_show_current_patch(const struct option *opt,\n       \t\tif (new_value >= ARRAY_SIZE(valid_modes))\n      -\t\t\treturn error(_(\"Invalid value for --show-current-patch: %s\"), arg);\n      +\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n     -+\t\t\t\t\t\t \"--show-current-patch\", arg);\n     ++\t\t\t\t     \"--show-current-patch\", arg);\n       \t}\n       \n       \tif (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)\n     @@ builtin/blame.c: static int git_blame_config(const char *var, const char *value,\n      -\t\t\twarning(_(\"invalid color '%s' in color.blame.repeatedLines\"),\n      -\t\t\t\tvalue);\n      +\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n     -+\t\t\t\t\t\"color.blame.repeatedLines\", value);\n     ++\t\t\t\t\"color.blame.repeatedLines\", value);\n       \t\treturn 0;\n       \t}\n       \tif (!strcmp(var, \"color.blame.highlightrecent\")) {\n     @@ builtin/blame.c: static int git_blame_config(const char *var, const char *value,\n       \t\t\t\t\t    OUTPUT_SHOW_AGE_WITH_COLOR);\n       \t\t} else {\n      -\t\t\twarning(_(\"invalid value for blame.coloring\"));\n     -+\t\t\twarning(_(\"invalid value for '%s': '%s'\"), \"blame.coloring\", value);\n     ++\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n     ++\t\t\t\t\"blame.coloring\", value);\n       \t\t\treturn 0;\n       \t\t}\n       \t}\n     @@ builtin/fetch.c: static void prepare_format_display(struct ref *ref_map)\n      -\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n      -\t\t    format);\n      +\t\tdie(_(\"invalid value for '%s': '%s'\"),\n     -+\t\t\t  \"fetch.output\", format);\n     ++\t\t    \"fetch.output\", format);\n       \n       \tfor (rm = ref_map; rm; rm = rm->next) {\n       \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n     @@ gpg-interface.c: int git_gpg_config(const char *var, const char *value, void *cb\n       \t\tfmt = get_format_by_name(value);\n       \t\tif (!fmt)\n      -\t\t\treturn error(\"unsupported value for %s: %s\",\n     -+\t\t\treturn error(\"invalid value for '%s': '%s'\",\n     ++\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n       \t\t\t\t     var, value);\n       \t\tuse_format = fmt;\n       \t\treturn 0;\n     @@ gpg-interface.c: int git_gpg_config(const char *var, const char *value, void *cb\n       \n       \t\tif (ret)\n      -\t\t\treturn error(\"unsupported value for %s: %s\", var,\n     -+\t\t\treturn error(\"invalid value for '%s': '%s'\", var,\n     - \t\t\t\t     value);\n     +-\t\t\t\t     value);\n     ++\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n     ++\t\t\t\t     var, value);\n       \t\treturn 0;\n       \t}\n     + \n      \n       ## ls-refs.c ##\n      @@ ls-refs.c: static void ensure_config_read(void)\n     @@ ls-refs.c: static void ensure_config_read(void)\n       \t\t\t/* do nothing */\n       \t\t} else {\n      -\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n     -+\t\t\tdie(_(\"invalid value for '%s': '%s'\"), \"lsrefs.unborn\", str);\n     ++\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n     ++\t\t\t    \"lsrefs.unborn\", str);\n       \t\t}\n       \t}\n       \tconfig_read = 1;\n     @@ parallel-checkout.c: void get_parallel_checkout_configs(int *num_workers, int *t\n       \tif (env_workers && *env_workers) {\n       \t\tif (strtol_i(env_workers, 10, num_workers)) {\n      -\t\t\tdie(\"invalid value for GIT_TEST_CHECKOUT_WORKERS: '%s'\",\n     +-\t\t\t    env_workers);\n      +\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n     -+\t\t\t\t\"GIT_TEST_CHECKOUT_WORKERS\",\n     - \t\t\t    env_workers);\n     ++\t\t\t    \"GIT_TEST_CHECKOUT_WORKERS\", env_workers);\n       \t\t}\n       \t\tif (*num_workers < 1)\n     + \t\t\t*num_workers = online_cpus();\n      \n       ## sequencer.c ##\n      @@ sequencer.c: static int populate_opts_cb(const char *key, const char *value, void *data)\n     @@ setup.c: static enum extension_result handle_extension(const char *var,\n       \t\tformat = hash_algo_by_name(value);\n       \t\tif (format == GIT_HASH_UNKNOWN)\n      -\t\t\treturn error(\"invalid value for 'extensions.objectformat'\");\n     -+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"extensions.objectformat\", value);\n     ++\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n     ++\t\t\t\t     \"extensions.objectformat\", value);\n       \t\tdata->hash_algo = format;\n       \t\treturn EXTENSION_OK;\n       \t}\n 3:  376f8b56908 = 3:  d5ddd903df7 i18n: remove from i18n strings that do not hold translatable parts\n 4:  8b0057f6180 = 4:  b8e80e178ee i18n: transfer variables into placeholders in command synopsis\n\n-- \ngitgitgadget\n"},{"id":"447318","messageId":"aac3f9821c7f8927ab7cb65bd5c443f5a0174068.1643580113.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v3.git.1643580113.gitgitgadget@gmail.com","subject":"[PATCH v3 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-30T22:01:50Z","receivedAt":"2022-01-30T22:02:01Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nFind more incompatible options to factorize.\n\nWhen more than two options are mutually exclusive, print the ones\nwhich are actually on the command line.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/commit.c                          | 35 ++++++++++-------------\n builtin/difftool.c                        |  5 ++--\n builtin/grep.c                            |  8 ++----\n builtin/log.c                             |  5 ++--\n builtin/merge-base.c                      |  6 ++--\n parse-options.c                           | 34 ++++++++++++++++++++++\n parse-options.h                           | 17 +++++++++++\n t/t7500-commit-template-squash-signoff.sh |  2 +-\n 8 files changed, 80 insertions(+), 32 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex b9ed0374e30..33ca9e99c80 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1242,8 +1242,6 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\t\t\t      struct commit *current_head,\n \t\t\t\t      struct wt_status *s)\n {\n-\tint f = 0;\n-\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \tfinalize_deferred_config(s);\n \n@@ -1251,7 +1249,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tforce_author = find_author_by_nickname(force_author);\n \n \tif (force_author && renew_authorship)\n-\t\tdie(_(\"Using both --reset-author and --author does not make sense\"));\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--reset-author\", \"--author\");\n \n \tif (logfile || have_option_m || use_message)\n \t\tuse_editor = 0;\n@@ -1268,20 +1266,16 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\t\tdie(_(\"You are in the middle of a rebase -- cannot amend.\"));\n \t}\n \tif (fixup_message && squash_message)\n-\t\tdie(_(\"Options --squash and --fixup cannot be used together\"));\n-\tif (use_message)\n-\t\tf++;\n-\tif (edit_message)\n-\t\tf++;\n-\tif (fixup_message)\n-\t\tf++;\n-\tif (logfile)\n-\t\tf++;\n-\tif (f > 1)\n-\t\tdie(_(\"Only one of -c/-C/-F/--fixup can be used.\"));\n-\tif (have_option_m && (edit_message || use_message || logfile))\n-\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n-\tif (f || have_option_m)\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n+\tdie_for_incompatible_opt4(!!use_message, \"-C\",\n+\t\t\t\t  !!edit_message, \"-c\",\n+\t\t\t\t  !!logfile, \"-F\",\n+\t\t\t\t  !!fixup_message, \"--fixup\");\n+\tdie_for_incompatible_opt4(have_option_m, \"-m\",\n+\t\t\t\t  !!edit_message, \"-c\",\n+\t\t\t\t  !!use_message, \"-C\",\n+\t\t\t\t  !!logfile, \"-F\");\n+\tif (use_message || edit_message || logfile ||fixup_message || have_option_m)\n \t\ttemplate_file = NULL;\n \tif (edit_message)\n \t\tuse_message = edit_message;\n@@ -1306,9 +1300,10 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \tif (patch_interactive)\n \t\tinteractive = 1;\n \n-\tif (also + only + all + interactive > 1)\n-\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n-\n+\tdie_for_incompatible_opt4(also, \"-i/--include\",\n+\t\t\t\t  only, \"-o/--only\",\n+\t\t\t\t  all, \"-a/--all\",\n+\t\t\t\t  interactive, \"--interactive/-p/--patch\");\n \tif (fixup_message) {\n \t\t/*\n \t\t * We limit --fixup's suboptions to only alpha characters.\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex c79fbbf67e5..faa3507369a 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -732,8 +732,9 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n \t} else if (dir_diff)\n \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--dir-diff\", \"--no-index\");\n \n-\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n-\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n+\tdie_for_incompatible_opt3(use_gui_tool, \"--gui\",\n+\t\t\t\t  !!difftool_cmd, \"--tool\",\n+\t\t\t\t  !!extcmd, \"--extcmd\");\n \n \tif (use_gui_tool)\n \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 9e34a820ad4..88144f06300 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -1167,11 +1167,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \tif (!show_in_pager && !opt.status_only)\n \t\tsetup_pager();\n \n-\tif (!use_index && (untracked || cached))\n-\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n-\n-\tif (untracked && cached)\n-\t\tdie(_(\"--untracked cannot be used with --cached\"));\n+\tdie_for_incompatible_opt3(!use_index, \"--no-index\",\n+\t\t\t\t  untracked, \"--untracked\",\n+\t\t\t\t  cached, \"--cached\");\n \n \tif (!use_index || untracked) {\n \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 4b493408cc5..970aa3483c4 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1978,8 +1978,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.show_notes)\n \t\tload_display_notes(&rev.notes_opt);\n \n-\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n-\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n+\tdie_for_incompatible_opt3(use_stdout, \"--stdout\",\n+\t\t\t\t  rev.diffopt.close_file, \"--output\",\n+\t\t\t\t  !!output_directory, \"--output-directory\");\n \n \tif (use_stdout) {\n \t\tsetup_pager();\ndiff --git a/builtin/merge-base.c b/builtin/merge-base.c\nindex 6719ac198dc..26b84980dbd 100644\n--- a/builtin/merge-base.c\n+++ b/builtin/merge-base.c\n@@ -159,12 +159,14 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)\n \t\tif (argc < 2)\n \t\t\tusage_with_options(merge_base_usage, options);\n \t\tif (show_all)\n-\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n+\t\t\t    \"--is-ancestor\", \"--all\");\n \t\treturn handle_is_ancestor(argc, argv);\n \t}\n \n \tif (cmdmode == 'r' && show_all)\n-\t\tdie(\"--independent cannot be used with --all\");\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n+\t\t    \"--independent\", \"--all\");\n \n \tif (cmdmode == 'o')\n \t\treturn handle_octopus(argc, argv, show_all);\ndiff --git a/parse-options.c b/parse-options.c\nindex a8283037be9..276e3911a74 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -1079,3 +1079,37 @@ void NORETURN usage_msg_opt(const char *msg,\n \tdie_message(\"%s\\n\", msg); /* The extra \\n is intentional */\n \tusage_with_options(usagestr, options);\n }\n+\n+void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n+\t\t\t       int opt2, const char *opt2_name,\n+\t\t\t       int opt3, const char *opt3_name,\n+\t\t\t       int opt4, const char *opt4_name)\n+{\n+\tint count = 0;\n+\tconst char *options[4];\n+\n+\tif (opt1)\n+\t\toptions[count++] = opt1_name;\n+\tif (opt2)\n+\t\toptions[count++] = opt2_name;\n+\tif (opt3)\n+\t\toptions[count++] = opt3_name;\n+\tif (opt4)\n+\t\toptions[count++] = opt4_name;\n+\tswitch (count) {\n+\tcase 4:\n+\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"),\n+\t\t    opt1_name, opt2_name, opt3_name, opt4_name);\n+\t\tbreak;\n+\tcase 3:\n+\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"),\n+\t\t    options[0], options[1], options[2]);\n+\t\tbreak;\n+\tcase 2:\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n+\t\t    options[0], options[1]);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\ndiff --git a/parse-options.h b/parse-options.h\nindex e22846d3b7b..68f7ae9c31a 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -225,6 +225,22 @@ NORETURN void usage_msg_opt(const char *msg,\n \t\t\t    const char * const *usagestr,\n \t\t\t    const struct option *options);\n \n+void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n+\t\t\t       int opt2, const char *opt2_name,\n+\t\t\t       int opt3, const char *opt3_name,\n+\t\t\t       int opt4, const char *opt4_name);\n+\n+\n+inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,\n+\t\t\t\t      int opt2, const char *opt2_name,\n+\t\t\t\t      int opt3, const char *opt3_name)\n+{\n+\tdie_for_incompatible_opt4(opt1, opt1_name,\n+\t\t\t\t  opt2, opt2_name,\n+\t\t\t\t  opt3, opt3_name,\n+\t\t\t\t  0, \"\");\n+}\n+\n /*\n  * Use these assertions for callbacks that expect to be called with NONEG and\n  * NOARG respectively, and do not otherwise handle the \"unset\" and \"arg\"\n@@ -339,4 +355,5 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);\n #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n \n+\n #endif\ndiff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\nindex 91964653a0b..5fcaa0b4f2a 100755\n--- a/t/t7500-commit-template-squash-signoff.sh\n+++ b/t/t7500-commit-template-squash-signoff.sh\n@@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '\n '\n \n test_expect_success '--fixup=reword: -F give error message' '\n-\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n+\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used together\" >expect &&\n \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n \ttest_cmp expect actual\n '\n-- \ngitgitgadget\n\n"},{"id":"447319","messageId":"078336ae418309ee65cba0ace53edcbdebcd8116.1643580113.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v3.git.1643580113.gitgitgadget@gmail.com","subject":"[PATCH v3 2/4] i18n: factorize \"invalid value\" messages","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-30T22:01:51Z","receivedAt":"2022-01-30T22:02:01Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nUse the same message when an invalid value is passed to a command line\noption or a configuration variable.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/am.c           | 8 +++++---\n builtin/blame.c        | 7 ++++---\n builtin/fetch.c        | 4 ++--\n builtin/pack-objects.c | 2 +-\n builtin/pull.c         | 6 +++---\n builtin/push.c         | 2 +-\n builtin/send-pack.c    | 2 +-\n diff-merges.c          | 2 +-\n gpg-interface.c        | 6 +++---\n ls-refs.c              | 3 ++-\n parallel-checkout.c    | 4 ++--\n sequencer.c            | 2 +-\n setup.c                | 3 ++-\n submodule-config.c     | 2 +-\n t/t4150-am.sh          | 2 +-\n 15 files changed, 30 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex b6be1f1cb11..97dbeb8e49e 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -199,7 +199,7 @@ static int am_option_parse_empty(const struct option *opt,\n \telse if (!strcmp(arg, \"keep\"))\n \t\t*opt_value = KEEP_EMPTY_COMMIT;\n \telse\n-\t\treturn error(_(\"Invalid value for --empty: %s\"), arg);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n \n \treturn 0;\n }\n@@ -2239,7 +2239,8 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int\n \t * when you add new options\n \t */\n \telse\n-\t\treturn error(_(\"Invalid value for --patch-format: %s\"), arg);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t     \"--patch-format\", arg);\n \treturn 0;\n }\n \n@@ -2282,7 +2283,8 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar\n \t\t\t\tbreak;\n \t\t}\n \t\tif (new_value >= ARRAY_SIZE(valid_modes))\n-\t\t\treturn error(_(\"Invalid value for --show-current-patch: %s\"), arg);\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t     \"--show-current-patch\", arg);\n \t}\n \n \tif (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 7fafeac4081..9c142f17a2e 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -721,8 +721,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!strcmp(var, \"color.blame.repeatedlines\")) {\n \t\tif (color_parse_mem(value, strlen(value), repeated_meta_color))\n-\t\t\twarning(_(\"invalid color '%s' in color.blame.repeatedLines\"),\n-\t\t\t\tvalue);\n+\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\"color.blame.repeatedLines\", value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"color.blame.highlightrecent\")) {\n@@ -739,7 +739,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t\t\tcoloring_mode &= ~(OUTPUT_COLOR_LINE |\n \t\t\t\t\t    OUTPUT_SHOW_AGE_WITH_COLOR);\n \t\t} else {\n-\t\t\twarning(_(\"invalid value for blame.coloring\"));\n+\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\"blame.coloring\", value);\n \t\t\treturn 0;\n \t\t}\n \t}\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 5f06b21f8e9..8be19bb8799 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -763,8 +763,8 @@ static void prepare_format_display(struct ref *ref_map)\n \telse if (!strcasecmp(format, \"compact\"))\n \t\tcompact_format = 1;\n \telse\n-\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n-\t\t    format);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t    \"fetch.output\", format);\n \n \tfor (rm = ref_map; rm; rm = rm->next) {\n \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex ba2006f2212..192c3ca305e 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3504,7 +3504,7 @@ static int option_parse_missing_action(const struct option *opt,\n \t\treturn 0;\n \t}\n \n-\tdie(_(\"invalid value for --missing\"));\n+\tdie(_(\"invalid value for '%s': '%s'\"), \"--missing\", arg);\n \treturn 0;\n }\n \ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 100cbf9fb85..e54a0ccadc6 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -42,9 +42,9 @@ static enum rebase_type parse_config_rebase(const char *key, const char *value,\n \t\treturn v;\n \n \tif (fatal)\n-\t\tdie(_(\"Invalid value for %s: %s\"), key, value);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"), key, value);\n \telse\n-\t\terror(_(\"Invalid value for %s: %s\"), key, value);\n+\t\terror(_(\"invalid value for '%s': '%s'\"), key, value);\n \n \treturn REBASE_INVALID;\n }\n@@ -318,7 +318,7 @@ static const char *config_get_ff(void)\n \tif (!strcmp(value, \"only\"))\n \t\treturn \"--ff-only\";\n \n-\tdie(_(\"Invalid value for pull.ff: %s\"), value);\n+\tdie(_(\"invalid value for '%s': '%s'\"), \"pull.ff\", value);\n }\n \n /**\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 359db90321c..cad997965a7 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -486,7 +486,7 @@ static int git_push_config(const char *k, const char *v, void *cb)\n \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n \t\t\t\t\tset_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);\n \t\t\t\telse\n-\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n+\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n \t\t\t}\n \t\t}\n \t} else if (!strcmp(k, \"push.recursesubmodules\")) {\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex 69c432ef1a6..64962be0168 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -145,7 +145,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)\n \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n \t\t\t\t\targs.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;\n \t\t\t\telse\n-\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n+\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n \t\t\t}\n \t\t}\n \t}\ndiff --git a/diff-merges.c b/diff-merges.c\nindex 5060ccd890b..cd6c102a0d5 100644\n--- a/diff-merges.c\n+++ b/diff-merges.c\n@@ -67,7 +67,7 @@ static void set_diff_merges(struct rev_info *revs, const char *optarg)\n \tdiff_merges_setup_func_t func = func_by_opt(optarg);\n \n \tif (!func)\n-\t\tdie(_(\"unknown value for --diff-merges: %s\"), optarg);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"), \"--diff-merges\", optarg);\n \n \tfunc(revs);\n \ndiff --git a/gpg-interface.c b/gpg-interface.c\nindex b52eb0e2e04..4e084b08f67 100644\n--- a/gpg-interface.c\n+++ b/gpg-interface.c\n@@ -702,7 +702,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n \t\t\treturn config_error_nonbool(var);\n \t\tfmt = get_format_by_name(value);\n \t\tif (!fmt)\n-\t\t\treturn error(\"unsupported value for %s: %s\",\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n \t\t\t\t     var, value);\n \t\tuse_format = fmt;\n \t\treturn 0;\n@@ -717,8 +717,8 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n \t\tfree(trust);\n \n \t\tif (ret)\n-\t\t\treturn error(\"unsupported value for %s: %s\", var,\n-\t\t\t\t     value);\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t     var, value);\n \t\treturn 0;\n \t}\n \ndiff --git a/ls-refs.c b/ls-refs.c\nindex 54078323dcb..98e69373c84 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -34,7 +34,8 @@ static void ensure_config_read(void)\n \t\t} else if (!strcmp(str, \"ignore\")) {\n \t\t\t/* do nothing */\n \t\t} else {\n-\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n+\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t    \"lsrefs.unborn\", str);\n \t\t}\n \t}\n \tconfig_read = 1;\ndiff --git a/parallel-checkout.c b/parallel-checkout.c\nindex 8dd7e7bad40..31a3d0ee1bf 100644\n--- a/parallel-checkout.c\n+++ b/parallel-checkout.c\n@@ -39,8 +39,8 @@ void get_parallel_checkout_configs(int *num_workers, int *threshold)\n \n \tif (env_workers && *env_workers) {\n \t\tif (strtol_i(env_workers, 10, num_workers)) {\n-\t\t\tdie(\"invalid value for GIT_TEST_CHECKOUT_WORKERS: '%s'\",\n-\t\t\t    env_workers);\n+\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t    \"GIT_TEST_CHECKOUT_WORKERS\", env_workers);\n \t\t}\n \t\tif (*num_workers < 1)\n \t\t\t*num_workers = online_cpus();\ndiff --git a/sequencer.c b/sequencer.c\nindex 5213d16e971..683f5392eba 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2806,7 +2806,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\treturn error(_(\"invalid key: %s\"), key);\n \n \tif (!error_flag)\n-\t\treturn error(_(\"invalid value for %s: %s\"), key, value);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), key, value);\n \n \treturn 0;\n }\ndiff --git a/setup.c b/setup.c\nindex af3b8c09abe..04ce33cdcd4 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -559,7 +559,8 @@ static enum extension_result handle_extension(const char *var,\n \t\t\treturn config_error_nonbool(var);\n \t\tformat = hash_algo_by_name(value);\n \t\tif (format == GIT_HASH_UNKNOWN)\n-\t\t\treturn error(\"invalid value for 'extensions.objectformat'\");\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t     \"extensions.objectformat\", value);\n \t\tdata->hash_algo = format;\n \t\treturn EXTENSION_OK;\n \t}\ndiff --git a/submodule-config.c b/submodule-config.c\nindex f95344028b5..fb95a026f48 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -496,7 +496,7 @@ static int parse_config(const char *var, const char *value, void *data)\n \t\telse if (parse_submodule_update_strategy(value,\n \t\t\t &submodule->update_strategy) < 0 ||\n \t\t\t submodule->update_strategy.type == SM_UPDATE_COMMAND)\n-\t\t\tdie(_(\"invalid value for %s\"), var);\n+\t\t\tdie(_(\"invalid value for '%s'\"), var);\n \t} else if (!strcmp(item.buf, \"shallow\")) {\n \t\tif (!me->overwrite && submodule->recommend_shallow != -1)\n \t\t\twarn_multiple_config(me->treeish_name, submodule->name,\ndiff --git a/t/t4150-am.sh b/t/t4150-am.sh\nindex 6caff0ca397..159fae8d016 100755\n--- a/t/t4150-am.sh\n+++ b/t/t4150-am.sh\n@@ -1169,7 +1169,7 @@ test_expect_success 'invalid when passing the --empty option alone' '\n \ttest_when_finished \"git am --abort || :\" &&\n \tgit checkout empty-commit^ &&\n \ttest_must_fail git am --empty empty-commit.patch 2>err &&\n-\techo \"error: Invalid value for --empty: empty-commit.patch\" >expected &&\n+\techo \"error: invalid value for '\\''--empty'\\'': '\\''empty-commit.patch'\\''\" >expected &&\n \ttest_cmp expected err\n '\n \n-- \ngitgitgadget\n\n"},{"id":"447320","messageId":"b8e80e178eeb2fccd9e10d783c7cf4b5f4cc80e4.1643580113.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v3.git.1643580113.gitgitgadget@gmail.com","subject":"[PATCH v3 4/4] i18n: transfer variables into placeholders in command synopsis","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-30T22:01:53Z","receivedAt":"2022-01-30T22:02:03Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nThis applies the style guide for documentation.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/fast-export.c | 2 +-\n builtin/reflog.c      | 4 ++--\n builtin/rev-list.c    | 2 +-\n 3 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 9f1c730e587..510139e9b54 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -26,7 +26,7 @@\n #include \"commit-slab.h\"\n \n static const char *fast_export_usage[] = {\n-\tN_(\"git fast-export [rev-list-opts]\"),\n+\tN_(\"git fast-export [<rev-list-opts>]\"),\n \tNULL\n };\n \ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex ee5ee8d8cf6..343a10d371b 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -17,10 +17,10 @@ static const char reflog_expire_usage[] =\n N_(\"git reflog expire [--expire=<time>] \"\n    \"[--expire-unreachable=<time>] \"\n    \"[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] \"\n-   \"[--verbose] [--all] <refs>...\");\n+   \"[--verbose] [--all] <ref>...\");\n static const char reflog_delete_usage[] =\n N_(\"git reflog delete [--rewrite] [--updateref] \"\n-   \"[--dry-run | -n] [--verbose] <refs>...\");\n+   \"[--dry-run | -n] [--verbose] <ref>...\");\n static const char reflog_exists_usage[] =\n N_(\"git reflog exists <ref>\");\n \ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 777558e9b06..38528c7f156 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -20,7 +20,7 @@\n #include \"packfile.h\"\n \n static const char rev_list_usage[] =\n-\"git rev-list [OPTION] <commit-id>... [ -- paths... ]\\n\"\n+\"git rev-list [<options>] <commit-id>... [-- <path>...]\\n\"\n \"  limiting output:\\n\"\n \"    --max-count=<n>\\n\"\n \"    --max-age=<epoch>\\n\"\n-- \ngitgitgadget\n"},{"id":"447321","messageId":"d5ddd903df76b829dbaab41f9bea68595b5a65aa.1643580113.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v3.git.1643580113.gitgitgadget@gmail.com","subject":"[PATCH v3 3/4] i18n: remove from i18n strings that do not hold translatable parts","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-30T22:01:52Z","receivedAt":"2022-01-30T22:02:03Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n archive.c                    | 2 +-\n builtin/bisect--helper.c     | 6 +++---\n builtin/count-objects.c      | 2 +-\n builtin/hash-object.c        | 2 +-\n builtin/help.c               | 4 ++--\n builtin/mktag.c              | 2 +-\n builtin/mktree.c             | 2 +-\n builtin/notes.c              | 6 +++---\n builtin/prune-packed.c       | 2 +-\n builtin/rebase.c             | 2 +-\n builtin/reflog.c             | 2 +-\n builtin/remote.c             | 2 +-\n builtin/replace.c            | 2 +-\n builtin/sparse-checkout.c    | 8 ++++----\n builtin/stripspace.c         | 4 ++--\n builtin/submodule--helper.c  | 2 +-\n builtin/update-server-info.c | 2 +-\n 17 files changed, 26 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex d571249cf39..e29d0e00f6c 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -12,7 +12,7 @@\n \n static char const * const archive_usage[] = {\n \tN_(\"git archive [<options>] <tree-ish> [<path>...]\"),\n-\tN_(\"git archive --list\"),\n+\t\"git archive --list\",\n \tN_(\"git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]\"),\n \tN_(\"git archive --remote <repo> [--exec <cmd>] --list\"),\n \tNULL\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 28a2e6a5750..f962dbd4303 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -22,15 +22,15 @@ static GIT_PATH_FUNC(git_path_bisect_run, \"BISECT_RUN\")\n \n static const char * const git_bisect_helper_usage[] = {\n \tN_(\"git bisect--helper --bisect-reset [<commit>]\"),\n-\tN_(\"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]\"),\n+\t\"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]\",\n \tN_(\"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]\"\n \t\t\t\t\t    \" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]\"),\n-\tN_(\"git bisect--helper --bisect-next\"),\n+\t\"git bisect--helper --bisect-next\",\n \tN_(\"git bisect--helper --bisect-state (bad|new) [<rev>]\"),\n \tN_(\"git bisect--helper --bisect-state (good|old) [<rev>...]\"),\n \tN_(\"git bisect--helper --bisect-replay <filename>\"),\n \tN_(\"git bisect--helper --bisect-skip [(<rev>|<range>)...]\"),\n-\tN_(\"git bisect--helper --bisect-visualize\"),\n+\t\"git bisect--helper --bisect-visualize\",\n \tN_(\"git bisect--helper --bisect-run <cmd>...\"),\n \tNULL\n };\ndiff --git a/builtin/count-objects.c b/builtin/count-objects.c\nindex 3fae474f6f1..07b94195962 100644\n--- a/builtin/count-objects.c\n+++ b/builtin/count-objects.c\n@@ -87,7 +87,7 @@ static int print_alternate(struct object_directory *odb, void *data)\n }\n \n static char const * const count_objects_usage[] = {\n-\tN_(\"git count-objects [-v] [-H | --human-readable]\"),\n+\t\"git count-objects [-v] [-H | --human-readable]\",\n \tNULL\n };\n \ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex c7b3ad74c60..04e2442ec7c 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -81,7 +81,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n {\n \tstatic const char * const hash_object_usage[] = {\n \t\tN_(\"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>...\"),\n-\t\tN_(\"git hash-object  --stdin-paths\"),\n+\t\t\"git hash-object  --stdin-paths\",\n \t\tNULL\n \t};\n \tconst char *type = blob_type;\ndiff --git a/builtin/help.c b/builtin/help.c\nindex d387131dd83..b4f2ad3f94e 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -77,8 +77,8 @@ static struct option builtin_help_options[] = {\n static const char * const builtin_help_usage[] = {\n \tN_(\"git help [-a|--all] [--[no-]verbose]]\\n\"\n \t   \"         [[-i|--info] [-m|--man] [-w|--web]] [<command>]\"),\n-\tN_(\"git help [-g|--guides]\"),\n-\tN_(\"git help [-c|--config]\"),\n+\t\"git help [-g|--guides]\",\n+\t\"git help [-c|--config]\",\n \tNULL\n };\n \ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 3b2dbbb37e6..c7b905c614b 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -7,7 +7,7 @@\n #include \"config.h\"\n \n static char const * const builtin_mktag_usage[] = {\n-\tN_(\"git mktag\"),\n+\t\"git mktag\",\n \tNULL\n };\n static int option_strict = 1;\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex ae78ca1c029..8bdaada922a 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -63,7 +63,7 @@ static void write_tree(struct object_id *oid)\n }\n \n static const char *mktree_usage[] = {\n-\tN_(\"git mktree [-z] [--missing] [--batch]\"),\n+\t\"git mktree [-z] [--missing] [--batch]\",\n \tNULL\n };\n \ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 05d60483e82..f99593a1853 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -32,8 +32,8 @@ static const char * const git_notes_usage[] = {\n \tN_(\"git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]\"),\n \tN_(\"git notes [--ref <notes-ref>] show [<object>]\"),\n \tN_(\"git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>\"),\n-\tN_(\"git notes merge --commit [-v | -q]\"),\n-\tN_(\"git notes merge --abort [-v | -q]\"),\n+\t\"git notes merge --commit [-v | -q]\",\n+\t\"git notes merge --abort [-v | -q]\",\n \tN_(\"git notes [--ref <notes-ref>] remove [<object>...]\"),\n \tN_(\"git notes [--ref <notes-ref>] prune [-n] [-v]\"),\n \tN_(\"git notes [--ref <notes-ref>] get-ref\"),\n@@ -89,7 +89,7 @@ static const char * const git_notes_prune_usage[] = {\n };\n \n static const char * const git_notes_get_ref_usage[] = {\n-\tN_(\"git notes get-ref\"),\n+\t\"git notes get-ref\",\n \tNULL\n };\n \ndiff --git a/builtin/prune-packed.c b/builtin/prune-packed.c\nindex b7b9281a8ce..da3273a268b 100644\n--- a/builtin/prune-packed.c\n+++ b/builtin/prune-packed.c\n@@ -3,7 +3,7 @@\n #include \"prune-packed.h\"\n \n static const char * const prune_packed_usage[] = {\n-\tN_(\"git prune-packed [-n | --dry-run] [-q | --quiet]\"),\n+\t\"git prune-packed [-n | --dry-run] [-q | --quiet]\",\n \tNULL\n };\n \ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 36490d06c8a..64796c6a785 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -36,7 +36,7 @@ static char const * const builtin_rebase_usage[] = {\n \t\t\"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]\"),\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] \"\n \t\t\"--root [<branch>]\"),\n-\tN_(\"git rebase --continue | --abort | --skip | --edit-todo\"),\n+\t\"git rebase --continue | --abort | --skip | --edit-todo\",\n \tNULL\n };\n \ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex a4b1dd27e13..ee5ee8d8cf6 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -800,7 +800,7 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)\n  */\n \n static const char reflog_usage[] =\n-N_(\"git reflog [ show | expire | delete | exists ]\");\n+\"git reflog [ show | expire | delete | exists ]\";\n \n int cmd_reflog(int argc, const char **argv, const char *prefix)\n {\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 299c466116d..6f27ddc47bd 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -14,7 +14,7 @@\n #include \"commit-reach.h\"\n \n static const char * const builtin_remote_usage[] = {\n-\tN_(\"git remote [-v | --verbose]\"),\n+\t\"git remote [-v | --verbose]\",\n \tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n \tN_(\"git remote rename <old> <new>\"),\n \tN_(\"git remote remove <name>\"),\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 6ff1734d587..ac92337c0ec 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -22,7 +22,7 @@ static const char * const git_replace_usage[] = {\n \tN_(\"git replace [-f] <object> <replacement>\"),\n \tN_(\"git replace [-f] --edit <object>\"),\n \tN_(\"git replace [-f] --graft <commit> [<parent>...]\"),\n-\tN_(\"git replace [-f] --convert-graft-file\"),\n+\t\"git replace [-f] --convert-graft-file\",\n \tN_(\"git replace -d <object>...\"),\n \tN_(\"git replace [--format=<format>] [-l [<pattern>]]\"),\n \tNULL\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 679c1070368..771c9869a1c 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -43,7 +43,7 @@ static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)\n }\n \n static char const * const builtin_sparse_checkout_list_usage[] = {\n-\tN_(\"git sparse-checkout list\"),\n+\t\"git sparse-checkout list\",\n \tNULL\n };\n \n@@ -419,7 +419,7 @@ static int update_modes(int *cone_mode, int *sparse_index)\n }\n \n static char const * const builtin_sparse_checkout_init_usage[] = {\n-\tN_(\"git sparse-checkout init [--cone] [--[no-]sparse-index]\"),\n+\t\"git sparse-checkout init [--cone] [--[no-]sparse-index]\",\n \tNULL\n };\n \n@@ -762,7 +762,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)\n }\n \n static char const * const builtin_sparse_checkout_reapply_usage[] = {\n-\tN_(\"git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]\"),\n+\t\"git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]\",\n \tNULL\n };\n \n@@ -800,7 +800,7 @@ static int sparse_checkout_reapply(int argc, const char **argv)\n }\n \n static char const * const builtin_sparse_checkout_disable_usage[] = {\n-\tN_(\"git sparse-checkout disable\"),\n+\t\"git sparse-checkout disable\",\n \tNULL\n };\n \ndiff --git a/builtin/stripspace.c b/builtin/stripspace.c\nindex be33eb83c1b..1e34cf2bebd 100644\n--- a/builtin/stripspace.c\n+++ b/builtin/stripspace.c\n@@ -15,8 +15,8 @@ static void comment_lines(struct strbuf *buf)\n }\n \n static const char * const stripspace_usage[] = {\n-\tN_(\"git stripspace [-s | --strip-comments]\"),\n-\tN_(\"git stripspace [-c | --comment-lines]\"),\n+\t\"git stripspace [-s | --strip-comments]\",\n+\t\"git stripspace [-c | --comment-lines]\",\n \tNULL\n };\n \ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c5d3fc3817f..b80aa9898ac 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2883,7 +2883,7 @@ static int module_config(int argc, const char **argv, const char *prefix)\n \tconst char *const git_submodule_helper_usage[] = {\n \t\tN_(\"git submodule--helper config <name> [<value>]\"),\n \t\tN_(\"git submodule--helper config --unset <name>\"),\n-\t\tN_(\"git submodule--helper config --check-writeable\"),\n+\t\t\"git submodule--helper config --check-writeable\",\n \t\tNULL\n \t};\n \ndiff --git a/builtin/update-server-info.c b/builtin/update-server-info.c\nindex 4321a344567..880fffec587 100644\n--- a/builtin/update-server-info.c\n+++ b/builtin/update-server-info.c\n@@ -4,7 +4,7 @@\n #include \"parse-options.h\"\n \n static const char * const update_server_info_usage[] = {\n-\tN_(\"git update-server-info [--force]\"),\n+\t\"git update-server-info [--force]\",\n \tNULL\n };\n \n-- \ngitgitgadget\n\n"},{"id":"447322","messageId":"38b59dec-0ae5-ebed-18f0-bec0a3aa3333@kdbg.org","threadId":"57287","inReplyTo":"aac3f9821c7f8927ab7cb65bd5c443f5a0174068.1643580113.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-01-31T07:15:36Z","receivedAt":"2022-01-31T07:15:47Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 30.01.22 um 23:01 schrieb Jean-Noël Avila via GitGitGadget:\n> diff --git a/parse-options.h b/parse-options.h\n> index e22846d3b7b..68f7ae9c31a 100644\n> --- a/parse-options.h\n> +++ b/parse-options.h\n\n> @@ -339,4 +355,5 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);\n>  #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n>  #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n>  \n> +\n>  #endif\n\nAn unnecessary remnant from the earlier round?\n\nExcept for this, the patch looks good.\n\n-- Hannes\n"},{"id":"447323","messageId":"7f68c92f-b74f-2182-faa9-1a01d2e9b235@kdbg.org","threadId":"57287","inReplyTo":"pull.1123.v3.git.1643580113.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 0/4] Factorize i18n","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-01-31T07:15:54Z","receivedAt":"2022-01-31T07:16:04Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 30.01.22 um 23:01 schrieb Jean-Noël Avila via GitGitGadget:\n> This is a small series of i18n factorizations. Again the idea is to:\n> \n>  * reduce the number of strings to translate\n>  * make life easier for translators by removing constant parts of the\n>    strings to translate\n>  * uniformize the UI by using a type of message for the a given type of\n>    condition.\n> \n> Changes since V1:\n> \n>  * factorize checks of more than 2 incompatible options into dedicated\n>    functions which list all the incompatible options present on the command\n>    line\n> \n> Changes since V2:\n> \n>  * switch to tab size 8\n>  * rename die_if* to die_for*\n>  * make 3 arg check a call to 4 arg check\n>  * switch another part to die_for_incompatible_opt4\n\nThis iteration looks good overall. There is an unnecessary empty-line\naddition hunk in 1/2, though.\n\n-- Hannes\n\n> \n> Jean-Noël Avila (4):\n>   i18n: factorize more 'incompatible options' messages\n>   i18n: factorize \"invalid value\" messages\n>   i18n: remove from i18n strings that do not hold translatable parts\n>   i18n: transfer variables into placeholders in command synopsis\n> \n>  archive.c                                 |  2 +-\n>  builtin/am.c                              |  8 ++++--\n>  builtin/bisect--helper.c                  |  6 ++--\n>  builtin/blame.c                           |  7 +++--\n>  builtin/commit.c                          | 35 ++++++++++-------------\n>  builtin/count-objects.c                   |  2 +-\n>  builtin/difftool.c                        |  5 ++--\n>  builtin/fast-export.c                     |  2 +-\n>  builtin/fetch.c                           |  4 +--\n>  builtin/grep.c                            |  8 ++----\n>  builtin/hash-object.c                     |  2 +-\n>  builtin/help.c                            |  4 +--\n>  builtin/log.c                             |  5 ++--\n>  builtin/merge-base.c                      |  6 ++--\n>  builtin/mktag.c                           |  2 +-\n>  builtin/mktree.c                          |  2 +-\n>  builtin/notes.c                           |  6 ++--\n>  builtin/pack-objects.c                    |  2 +-\n>  builtin/prune-packed.c                    |  2 +-\n>  builtin/pull.c                            |  6 ++--\n>  builtin/push.c                            |  2 +-\n>  builtin/rebase.c                          |  2 +-\n>  builtin/reflog.c                          |  6 ++--\n>  builtin/remote.c                          |  2 +-\n>  builtin/replace.c                         |  2 +-\n>  builtin/rev-list.c                        |  2 +-\n>  builtin/send-pack.c                       |  2 +-\n>  builtin/sparse-checkout.c                 |  8 +++---\n>  builtin/stripspace.c                      |  4 +--\n>  builtin/submodule--helper.c               |  2 +-\n>  builtin/update-server-info.c              |  2 +-\n>  diff-merges.c                             |  2 +-\n>  gpg-interface.c                           |  6 ++--\n>  ls-refs.c                                 |  3 +-\n>  parallel-checkout.c                       |  4 +--\n>  parse-options.c                           | 34 ++++++++++++++++++++++\n>  parse-options.h                           | 17 +++++++++++\n>  sequencer.c                               |  2 +-\n>  setup.c                                   |  3 +-\n>  submodule-config.c                        |  2 +-\n>  t/t4150-am.sh                             |  2 +-\n>  t/t7500-commit-template-squash-signoff.sh |  2 +-\n>  42 files changed, 140 insertions(+), 87 deletions(-)\n> \n> \n> base-commit: 5d01301f2b865aa8dba1654d3f447ce9d21db0b5\n> Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1123%2Fjnavila%2Ffactorize_i18n-v3\n> Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1123/jnavila/factorize_i18n-v3\n> Pull-Request: https://github.com/gitgitgadget/git/pull/1123\n> \n> Range-diff vs v2:\n> \n>  1:  844e01391e1 ! 1:  aac3f9821c7 i18n: factorize more 'incompatible options' messages\n>      @@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *ar\n>       -\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n>       -\tif (f || have_option_m)\n>       +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n>      -+\tdie_if_incompatible_opt4(!!use_message, \"-C\",\n>      -+\t\t\t\t\t\t\t !!edit_message, \"-c\",\n>      -+\t\t\t\t\t\t\t !!logfile, \"-F\",\n>      -+\t\t\t\t\t\t\t !!fixup_message, \"--fixup\");\n>      -+\tif (have_option_m) {\n>      -+\t\tif (edit_message)\n>      -+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-c\");\n>      -+\t\telse if  (use_message)\n>      -+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-C\");\n>      -+\t\telse if (logfile)\n>      -+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"-m\", \"-F\");\n>      -+\t}\n>      ++\tdie_for_incompatible_opt4(!!use_message, \"-C\",\n>      ++\t\t\t\t  !!edit_message, \"-c\",\n>      ++\t\t\t\t  !!logfile, \"-F\",\n>      ++\t\t\t\t  !!fixup_message, \"--fixup\");\n>      ++\tdie_for_incompatible_opt4(have_option_m, \"-m\",\n>      ++\t\t\t\t  !!edit_message, \"-c\",\n>      ++\t\t\t\t  !!use_message, \"-C\",\n>      ++\t\t\t\t  !!logfile, \"-F\");\n>       +\tif (use_message || edit_message || logfile ||fixup_message || have_option_m)\n>        \t\ttemplate_file = NULL;\n>        \tif (edit_message)\n>      @@ builtin/commit.c: static int parse_and_validate_options(int argc, const char *ar\n>       -\tif (also + only + all + interactive > 1)\n>       -\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n>       -\n>      -+\tdie_if_incompatible_opt4(also, \"-i/--include\",\n>      -+\t\t\t\t\t\t\t only, \"-o/--only\",\n>      -+\t\t\t\t\t\t\t all, \"-a/--all\",\n>      -+\t\t\t\t\t\t\t interactive, \"--interactive/-p/--patch\");\n>      ++\tdie_for_incompatible_opt4(also, \"-i/--include\",\n>      ++\t\t\t\t  only, \"-o/--only\",\n>      ++\t\t\t\t  all, \"-a/--all\",\n>      ++\t\t\t\t  interactive, \"--interactive/-p/--patch\");\n>        \tif (fixup_message) {\n>        \t\t/*\n>        \t\t * We limit --fixup's suboptions to only alpha characters.\n>      @@ builtin/difftool.c: int cmd_difftool(int argc, const char **argv, const char *pr\n>        \n>       -\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n>       -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n>      -+\tdie_if_incompatible_opt3(use_gui_tool, \"--gui\",\n>      -+\t\t\t\t\t\t\t !!difftool_cmd, \"--tool\",\n>      -+\t\t\t\t\t\t\t !!extcmd, \"--extcmd\");\n>      ++\tdie_for_incompatible_opt3(use_gui_tool, \"--gui\",\n>      ++\t\t\t\t  !!difftool_cmd, \"--tool\",\n>      ++\t\t\t\t  !!extcmd, \"--extcmd\");\n>        \n>        \tif (use_gui_tool)\n>        \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\n>      @@ builtin/grep.c: int cmd_grep(int argc, const char **argv, const char *prefix)\n>       -\n>       -\tif (untracked && cached)\n>       -\t\tdie(_(\"--untracked cannot be used with --cached\"));\n>      -+\tdie_if_incompatible_opt3(!use_index, \"--no-index\",\n>      -+\t\t\t\t\t\t\t untracked, \"--untracked\",\n>      -+\t\t\t\t\t\t\t cached, \"--cached\");\n>      ++\tdie_for_incompatible_opt3(!use_index, \"--no-index\",\n>      ++\t\t\t\t  untracked, \"--untracked\",\n>      ++\t\t\t\t  cached, \"--cached\");\n>        \n>        \tif (!use_index || untracked) {\n>        \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\n>      @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre\n>        \n>       -\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n>       -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n>      -+\tdie_if_incompatible_opt3(use_stdout, \"--stdout\",\n>      -+\t\t\t\t\t\t\t rev.diffopt.close_file, \"--output\",\n>      -+\t\t\t\t\t\t\t !!output_directory, \"--output-directory\");\n>      ++\tdie_for_incompatible_opt3(use_stdout, \"--stdout\",\n>      ++\t\t\t\t  rev.diffopt.close_file, \"--output\",\n>      ++\t\t\t\t  !!output_directory, \"--output-directory\");\n>        \n>        \tif (use_stdout) {\n>        \t\tsetup_pager();\n>      @@ builtin/merge-base.c: int cmd_merge_base(int argc, const char **argv, const char\n>        \t\t\tusage_with_options(merge_base_usage, options);\n>        \t\tif (show_all)\n>       -\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n>      -+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--is-ancestor\", \"--all\");\n>      ++\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n>      ++\t\t\t    \"--is-ancestor\", \"--all\");\n>        \t\treturn handle_is_ancestor(argc, argv);\n>        \t}\n>        \n>        \tif (cmdmode == 'r' && show_all)\n>       -\t\tdie(\"--independent cannot be used with --all\");\n>      -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\"--independent\", \"--all\");\n>      ++\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n>      ++\t\t    \"--independent\", \"--all\");\n>        \n>        \tif (cmdmode == 'o')\n>        \t\treturn handle_octopus(argc, argv, show_all);\n>      @@ parse-options.c: void NORETURN usage_msg_opt(const char *msg,\n>        \tusage_with_options(usagestr, options);\n>        }\n>       +\n>      -+void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n>      -+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n>      -+\t\t\t\t\t\t\t  int opt3, const char *opt3_name)\n>      -+{\n>      -+\tint count = 0;\n>      -+\tconst char *options[3];\n>      -+\n>      -+\tif (opt1)\n>      -+\t\toptions[count++] = opt1_name;\n>      -+\tif (opt2)\n>      -+\t\toptions[count++] = opt2_name;\n>      -+\tif (opt3)\n>      -+\t\toptions[count++] = opt3_name;\n>      -+\tif (count > 2)\n>      -+\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name);\n>      -+\telse if (count > 1)\n>      -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n>      -+}\n>      -+\n>      -+void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n>      -+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n>      -+\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n>      -+\t\t\t\t\t\t\t  int opt4, const char *opt4_name)\n>      ++void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n>      ++\t\t\t       int opt2, const char *opt2_name,\n>      ++\t\t\t       int opt3, const char *opt3_name,\n>      ++\t\t\t       int opt4, const char *opt4_name)\n>       +{\n>       +\tint count = 0;\n>       +\tconst char *options[4];\n>      @@ parse-options.c: void NORETURN usage_msg_opt(const char *msg,\n>       +\t\toptions[count++] = opt4_name;\n>       +\tswitch (count) {\n>       +\tcase 4:\n>      -+\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name, opt4_name);\n>      ++\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"),\n>      ++\t\t    opt1_name, opt2_name, opt3_name, opt4_name);\n>       +\t\tbreak;\n>       +\tcase 3:\n>      -+\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), options[0], options[1], options[2]);\n>      ++\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"),\n>      ++\t\t    options[0], options[1], options[2]);\n>       +\t\tbreak;\n>       +\tcase 2:\n>      -+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n>      ++\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n>      ++\t\t    options[0], options[1]);\n>       +\t\tbreak;\n>       +\tdefault:\n>       +\t\tbreak;\n>      @@ parse-options.c: void NORETURN usage_msg_opt(const char *msg,\n>       +}\n>       \n>        ## parse-options.h ##\n>      +@@ parse-options.h: NORETURN void usage_msg_opt(const char *msg,\n>      + \t\t\t    const char * const *usagestr,\n>      + \t\t\t    const struct option *options);\n>      + \n>      ++void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n>      ++\t\t\t       int opt2, const char *opt2_name,\n>      ++\t\t\t       int opt3, const char *opt3_name,\n>      ++\t\t\t       int opt4, const char *opt4_name);\n>      ++\n>      ++\n>      ++inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,\n>      ++\t\t\t\t      int opt2, const char *opt2_name,\n>      ++\t\t\t\t      int opt3, const char *opt3_name)\n>      ++{\n>      ++\tdie_for_incompatible_opt4(opt1, opt1_name,\n>      ++\t\t\t\t  opt2, opt2_name,\n>      ++\t\t\t\t  opt3, opt3_name,\n>      ++\t\t\t\t  0, \"\");\n>      ++}\n>      ++\n>      + /*\n>      +  * Use these assertions for callbacks that expect to be called with NONEG and\n>      +  * NOARG respectively, and do not otherwise handle the \"unset\" and \"arg\"\n>       @@ parse-options.h: int parse_opt_tracking_mode(const struct option *, const char *, int);\n>        #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n>        #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n>        \n>      -+void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n>      -+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n>      -+\t\t\t\t\t\t\t  int opt3, const char *opt3_name);\n>      -+\n>      -+void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n>      -+\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n>      -+\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n>      -+\t\t\t\t\t\t\t  int opt4, const char *opt4_name);\n>       +\n>        #endif\n>       \n>  2:  4c860698d69 ! 2:  078336ae418 i18n: factorize \"invalid value\" messages\n>      @@ builtin/am.c: static int parse_opt_patchformat(const struct option *opt, const c\n>        \t */\n>        \telse\n>       -\t\treturn error(_(\"Invalid value for --patch-format: %s\"), arg);\n>      -+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--patch-format\", arg);\n>      ++\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n>      ++\t\t\t     \"--patch-format\", arg);\n>        \treturn 0;\n>        }\n>        \n>      @@ builtin/am.c: static int parse_opt_show_current_patch(const struct option *opt,\n>        \t\tif (new_value >= ARRAY_SIZE(valid_modes))\n>       -\t\t\treturn error(_(\"Invalid value for --show-current-patch: %s\"), arg);\n>       +\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n>      -+\t\t\t\t\t\t \"--show-current-patch\", arg);\n>      ++\t\t\t\t     \"--show-current-patch\", arg);\n>        \t}\n>        \n>        \tif (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)\n>      @@ builtin/blame.c: static int git_blame_config(const char *var, const char *value,\n>       -\t\t\twarning(_(\"invalid color '%s' in color.blame.repeatedLines\"),\n>       -\t\t\t\tvalue);\n>       +\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n>      -+\t\t\t\t\t\"color.blame.repeatedLines\", value);\n>      ++\t\t\t\t\"color.blame.repeatedLines\", value);\n>        \t\treturn 0;\n>        \t}\n>        \tif (!strcmp(var, \"color.blame.highlightrecent\")) {\n>      @@ builtin/blame.c: static int git_blame_config(const char *var, const char *value,\n>        \t\t\t\t\t    OUTPUT_SHOW_AGE_WITH_COLOR);\n>        \t\t} else {\n>       -\t\t\twarning(_(\"invalid value for blame.coloring\"));\n>      -+\t\t\twarning(_(\"invalid value for '%s': '%s'\"), \"blame.coloring\", value);\n>      ++\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n>      ++\t\t\t\t\"blame.coloring\", value);\n>        \t\t\treturn 0;\n>        \t\t}\n>        \t}\n>      @@ builtin/fetch.c: static void prepare_format_display(struct ref *ref_map)\n>       -\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n>       -\t\t    format);\n>       +\t\tdie(_(\"invalid value for '%s': '%s'\"),\n>      -+\t\t\t  \"fetch.output\", format);\n>      ++\t\t    \"fetch.output\", format);\n>        \n>        \tfor (rm = ref_map; rm; rm = rm->next) {\n>        \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\n>      @@ gpg-interface.c: int git_gpg_config(const char *var, const char *value, void *cb\n>        \t\tfmt = get_format_by_name(value);\n>        \t\tif (!fmt)\n>       -\t\t\treturn error(\"unsupported value for %s: %s\",\n>      -+\t\t\treturn error(\"invalid value for '%s': '%s'\",\n>      ++\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n>        \t\t\t\t     var, value);\n>        \t\tuse_format = fmt;\n>        \t\treturn 0;\n>      @@ gpg-interface.c: int git_gpg_config(const char *var, const char *value, void *cb\n>        \n>        \t\tif (ret)\n>       -\t\t\treturn error(\"unsupported value for %s: %s\", var,\n>      -+\t\t\treturn error(\"invalid value for '%s': '%s'\", var,\n>      - \t\t\t\t     value);\n>      +-\t\t\t\t     value);\n>      ++\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n>      ++\t\t\t\t     var, value);\n>        \t\treturn 0;\n>        \t}\n>      + \n>       \n>        ## ls-refs.c ##\n>       @@ ls-refs.c: static void ensure_config_read(void)\n>      @@ ls-refs.c: static void ensure_config_read(void)\n>        \t\t\t/* do nothing */\n>        \t\t} else {\n>       -\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n>      -+\t\t\tdie(_(\"invalid value for '%s': '%s'\"), \"lsrefs.unborn\", str);\n>      ++\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n>      ++\t\t\t    \"lsrefs.unborn\", str);\n>        \t\t}\n>        \t}\n>        \tconfig_read = 1;\n>      @@ parallel-checkout.c: void get_parallel_checkout_configs(int *num_workers, int *t\n>        \tif (env_workers && *env_workers) {\n>        \t\tif (strtol_i(env_workers, 10, num_workers)) {\n>       -\t\t\tdie(\"invalid value for GIT_TEST_CHECKOUT_WORKERS: '%s'\",\n>      +-\t\t\t    env_workers);\n>       +\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n>      -+\t\t\t\t\"GIT_TEST_CHECKOUT_WORKERS\",\n>      - \t\t\t    env_workers);\n>      ++\t\t\t    \"GIT_TEST_CHECKOUT_WORKERS\", env_workers);\n>        \t\t}\n>        \t\tif (*num_workers < 1)\n>      + \t\t\t*num_workers = online_cpus();\n>       \n>        ## sequencer.c ##\n>       @@ sequencer.c: static int populate_opts_cb(const char *key, const char *value, void *data)\n>      @@ setup.c: static enum extension_result handle_extension(const char *var,\n>        \t\tformat = hash_algo_by_name(value);\n>        \t\tif (format == GIT_HASH_UNKNOWN)\n>       -\t\t\treturn error(\"invalid value for 'extensions.objectformat'\");\n>      -+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"extensions.objectformat\", value);\n>      ++\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n>      ++\t\t\t\t     \"extensions.objectformat\", value);\n>        \t\tdata->hash_algo = format;\n>        \t\treturn EXTENSION_OK;\n>        \t}\n>  3:  376f8b56908 = 3:  d5ddd903df7 i18n: remove from i18n strings that do not hold translatable parts\n>  4:  8b0057f6180 = 4:  b8e80e178ee i18n: transfer variables into placeholders in command synopsis\n> \n\n"},{"id":"447325","messageId":"f50e2e2d-8b33-a403-ad94-150b065a1531@gmail.com","threadId":"57287","inReplyTo":"aac3f9821c7f8927ab7cb65bd5c443f5a0174068.1643580113.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-01-31T10:56:54Z","receivedAt":"2022-01-31T10:57:05Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Jean-Noël\n\nOn 30/01/2022 22:01, Jean-Noël Avila via GitGitGadget wrote:\n> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n> \n> Find more incompatible options to factorize.\n> \n> When more than two options are mutually exclusive, print the ones\n> which are actually on the command line.\n\nI think this looks good and I'd be happy to see it go in as is with the \nblank line hunk removed. I like the way it now shows all the \nincompatible options that were on the command line, not just the first \ntwo. Using a function to handle the messages makes the code much nicer \nto read as well. I'm not sure that making die_for_incompatible_opt3() \nreally needs to be inline or that we need _for_ in the function names \nbut neither does any harm.\n\nBest Wishes\n\nPhillip\n\n> Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>\n> ---\n>   builtin/commit.c                          | 35 ++++++++++-------------\n>   builtin/difftool.c                        |  5 ++--\n>   builtin/grep.c                            |  8 ++----\n>   builtin/log.c                             |  5 ++--\n>   builtin/merge-base.c                      |  6 ++--\n>   parse-options.c                           | 34 ++++++++++++++++++++++\n>   parse-options.h                           | 17 +++++++++++\n>   t/t7500-commit-template-squash-signoff.sh |  2 +-\n>   8 files changed, 80 insertions(+), 32 deletions(-)\n> \n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index b9ed0374e30..33ca9e99c80 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1242,8 +1242,6 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>   \t\t\t\t      struct commit *current_head,\n>   \t\t\t\t      struct wt_status *s)\n>   {\n> -\tint f = 0;\n> -\n>   \targc = parse_options(argc, argv, prefix, options, usage, 0);\n>   \tfinalize_deferred_config(s);\n>   \n> @@ -1251,7 +1249,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>   \t\tforce_author = find_author_by_nickname(force_author);\n>   \n>   \tif (force_author && renew_authorship)\n> -\t\tdie(_(\"Using both --reset-author and --author does not make sense\"));\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--reset-author\", \"--author\");\n>   \n>   \tif (logfile || have_option_m || use_message)\n>   \t\tuse_editor = 0;\n> @@ -1268,20 +1266,16 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>   \t\t\tdie(_(\"You are in the middle of a rebase -- cannot amend.\"));\n>   \t}\n>   \tif (fixup_message && squash_message)\n> -\t\tdie(_(\"Options --squash and --fixup cannot be used together\"));\n> -\tif (use_message)\n> -\t\tf++;\n> -\tif (edit_message)\n> -\t\tf++;\n> -\tif (fixup_message)\n> -\t\tf++;\n> -\tif (logfile)\n> -\t\tf++;\n> -\tif (f > 1)\n> -\t\tdie(_(\"Only one of -c/-C/-F/--fixup can be used.\"));\n> -\tif (have_option_m && (edit_message || use_message || logfile))\n> -\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n> -\tif (f || have_option_m)\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n> +\tdie_for_incompatible_opt4(!!use_message, \"-C\",\n> +\t\t\t\t  !!edit_message, \"-c\",\n> +\t\t\t\t  !!logfile, \"-F\",\n> +\t\t\t\t  !!fixup_message, \"--fixup\");\n> +\tdie_for_incompatible_opt4(have_option_m, \"-m\",\n> +\t\t\t\t  !!edit_message, \"-c\",\n> +\t\t\t\t  !!use_message, \"-C\",\n> +\t\t\t\t  !!logfile, \"-F\");\n> +\tif (use_message || edit_message || logfile ||fixup_message || have_option_m)\n>   \t\ttemplate_file = NULL;\n>   \tif (edit_message)\n>   \t\tuse_message = edit_message;\n> @@ -1306,9 +1300,10 @@ static int parse_and_validate_options(int argc, const char *argv[],\n>   \tif (patch_interactive)\n>   \t\tinteractive = 1;\n>   \n> -\tif (also + only + all + interactive > 1)\n> -\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n> -\n> +\tdie_for_incompatible_opt4(also, \"-i/--include\",\n> +\t\t\t\t  only, \"-o/--only\",\n> +\t\t\t\t  all, \"-a/--all\",\n> +\t\t\t\t  interactive, \"--interactive/-p/--patch\");\n>   \tif (fixup_message) {\n>   \t\t/*\n>   \t\t * We limit --fixup's suboptions to only alpha characters.\n> diff --git a/builtin/difftool.c b/builtin/difftool.c\n> index c79fbbf67e5..faa3507369a 100644\n> --- a/builtin/difftool.c\n> +++ b/builtin/difftool.c\n> @@ -732,8 +732,9 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n>   \t} else if (dir_diff)\n>   \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--dir-diff\", \"--no-index\");\n>   \n> -\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n> -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n> +\tdie_for_incompatible_opt3(use_gui_tool, \"--gui\",\n> +\t\t\t\t  !!difftool_cmd, \"--tool\",\n> +\t\t\t\t  !!extcmd, \"--extcmd\");\n>   \n>   \tif (use_gui_tool)\n>   \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\n> diff --git a/builtin/grep.c b/builtin/grep.c\n> index 9e34a820ad4..88144f06300 100644\n> --- a/builtin/grep.c\n> +++ b/builtin/grep.c\n> @@ -1167,11 +1167,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n>   \tif (!show_in_pager && !opt.status_only)\n>   \t\tsetup_pager();\n>   \n> -\tif (!use_index && (untracked || cached))\n> -\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n> -\n> -\tif (untracked && cached)\n> -\t\tdie(_(\"--untracked cannot be used with --cached\"));\n> +\tdie_for_incompatible_opt3(!use_index, \"--no-index\",\n> +\t\t\t\t  untracked, \"--untracked\",\n> +\t\t\t\t  cached, \"--cached\");\n>   \n>   \tif (!use_index || untracked) {\n>   \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 4b493408cc5..970aa3483c4 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -1978,8 +1978,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>   \tif (rev.show_notes)\n>   \t\tload_display_notes(&rev.notes_opt);\n>   \n> -\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n> -\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n> +\tdie_for_incompatible_opt3(use_stdout, \"--stdout\",\n> +\t\t\t\t  rev.diffopt.close_file, \"--output\",\n> +\t\t\t\t  !!output_directory, \"--output-directory\");\n>   \n>   \tif (use_stdout) {\n>   \t\tsetup_pager();\n> diff --git a/builtin/merge-base.c b/builtin/merge-base.c\n> index 6719ac198dc..26b84980dbd 100644\n> --- a/builtin/merge-base.c\n> +++ b/builtin/merge-base.c\n> @@ -159,12 +159,14 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)\n>   \t\tif (argc < 2)\n>   \t\t\tusage_with_options(merge_base_usage, options);\n>   \t\tif (show_all)\n> -\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n> +\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n> +\t\t\t    \"--is-ancestor\", \"--all\");\n>   \t\treturn handle_is_ancestor(argc, argv);\n>   \t}\n>   \n>   \tif (cmdmode == 'r' && show_all)\n> -\t\tdie(\"--independent cannot be used with --all\");\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n> +\t\t    \"--independent\", \"--all\");\n>   \n>   \tif (cmdmode == 'o')\n>   \t\treturn handle_octopus(argc, argv, show_all);\n> diff --git a/parse-options.c b/parse-options.c\n> index a8283037be9..276e3911a74 100644\n> --- a/parse-options.c\n> +++ b/parse-options.c\n> @@ -1079,3 +1079,37 @@ void NORETURN usage_msg_opt(const char *msg,\n>   \tdie_message(\"%s\\n\", msg); /* The extra \\n is intentional */\n>   \tusage_with_options(usagestr, options);\n>   }\n> +\n> +void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n> +\t\t\t       int opt2, const char *opt2_name,\n> +\t\t\t       int opt3, const char *opt3_name,\n> +\t\t\t       int opt4, const char *opt4_name)\n> +{\n> +\tint count = 0;\n> +\tconst char *options[4];\n> +\n> +\tif (opt1)\n> +\t\toptions[count++] = opt1_name;\n> +\tif (opt2)\n> +\t\toptions[count++] = opt2_name;\n> +\tif (opt3)\n> +\t\toptions[count++] = opt3_name;\n> +\tif (opt4)\n> +\t\toptions[count++] = opt4_name;\n> +\tswitch (count) {\n> +\tcase 4:\n> +\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"),\n> +\t\t    opt1_name, opt2_name, opt3_name, opt4_name);\n> +\t\tbreak;\n> +\tcase 3:\n> +\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"),\n> +\t\t    options[0], options[1], options[2]);\n> +\t\tbreak;\n> +\tcase 2:\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n> +\t\t    options[0], options[1]);\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tbreak;\n> +\t}\n> +}\n> diff --git a/parse-options.h b/parse-options.h\n> index e22846d3b7b..68f7ae9c31a 100644\n> --- a/parse-options.h\n> +++ b/parse-options.h\n> @@ -225,6 +225,22 @@ NORETURN void usage_msg_opt(const char *msg,\n>   \t\t\t    const char * const *usagestr,\n>   \t\t\t    const struct option *options);\n>   \n> +void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n> +\t\t\t       int opt2, const char *opt2_name,\n> +\t\t\t       int opt3, const char *opt3_name,\n> +\t\t\t       int opt4, const char *opt4_name);\n> +\n> +\n> +inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,\n> +\t\t\t\t      int opt2, const char *opt2_name,\n> +\t\t\t\t      int opt3, const char *opt3_name)\n> +{\n> +\tdie_for_incompatible_opt4(opt1, opt1_name,\n> +\t\t\t\t  opt2, opt2_name,\n> +\t\t\t\t  opt3, opt3_name,\n> +\t\t\t\t  0, \"\");\n> +}\n> +\n>   /*\n>    * Use these assertions for callbacks that expect to be called with NONEG and\n>    * NOARG respectively, and do not otherwise handle the \"unset\" and \"arg\"\n> @@ -339,4 +355,5 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);\n>   #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n>   #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n>   \n> +\n>   #endif\n> diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\n> index 91964653a0b..5fcaa0b4f2a 100755\n> --- a/t/t7500-commit-template-squash-signoff.sh\n> +++ b/t/t7500-commit-template-squash-signoff.sh\n> @@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '\n>   '\n>   \n>   test_expect_success '--fixup=reword: -F give error message' '\n> -\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n> +\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used together\" >expect &&\n>   \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n>   \ttest_cmp expect actual\n>   '\n\n"},{"id":"447326","messageId":"3a12de6d-48f6-b1c1-4fca-655c0db648c7@gmail.com","threadId":"57287","inReplyTo":"b8e80e178eeb2fccd9e10d783c7cf4b5f4cc80e4.1643580113.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 4/4] i18n: transfer variables into placeholders in command synopsis","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-01-31T11:00:09Z","receivedAt":"2022-01-31T11:03:46Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 30/01/2022 22:01, Jean-Noël Avila via GitGitGadget wrote:\n> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n> \n> This applies the style guide for documentation.\n\nI don't really understand the title of this patch. Should 'transfer' be \n'transform'? All the changes look fine but seem to be two sorts of \nchanges mixed together\n  - adding '<>' around arguments where they are missing\n  - converting plurals followed by '...' into singulars followed by '...'\n\nBest Wishes\n\nPhillip\n\n> Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>\n> ---\n>   builtin/fast-export.c | 2 +-\n>   builtin/reflog.c      | 4 ++--\n>   builtin/rev-list.c    | 2 +-\n>   3 files changed, 4 insertions(+), 4 deletions(-)\n> \n> diff --git a/builtin/fast-export.c b/builtin/fast-export.c\n> index 9f1c730e587..510139e9b54 100644\n> --- a/builtin/fast-export.c\n> +++ b/builtin/fast-export.c\n> @@ -26,7 +26,7 @@\n>   #include \"commit-slab.h\"\n>   \n>   static const char *fast_export_usage[] = {\n> -\tN_(\"git fast-export [rev-list-opts]\"),\n> +\tN_(\"git fast-export [<rev-list-opts>]\"),\n>   \tNULL\n>   };\n>   \n> diff --git a/builtin/reflog.c b/builtin/reflog.c\n> index ee5ee8d8cf6..343a10d371b 100644\n> --- a/builtin/reflog.c\n> +++ b/builtin/reflog.c\n> @@ -17,10 +17,10 @@ static const char reflog_expire_usage[] =\n>   N_(\"git reflog expire [--expire=<time>] \"\n>      \"[--expire-unreachable=<time>] \"\n>      \"[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] \"\n> -   \"[--verbose] [--all] <refs>...\");\n> +   \"[--verbose] [--all] <ref>...\");\n>   static const char reflog_delete_usage[] =\n>   N_(\"git reflog delete [--rewrite] [--updateref] \"\n> -   \"[--dry-run | -n] [--verbose] <refs>...\");\n> +   \"[--dry-run | -n] [--verbose] <ref>...\");\n>   static const char reflog_exists_usage[] =\n>   N_(\"git reflog exists <ref>\");\n>   \n> diff --git a/builtin/rev-list.c b/builtin/rev-list.c\n> index 777558e9b06..38528c7f156 100644\n> --- a/builtin/rev-list.c\n> +++ b/builtin/rev-list.c\n> @@ -20,7 +20,7 @@\n>   #include \"packfile.h\"\n>   \n>   static const char rev_list_usage[] =\n> -\"git rev-list [OPTION] <commit-id>... [ -- paths... ]\\n\"\n> +\"git rev-list [<options>] <commit-id>... [-- <path>...]\\n\"\n>   \"  limiting output:\\n\"\n>   \"    --max-count=<n>\\n\"\n>   \"    --max-age=<epoch>\\n\"\n\n"},{"id":"447331","messageId":"b8ff5544-6565-79f7-ee1e-0b53fefef94a@free.fr","threadId":"57287","inReplyTo":"3a12de6d-48f6-b1c1-4fca-655c0db648c7@gmail.com","subject":"Re: [PATCH v3 4/4] i18n: transfer variables into placeholders in command synopsis","fromName":"Jean-Noël Avila","fromEmail":"jn.avila@free.fr","sentAt":"2022-01-31T13:36:07Z","receivedAt":"2022-01-31T13:36:16Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"Le 31/01/2022 à 12:00, Phillip Wood a écrit :\n> On 30/01/2022 22:01, Jean-Noël Avila via GitGitGadget wrote:\n>> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n>>\n>> This applies the style guide for documentation.\n> \n> I don't really understand the title of this patch. Should 'transfer' be \n> 'transform'? All the changes look fine but seem to be two sorts of \n> changes mixed together\n>   - adding '<>' around arguments where they are missing\n>   - converting plurals followed by '...' into singulars followed by '...'\n> \n> Best Wishes\n> \n> Phillip\n> \n\nTBH, this patch is a remaining of some changes performed while making \nthe preceding one. They are the places that were false positive from the \ngrep.\n\nFair enough, the changes are a little broader than advertised.\n\nAs for the plural forms transformed into singular forms, the coding \nguidelines is clear about repeated parameters of the same type, (e.g. \n<file>...), but some subsets of parameters are still preserved with \nplural, such as <options>, in which case they are not \"atomic\" (they may \nprovide several cells in argv). This is not formalized atm.\n\nMy heuristic is that when the subset is not an array of a given type, \nbut may be a collection of different types, we keep the plural. Maybe \nusing another syntax for these \"sub-expressions\", along with a link to \nwhere to find them would help.\n\n\nJN\n"},{"id":"447370","messageId":"pull.1123.v4.git.1643666870.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v3.git.1643580113.gitgitgadget@gmail.com","subject":"[PATCH v4 0/4] Factorize i18n","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T22:07:45Z","receivedAt":"2022-01-31T22:07:55Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"This is a small series of i18n factorizations. Again the idea is to:\n\n * reduce the number of strings to translate\n * make life easier for translators by removing constant parts of the\n   strings to translate\n * uniformize the UI by using a type of message for the a given type of\n   condition.\n\nChanges since V1:\n\n * factorize checks of more than 2 incompatible options into dedicated\n   functions which list all the incompatible options present on the command\n   line\n\nChanges since V2:\n\n * switch to tab size 8\n * rename die_if* to die_for*\n * make 3 arg check a call to 4 arg check\n * switch another part to die_for_incompatible_opt4\n\nChanges since V3:\n\n * remove spurious blank line\n * reword last commit to better reflect the changes\n\nJean-Noël Avila (4):\n  i18n: factorize more 'incompatible options' messages\n  i18n: factorize \"invalid value\" messages\n  i18n: remove from i18n strings that do not hold translatable parts\n  i18n: fix some misformated placeholders in command synopsis\n\n archive.c                                 |  2 +-\n builtin/am.c                              |  8 ++++--\n builtin/bisect--helper.c                  |  6 ++--\n builtin/blame.c                           |  7 +++--\n builtin/commit.c                          | 35 ++++++++++-------------\n builtin/count-objects.c                   |  2 +-\n builtin/difftool.c                        |  5 ++--\n builtin/fast-export.c                     |  2 +-\n builtin/fetch.c                           |  4 +--\n builtin/grep.c                            |  8 ++----\n builtin/hash-object.c                     |  2 +-\n builtin/help.c                            |  4 +--\n builtin/log.c                             |  5 ++--\n builtin/merge-base.c                      |  6 ++--\n builtin/mktag.c                           |  2 +-\n builtin/mktree.c                          |  2 +-\n builtin/notes.c                           |  6 ++--\n builtin/pack-objects.c                    |  2 +-\n builtin/prune-packed.c                    |  2 +-\n builtin/pull.c                            |  6 ++--\n builtin/push.c                            |  2 +-\n builtin/rebase.c                          |  2 +-\n builtin/reflog.c                          |  6 ++--\n builtin/remote.c                          |  2 +-\n builtin/replace.c                         |  2 +-\n builtin/rev-list.c                        |  2 +-\n builtin/send-pack.c                       |  2 +-\n builtin/sparse-checkout.c                 |  8 +++---\n builtin/stripspace.c                      |  4 +--\n builtin/submodule--helper.c               |  2 +-\n builtin/update-server-info.c              |  2 +-\n diff-merges.c                             |  2 +-\n gpg-interface.c                           |  6 ++--\n ls-refs.c                                 |  3 +-\n parallel-checkout.c                       |  4 +--\n parse-options.c                           | 34 ++++++++++++++++++++++\n parse-options.h                           | 16 +++++++++++\n sequencer.c                               |  2 +-\n setup.c                                   |  3 +-\n submodule-config.c                        |  2 +-\n t/t4150-am.sh                             |  2 +-\n t/t7500-commit-template-squash-signoff.sh |  2 +-\n 42 files changed, 139 insertions(+), 87 deletions(-)\n\n\nbase-commit: 5d01301f2b865aa8dba1654d3f447ce9d21db0b5\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-1123%2Fjnavila%2Ffactorize_i18n-v4\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1123/jnavila/factorize_i18n-v4\nPull-Request: https://github.com/gitgitgadget/git/pull/1123\n\nRange-diff vs v3:\n\n 1:  aac3f9821c7 ! 1:  2eac2ef502b i18n: factorize more 'incompatible options' messages\n     @@ parse-options.h: NORETURN void usage_msg_opt(const char *msg,\n       /*\n        * Use these assertions for callbacks that expect to be called with NONEG and\n        * NOARG respectively, and do not otherwise handle the \"unset\" and \"arg\"\n     -@@ parse-options.h: int parse_opt_tracking_mode(const struct option *, const char *, int);\n     - #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n     - #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n     - \n     -+\n     - #endif\n      \n       ## t/t7500-commit-template-squash-signoff.sh ##\n      @@ t/t7500-commit-template-squash-signoff.sh: test_expect_success '--fixup=reword: give error with pathsec' '\n 2:  078336ae418 = 2:  2d359118d8f i18n: factorize \"invalid value\" messages\n 3:  d5ddd903df7 = 3:  cdaa576aac1 i18n: remove from i18n strings that do not hold translatable parts\n 4:  b8e80e178ee ! 4:  f37b03128b2 i18n: transfer variables into placeholders in command synopsis\n     @@ Metadata\n      Author: Jean-Noël Avila <jn.avila@free.fr>\n      \n       ## Commit message ##\n     -    i18n: transfer variables into placeholders in command synopsis\n     +    i18n: fix some misformated placeholders in command synopsis\n     +\n     +     * add '<>' around arguments where missing\n     +     * convert plurals into '...' forms\n      \n          This applies the style guide for documentation.\n      \n          Signed-off-by: Jean-Noël Avila <jn.avila@free.fr>\n     +    Reviewed-by: Phillip Wood <phillip.wood123@gmail.com>\n      \n       ## builtin/fast-export.c ##\n      @@\n\n-- \ngitgitgadget\n"},{"id":"447371","messageId":"2eac2ef502b86d0c15513c8d0e69928ce2140b1f.1643666870.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v4.git.1643666870.gitgitgadget@gmail.com","subject":"[PATCH v4 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T22:07:46Z","receivedAt":"2022-01-31T22:07:57Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nFind more incompatible options to factorize.\n\nWhen more than two options are mutually exclusive, print the ones\nwhich are actually on the command line.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/commit.c                          | 35 ++++++++++-------------\n builtin/difftool.c                        |  5 ++--\n builtin/grep.c                            |  8 ++----\n builtin/log.c                             |  5 ++--\n builtin/merge-base.c                      |  6 ++--\n parse-options.c                           | 34 ++++++++++++++++++++++\n parse-options.h                           | 16 +++++++++++\n t/t7500-commit-template-squash-signoff.sh |  2 +-\n 8 files changed, 79 insertions(+), 32 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex b9ed0374e30..33ca9e99c80 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1242,8 +1242,6 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\t\t\t      struct commit *current_head,\n \t\t\t\t      struct wt_status *s)\n {\n-\tint f = 0;\n-\n \targc = parse_options(argc, argv, prefix, options, usage, 0);\n \tfinalize_deferred_config(s);\n \n@@ -1251,7 +1249,7 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\tforce_author = find_author_by_nickname(force_author);\n \n \tif (force_author && renew_authorship)\n-\t\tdie(_(\"Using both --reset-author and --author does not make sense\"));\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--reset-author\", \"--author\");\n \n \tif (logfile || have_option_m || use_message)\n \t\tuse_editor = 0;\n@@ -1268,20 +1266,16 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \t\t\tdie(_(\"You are in the middle of a rebase -- cannot amend.\"));\n \t}\n \tif (fixup_message && squash_message)\n-\t\tdie(_(\"Options --squash and --fixup cannot be used together\"));\n-\tif (use_message)\n-\t\tf++;\n-\tif (edit_message)\n-\t\tf++;\n-\tif (fixup_message)\n-\t\tf++;\n-\tif (logfile)\n-\t\tf++;\n-\tif (f > 1)\n-\t\tdie(_(\"Only one of -c/-C/-F/--fixup can be used.\"));\n-\tif (have_option_m && (edit_message || use_message || logfile))\n-\t\tdie((_(\"Option -m cannot be combined with -c/-C/-F.\")));\n-\tif (f || have_option_m)\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--squash\", \"--fixup\");\n+\tdie_for_incompatible_opt4(!!use_message, \"-C\",\n+\t\t\t\t  !!edit_message, \"-c\",\n+\t\t\t\t  !!logfile, \"-F\",\n+\t\t\t\t  !!fixup_message, \"--fixup\");\n+\tdie_for_incompatible_opt4(have_option_m, \"-m\",\n+\t\t\t\t  !!edit_message, \"-c\",\n+\t\t\t\t  !!use_message, \"-C\",\n+\t\t\t\t  !!logfile, \"-F\");\n+\tif (use_message || edit_message || logfile ||fixup_message || have_option_m)\n \t\ttemplate_file = NULL;\n \tif (edit_message)\n \t\tuse_message = edit_message;\n@@ -1306,9 +1300,10 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \tif (patch_interactive)\n \t\tinteractive = 1;\n \n-\tif (also + only + all + interactive > 1)\n-\t\tdie(_(\"Only one of --include/--only/--all/--interactive/--patch can be used.\"));\n-\n+\tdie_for_incompatible_opt4(also, \"-i/--include\",\n+\t\t\t\t  only, \"-o/--only\",\n+\t\t\t\t  all, \"-a/--all\",\n+\t\t\t\t  interactive, \"--interactive/-p/--patch\");\n \tif (fixup_message) {\n \t\t/*\n \t\t * We limit --fixup's suboptions to only alpha characters.\ndiff --git a/builtin/difftool.c b/builtin/difftool.c\nindex c79fbbf67e5..faa3507369a 100644\n--- a/builtin/difftool.c\n+++ b/builtin/difftool.c\n@@ -732,8 +732,9 @@ int cmd_difftool(int argc, const char **argv, const char *prefix)\n \t} else if (dir_diff)\n \t\tdie(_(\"options '%s' and '%s' cannot be used together\"), \"--dir-diff\", \"--no-index\");\n \n-\tif (use_gui_tool + !!difftool_cmd + !!extcmd > 1)\n-\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--gui\", \"--tool\", \"--extcmd\");\n+\tdie_for_incompatible_opt3(use_gui_tool, \"--gui\",\n+\t\t\t\t  !!difftool_cmd, \"--tool\",\n+\t\t\t\t  !!extcmd, \"--extcmd\");\n \n \tif (use_gui_tool)\n \t\tsetenv(\"GIT_MERGETOOL_GUI\", \"true\", 1);\ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 9e34a820ad4..88144f06300 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -1167,11 +1167,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n \tif (!show_in_pager && !opt.status_only)\n \t\tsetup_pager();\n \n-\tif (!use_index && (untracked || cached))\n-\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n-\n-\tif (untracked && cached)\n-\t\tdie(_(\"--untracked cannot be used with --cached\"));\n+\tdie_for_incompatible_opt3(!use_index, \"--no-index\",\n+\t\t\t\t  untracked, \"--untracked\",\n+\t\t\t\t  cached, \"--cached\");\n \n \tif (!use_index || untracked) {\n \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 4b493408cc5..970aa3483c4 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1978,8 +1978,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.show_notes)\n \t\tload_display_notes(&rev.notes_opt);\n \n-\tif (use_stdout + rev.diffopt.close_file + !!output_directory > 1)\n-\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), \"--stdout\", \"--output\", \"--output-directory\");\n+\tdie_for_incompatible_opt3(use_stdout, \"--stdout\",\n+\t\t\t\t  rev.diffopt.close_file, \"--output\",\n+\t\t\t\t  !!output_directory, \"--output-directory\");\n \n \tif (use_stdout) {\n \t\tsetup_pager();\ndiff --git a/builtin/merge-base.c b/builtin/merge-base.c\nindex 6719ac198dc..26b84980dbd 100644\n--- a/builtin/merge-base.c\n+++ b/builtin/merge-base.c\n@@ -159,12 +159,14 @@ int cmd_merge_base(int argc, const char **argv, const char *prefix)\n \t\tif (argc < 2)\n \t\t\tusage_with_options(merge_base_usage, options);\n \t\tif (show_all)\n-\t\t\tdie(\"--is-ancestor cannot be used with --all\");\n+\t\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n+\t\t\t    \"--is-ancestor\", \"--all\");\n \t\treturn handle_is_ancestor(argc, argv);\n \t}\n \n \tif (cmdmode == 'r' && show_all)\n-\t\tdie(\"--independent cannot be used with --all\");\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n+\t\t    \"--independent\", \"--all\");\n \n \tif (cmdmode == 'o')\n \t\treturn handle_octopus(argc, argv, show_all);\ndiff --git a/parse-options.c b/parse-options.c\nindex a8283037be9..276e3911a74 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -1079,3 +1079,37 @@ void NORETURN usage_msg_opt(const char *msg,\n \tdie_message(\"%s\\n\", msg); /* The extra \\n is intentional */\n \tusage_with_options(usagestr, options);\n }\n+\n+void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n+\t\t\t       int opt2, const char *opt2_name,\n+\t\t\t       int opt3, const char *opt3_name,\n+\t\t\t       int opt4, const char *opt4_name)\n+{\n+\tint count = 0;\n+\tconst char *options[4];\n+\n+\tif (opt1)\n+\t\toptions[count++] = opt1_name;\n+\tif (opt2)\n+\t\toptions[count++] = opt2_name;\n+\tif (opt3)\n+\t\toptions[count++] = opt3_name;\n+\tif (opt4)\n+\t\toptions[count++] = opt4_name;\n+\tswitch (count) {\n+\tcase 4:\n+\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"),\n+\t\t    opt1_name, opt2_name, opt3_name, opt4_name);\n+\t\tbreak;\n+\tcase 3:\n+\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"),\n+\t\t    options[0], options[1], options[2]);\n+\t\tbreak;\n+\tcase 2:\n+\t\tdie(_(\"options '%s' and '%s' cannot be used together\"),\n+\t\t    options[0], options[1]);\n+\t\tbreak;\n+\tdefault:\n+\t\tbreak;\n+\t}\n+}\ndiff --git a/parse-options.h b/parse-options.h\nindex e22846d3b7b..eb86c505470 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -225,6 +225,22 @@ NORETURN void usage_msg_opt(const char *msg,\n \t\t\t    const char * const *usagestr,\n \t\t\t    const struct option *options);\n \n+void die_for_incompatible_opt4(int opt1, const char *opt1_name,\n+\t\t\t       int opt2, const char *opt2_name,\n+\t\t\t       int opt3, const char *opt3_name,\n+\t\t\t       int opt4, const char *opt4_name);\n+\n+\n+inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,\n+\t\t\t\t      int opt2, const char *opt2_name,\n+\t\t\t\t      int opt3, const char *opt3_name)\n+{\n+\tdie_for_incompatible_opt4(opt1, opt1_name,\n+\t\t\t\t  opt2, opt2_name,\n+\t\t\t\t  opt3, opt3_name,\n+\t\t\t\t  0, \"\");\n+}\n+\n /*\n  * Use these assertions for callbacks that expect to be called with NONEG and\n  * NOARG respectively, and do not otherwise handle the \"unset\" and \"arg\"\ndiff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\nindex 91964653a0b..5fcaa0b4f2a 100755\n--- a/t/t7500-commit-template-squash-signoff.sh\n+++ b/t/t7500-commit-template-squash-signoff.sh\n@@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '\n '\n \n test_expect_success '--fixup=reword: -F give error message' '\n-\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n+\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used together\" >expect &&\n \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n \ttest_cmp expect actual\n '\n-- \ngitgitgadget\n\n"},{"id":"447372","messageId":"2d359118d8fd01355269c0484d4438764fb59d35.1643666870.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v4.git.1643666870.gitgitgadget@gmail.com","subject":"[PATCH v4 2/4] i18n: factorize \"invalid value\" messages","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T22:07:47Z","receivedAt":"2022-01-31T22:07:59Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nUse the same message when an invalid value is passed to a command line\noption or a configuration variable.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n builtin/am.c           | 8 +++++---\n builtin/blame.c        | 7 ++++---\n builtin/fetch.c        | 4 ++--\n builtin/pack-objects.c | 2 +-\n builtin/pull.c         | 6 +++---\n builtin/push.c         | 2 +-\n builtin/send-pack.c    | 2 +-\n diff-merges.c          | 2 +-\n gpg-interface.c        | 6 +++---\n ls-refs.c              | 3 ++-\n parallel-checkout.c    | 4 ++--\n sequencer.c            | 2 +-\n setup.c                | 3 ++-\n submodule-config.c     | 2 +-\n t/t4150-am.sh          | 2 +-\n 15 files changed, 30 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/am.c b/builtin/am.c\nindex b6be1f1cb11..97dbeb8e49e 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -199,7 +199,7 @@ static int am_option_parse_empty(const struct option *opt,\n \telse if (!strcmp(arg, \"keep\"))\n \t\t*opt_value = KEEP_EMPTY_COMMIT;\n \telse\n-\t\treturn error(_(\"Invalid value for --empty: %s\"), arg);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), \"--empty\", arg);\n \n \treturn 0;\n }\n@@ -2239,7 +2239,8 @@ static int parse_opt_patchformat(const struct option *opt, const char *arg, int\n \t * when you add new options\n \t */\n \telse\n-\t\treturn error(_(\"Invalid value for --patch-format: %s\"), arg);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t     \"--patch-format\", arg);\n \treturn 0;\n }\n \n@@ -2282,7 +2283,8 @@ static int parse_opt_show_current_patch(const struct option *opt, const char *ar\n \t\t\t\tbreak;\n \t\t}\n \t\tif (new_value >= ARRAY_SIZE(valid_modes))\n-\t\t\treturn error(_(\"Invalid value for --show-current-patch: %s\"), arg);\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t     \"--show-current-patch\", arg);\n \t}\n \n \tif (resume->mode == RESUME_SHOW_PATCH && new_value != resume->sub_mode)\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 7fafeac4081..9c142f17a2e 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -721,8 +721,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t}\n \tif (!strcmp(var, \"color.blame.repeatedlines\")) {\n \t\tif (color_parse_mem(value, strlen(value), repeated_meta_color))\n-\t\t\twarning(_(\"invalid color '%s' in color.blame.repeatedLines\"),\n-\t\t\t\tvalue);\n+\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\"color.blame.repeatedLines\", value);\n \t\treturn 0;\n \t}\n \tif (!strcmp(var, \"color.blame.highlightrecent\")) {\n@@ -739,7 +739,8 @@ static int git_blame_config(const char *var, const char *value, void *cb)\n \t\t\tcoloring_mode &= ~(OUTPUT_COLOR_LINE |\n \t\t\t\t\t    OUTPUT_SHOW_AGE_WITH_COLOR);\n \t\t} else {\n-\t\t\twarning(_(\"invalid value for blame.coloring\"));\n+\t\t\twarning(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t\"blame.coloring\", value);\n \t\t\treturn 0;\n \t\t}\n \t}\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 5f06b21f8e9..8be19bb8799 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -763,8 +763,8 @@ static void prepare_format_display(struct ref *ref_map)\n \telse if (!strcasecmp(format, \"compact\"))\n \t\tcompact_format = 1;\n \telse\n-\t\tdie(_(\"configuration fetch.output contains invalid value %s\"),\n-\t\t    format);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t    \"fetch.output\", format);\n \n \tfor (rm = ref_map; rm; rm = rm->next) {\n \t\tif (rm->status == REF_STATUS_REJECT_SHALLOW ||\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex ba2006f2212..192c3ca305e 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3504,7 +3504,7 @@ static int option_parse_missing_action(const struct option *opt,\n \t\treturn 0;\n \t}\n \n-\tdie(_(\"invalid value for --missing\"));\n+\tdie(_(\"invalid value for '%s': '%s'\"), \"--missing\", arg);\n \treturn 0;\n }\n \ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 100cbf9fb85..e54a0ccadc6 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -42,9 +42,9 @@ static enum rebase_type parse_config_rebase(const char *key, const char *value,\n \t\treturn v;\n \n \tif (fatal)\n-\t\tdie(_(\"Invalid value for %s: %s\"), key, value);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"), key, value);\n \telse\n-\t\terror(_(\"Invalid value for %s: %s\"), key, value);\n+\t\terror(_(\"invalid value for '%s': '%s'\"), key, value);\n \n \treturn REBASE_INVALID;\n }\n@@ -318,7 +318,7 @@ static const char *config_get_ff(void)\n \tif (!strcmp(value, \"only\"))\n \t\treturn \"--ff-only\";\n \n-\tdie(_(\"Invalid value for pull.ff: %s\"), value);\n+\tdie(_(\"invalid value for '%s': '%s'\"), \"pull.ff\", value);\n }\n \n /**\ndiff --git a/builtin/push.c b/builtin/push.c\nindex 359db90321c..cad997965a7 100644\n--- a/builtin/push.c\n+++ b/builtin/push.c\n@@ -486,7 +486,7 @@ static int git_push_config(const char *k, const char *v, void *cb)\n \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n \t\t\t\t\tset_push_cert_flags(flags, SEND_PACK_PUSH_CERT_IF_ASKED);\n \t\t\t\telse\n-\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n+\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n \t\t\t}\n \t\t}\n \t} else if (!strcmp(k, \"push.recursesubmodules\")) {\ndiff --git a/builtin/send-pack.c b/builtin/send-pack.c\nindex 69c432ef1a6..64962be0168 100644\n--- a/builtin/send-pack.c\n+++ b/builtin/send-pack.c\n@@ -145,7 +145,7 @@ static int send_pack_config(const char *k, const char *v, void *cb)\n \t\t\t\tif (value && !strcasecmp(value, \"if-asked\"))\n \t\t\t\t\targs.push_cert = SEND_PACK_PUSH_CERT_IF_ASKED;\n \t\t\t\telse\n-\t\t\t\t\treturn error(\"Invalid value for '%s'\", k);\n+\t\t\t\t\treturn error(_(\"invalid value for '%s'\"), k);\n \t\t\t}\n \t\t}\n \t}\ndiff --git a/diff-merges.c b/diff-merges.c\nindex 5060ccd890b..cd6c102a0d5 100644\n--- a/diff-merges.c\n+++ b/diff-merges.c\n@@ -67,7 +67,7 @@ static void set_diff_merges(struct rev_info *revs, const char *optarg)\n \tdiff_merges_setup_func_t func = func_by_opt(optarg);\n \n \tif (!func)\n-\t\tdie(_(\"unknown value for --diff-merges: %s\"), optarg);\n+\t\tdie(_(\"invalid value for '%s': '%s'\"), \"--diff-merges\", optarg);\n \n \tfunc(revs);\n \ndiff --git a/gpg-interface.c b/gpg-interface.c\nindex b52eb0e2e04..4e084b08f67 100644\n--- a/gpg-interface.c\n+++ b/gpg-interface.c\n@@ -702,7 +702,7 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n \t\t\treturn config_error_nonbool(var);\n \t\tfmt = get_format_by_name(value);\n \t\tif (!fmt)\n-\t\t\treturn error(\"unsupported value for %s: %s\",\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n \t\t\t\t     var, value);\n \t\tuse_format = fmt;\n \t\treturn 0;\n@@ -717,8 +717,8 @@ int git_gpg_config(const char *var, const char *value, void *cb)\n \t\tfree(trust);\n \n \t\tif (ret)\n-\t\t\treturn error(\"unsupported value for %s: %s\", var,\n-\t\t\t\t     value);\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t     var, value);\n \t\treturn 0;\n \t}\n \ndiff --git a/ls-refs.c b/ls-refs.c\nindex 54078323dcb..98e69373c84 100644\n--- a/ls-refs.c\n+++ b/ls-refs.c\n@@ -34,7 +34,8 @@ static void ensure_config_read(void)\n \t\t} else if (!strcmp(str, \"ignore\")) {\n \t\t\t/* do nothing */\n \t\t} else {\n-\t\t\tdie(_(\"invalid value '%s' for lsrefs.unborn\"), str);\n+\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t    \"lsrefs.unborn\", str);\n \t\t}\n \t}\n \tconfig_read = 1;\ndiff --git a/parallel-checkout.c b/parallel-checkout.c\nindex 8dd7e7bad40..31a3d0ee1bf 100644\n--- a/parallel-checkout.c\n+++ b/parallel-checkout.c\n@@ -39,8 +39,8 @@ void get_parallel_checkout_configs(int *num_workers, int *threshold)\n \n \tif (env_workers && *env_workers) {\n \t\tif (strtol_i(env_workers, 10, num_workers)) {\n-\t\t\tdie(\"invalid value for GIT_TEST_CHECKOUT_WORKERS: '%s'\",\n-\t\t\t    env_workers);\n+\t\t\tdie(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t    \"GIT_TEST_CHECKOUT_WORKERS\", env_workers);\n \t\t}\n \t\tif (*num_workers < 1)\n \t\t\t*num_workers = online_cpus();\ndiff --git a/sequencer.c b/sequencer.c\nindex 5213d16e971..683f5392eba 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2806,7 +2806,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \t\treturn error(_(\"invalid key: %s\"), key);\n \n \tif (!error_flag)\n-\t\treturn error(_(\"invalid value for %s: %s\"), key, value);\n+\t\treturn error(_(\"invalid value for '%s': '%s'\"), key, value);\n \n \treturn 0;\n }\ndiff --git a/setup.c b/setup.c\nindex af3b8c09abe..04ce33cdcd4 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -559,7 +559,8 @@ static enum extension_result handle_extension(const char *var,\n \t\t\treturn config_error_nonbool(var);\n \t\tformat = hash_algo_by_name(value);\n \t\tif (format == GIT_HASH_UNKNOWN)\n-\t\t\treturn error(\"invalid value for 'extensions.objectformat'\");\n+\t\t\treturn error(_(\"invalid value for '%s': '%s'\"),\n+\t\t\t\t     \"extensions.objectformat\", value);\n \t\tdata->hash_algo = format;\n \t\treturn EXTENSION_OK;\n \t}\ndiff --git a/submodule-config.c b/submodule-config.c\nindex f95344028b5..fb95a026f48 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -496,7 +496,7 @@ static int parse_config(const char *var, const char *value, void *data)\n \t\telse if (parse_submodule_update_strategy(value,\n \t\t\t &submodule->update_strategy) < 0 ||\n \t\t\t submodule->update_strategy.type == SM_UPDATE_COMMAND)\n-\t\t\tdie(_(\"invalid value for %s\"), var);\n+\t\t\tdie(_(\"invalid value for '%s'\"), var);\n \t} else if (!strcmp(item.buf, \"shallow\")) {\n \t\tif (!me->overwrite && submodule->recommend_shallow != -1)\n \t\t\twarn_multiple_config(me->treeish_name, submodule->name,\ndiff --git a/t/t4150-am.sh b/t/t4150-am.sh\nindex 6caff0ca397..159fae8d016 100755\n--- a/t/t4150-am.sh\n+++ b/t/t4150-am.sh\n@@ -1169,7 +1169,7 @@ test_expect_success 'invalid when passing the --empty option alone' '\n \ttest_when_finished \"git am --abort || :\" &&\n \tgit checkout empty-commit^ &&\n \ttest_must_fail git am --empty empty-commit.patch 2>err &&\n-\techo \"error: Invalid value for --empty: empty-commit.patch\" >expected &&\n+\techo \"error: invalid value for '\\''--empty'\\'': '\\''empty-commit.patch'\\''\" >expected &&\n \ttest_cmp expected err\n '\n \n-- \ngitgitgadget\n\n"},{"id":"447373","messageId":"cdaa576aac178f83e73b16e4390c196b26b49d1d.1643666870.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v4.git.1643666870.gitgitgadget@gmail.com","subject":"[PATCH v4 3/4] i18n: remove from i18n strings that do not hold translatable parts","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T22:07:48Z","receivedAt":"2022-01-31T22:08:00Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\n---\n archive.c                    | 2 +-\n builtin/bisect--helper.c     | 6 +++---\n builtin/count-objects.c      | 2 +-\n builtin/hash-object.c        | 2 +-\n builtin/help.c               | 4 ++--\n builtin/mktag.c              | 2 +-\n builtin/mktree.c             | 2 +-\n builtin/notes.c              | 6 +++---\n builtin/prune-packed.c       | 2 +-\n builtin/rebase.c             | 2 +-\n builtin/reflog.c             | 2 +-\n builtin/remote.c             | 2 +-\n builtin/replace.c            | 2 +-\n builtin/sparse-checkout.c    | 8 ++++----\n builtin/stripspace.c         | 4 ++--\n builtin/submodule--helper.c  | 2 +-\n builtin/update-server-info.c | 2 +-\n 17 files changed, 26 insertions(+), 26 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex d571249cf39..e29d0e00f6c 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -12,7 +12,7 @@\n \n static char const * const archive_usage[] = {\n \tN_(\"git archive [<options>] <tree-ish> [<path>...]\"),\n-\tN_(\"git archive --list\"),\n+\t\"git archive --list\",\n \tN_(\"git archive --remote <repo> [--exec <cmd>] [<options>] <tree-ish> [<path>...]\"),\n \tN_(\"git archive --remote <repo> [--exec <cmd>] --list\"),\n \tNULL\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 28a2e6a5750..f962dbd4303 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -22,15 +22,15 @@ static GIT_PATH_FUNC(git_path_bisect_run, \"BISECT_RUN\")\n \n static const char * const git_bisect_helper_usage[] = {\n \tN_(\"git bisect--helper --bisect-reset [<commit>]\"),\n-\tN_(\"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]\"),\n+\t\"git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]\",\n \tN_(\"git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]\"\n \t\t\t\t\t    \" [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]\"),\n-\tN_(\"git bisect--helper --bisect-next\"),\n+\t\"git bisect--helper --bisect-next\",\n \tN_(\"git bisect--helper --bisect-state (bad|new) [<rev>]\"),\n \tN_(\"git bisect--helper --bisect-state (good|old) [<rev>...]\"),\n \tN_(\"git bisect--helper --bisect-replay <filename>\"),\n \tN_(\"git bisect--helper --bisect-skip [(<rev>|<range>)...]\"),\n-\tN_(\"git bisect--helper --bisect-visualize\"),\n+\t\"git bisect--helper --bisect-visualize\",\n \tN_(\"git bisect--helper --bisect-run <cmd>...\"),\n \tNULL\n };\ndiff --git a/builtin/count-objects.c b/builtin/count-objects.c\nindex 3fae474f6f1..07b94195962 100644\n--- a/builtin/count-objects.c\n+++ b/builtin/count-objects.c\n@@ -87,7 +87,7 @@ static int print_alternate(struct object_directory *odb, void *data)\n }\n \n static char const * const count_objects_usage[] = {\n-\tN_(\"git count-objects [-v] [-H | --human-readable]\"),\n+\t\"git count-objects [-v] [-H | --human-readable]\",\n \tNULL\n };\n \ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex c7b3ad74c60..04e2442ec7c 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -81,7 +81,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n {\n \tstatic const char * const hash_object_usage[] = {\n \t\tN_(\"git hash-object [-t <type>] [-w] [--path=<file> | --no-filters] [--stdin] [--] <file>...\"),\n-\t\tN_(\"git hash-object  --stdin-paths\"),\n+\t\t\"git hash-object  --stdin-paths\",\n \t\tNULL\n \t};\n \tconst char *type = blob_type;\ndiff --git a/builtin/help.c b/builtin/help.c\nindex d387131dd83..b4f2ad3f94e 100644\n--- a/builtin/help.c\n+++ b/builtin/help.c\n@@ -77,8 +77,8 @@ static struct option builtin_help_options[] = {\n static const char * const builtin_help_usage[] = {\n \tN_(\"git help [-a|--all] [--[no-]verbose]]\\n\"\n \t   \"         [[-i|--info] [-m|--man] [-w|--web]] [<command>]\"),\n-\tN_(\"git help [-g|--guides]\"),\n-\tN_(\"git help [-c|--config]\"),\n+\t\"git help [-g|--guides]\",\n+\t\"git help [-c|--config]\",\n \tNULL\n };\n \ndiff --git a/builtin/mktag.c b/builtin/mktag.c\nindex 3b2dbbb37e6..c7b905c614b 100644\n--- a/builtin/mktag.c\n+++ b/builtin/mktag.c\n@@ -7,7 +7,7 @@\n #include \"config.h\"\n \n static char const * const builtin_mktag_usage[] = {\n-\tN_(\"git mktag\"),\n+\t\"git mktag\",\n \tNULL\n };\n static int option_strict = 1;\ndiff --git a/builtin/mktree.c b/builtin/mktree.c\nindex ae78ca1c029..8bdaada922a 100644\n--- a/builtin/mktree.c\n+++ b/builtin/mktree.c\n@@ -63,7 +63,7 @@ static void write_tree(struct object_id *oid)\n }\n \n static const char *mktree_usage[] = {\n-\tN_(\"git mktree [-z] [--missing] [--batch]\"),\n+\t\"git mktree [-z] [--missing] [--batch]\",\n \tNULL\n };\n \ndiff --git a/builtin/notes.c b/builtin/notes.c\nindex 05d60483e82..f99593a1853 100644\n--- a/builtin/notes.c\n+++ b/builtin/notes.c\n@@ -32,8 +32,8 @@ static const char * const git_notes_usage[] = {\n \tN_(\"git notes [--ref <notes-ref>] edit [--allow-empty] [<object>]\"),\n \tN_(\"git notes [--ref <notes-ref>] show [<object>]\"),\n \tN_(\"git notes [--ref <notes-ref>] merge [-v | -q] [-s <strategy>] <notes-ref>\"),\n-\tN_(\"git notes merge --commit [-v | -q]\"),\n-\tN_(\"git notes merge --abort [-v | -q]\"),\n+\t\"git notes merge --commit [-v | -q]\",\n+\t\"git notes merge --abort [-v | -q]\",\n \tN_(\"git notes [--ref <notes-ref>] remove [<object>...]\"),\n \tN_(\"git notes [--ref <notes-ref>] prune [-n] [-v]\"),\n \tN_(\"git notes [--ref <notes-ref>] get-ref\"),\n@@ -89,7 +89,7 @@ static const char * const git_notes_prune_usage[] = {\n };\n \n static const char * const git_notes_get_ref_usage[] = {\n-\tN_(\"git notes get-ref\"),\n+\t\"git notes get-ref\",\n \tNULL\n };\n \ndiff --git a/builtin/prune-packed.c b/builtin/prune-packed.c\nindex b7b9281a8ce..da3273a268b 100644\n--- a/builtin/prune-packed.c\n+++ b/builtin/prune-packed.c\n@@ -3,7 +3,7 @@\n #include \"prune-packed.h\"\n \n static const char * const prune_packed_usage[] = {\n-\tN_(\"git prune-packed [-n | --dry-run] [-q | --quiet]\"),\n+\t\"git prune-packed [-n | --dry-run] [-q | --quiet]\",\n \tNULL\n };\n \ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 36490d06c8a..64796c6a785 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -36,7 +36,7 @@ static char const * const builtin_rebase_usage[] = {\n \t\t\"[--onto <newbase> | --keep-base] [<upstream> [<branch>]]\"),\n \tN_(\"git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] \"\n \t\t\"--root [<branch>]\"),\n-\tN_(\"git rebase --continue | --abort | --skip | --edit-todo\"),\n+\t\"git rebase --continue | --abort | --skip | --edit-todo\",\n \tNULL\n };\n \ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex a4b1dd27e13..ee5ee8d8cf6 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -800,7 +800,7 @@ static int cmd_reflog_exists(int argc, const char **argv, const char *prefix)\n  */\n \n static const char reflog_usage[] =\n-N_(\"git reflog [ show | expire | delete | exists ]\");\n+\"git reflog [ show | expire | delete | exists ]\";\n \n int cmd_reflog(int argc, const char **argv, const char *prefix)\n {\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 299c466116d..6f27ddc47bd 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -14,7 +14,7 @@\n #include \"commit-reach.h\"\n \n static const char * const builtin_remote_usage[] = {\n-\tN_(\"git remote [-v | --verbose]\"),\n+\t\"git remote [-v | --verbose]\",\n \tN_(\"git remote add [-t <branch>] [-m <master>] [-f] [--tags | --no-tags] [--mirror=<fetch|push>] <name> <url>\"),\n \tN_(\"git remote rename <old> <new>\"),\n \tN_(\"git remote remove <name>\"),\ndiff --git a/builtin/replace.c b/builtin/replace.c\nindex 6ff1734d587..ac92337c0ec 100644\n--- a/builtin/replace.c\n+++ b/builtin/replace.c\n@@ -22,7 +22,7 @@ static const char * const git_replace_usage[] = {\n \tN_(\"git replace [-f] <object> <replacement>\"),\n \tN_(\"git replace [-f] --edit <object>\"),\n \tN_(\"git replace [-f] --graft <commit> [<parent>...]\"),\n-\tN_(\"git replace [-f] --convert-graft-file\"),\n+\t\"git replace [-f] --convert-graft-file\",\n \tN_(\"git replace -d <object>...\"),\n \tN_(\"git replace [--format=<format>] [-l [<pattern>]]\"),\n \tNULL\ndiff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c\nindex 679c1070368..771c9869a1c 100644\n--- a/builtin/sparse-checkout.c\n+++ b/builtin/sparse-checkout.c\n@@ -43,7 +43,7 @@ static void write_patterns_to_file(FILE *fp, struct pattern_list *pl)\n }\n \n static char const * const builtin_sparse_checkout_list_usage[] = {\n-\tN_(\"git sparse-checkout list\"),\n+\t\"git sparse-checkout list\",\n \tNULL\n };\n \n@@ -419,7 +419,7 @@ static int update_modes(int *cone_mode, int *sparse_index)\n }\n \n static char const * const builtin_sparse_checkout_init_usage[] = {\n-\tN_(\"git sparse-checkout init [--cone] [--[no-]sparse-index]\"),\n+\t\"git sparse-checkout init [--cone] [--[no-]sparse-index]\",\n \tNULL\n };\n \n@@ -762,7 +762,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)\n }\n \n static char const * const builtin_sparse_checkout_reapply_usage[] = {\n-\tN_(\"git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]\"),\n+\t\"git sparse-checkout reapply [--[no-]cone] [--[no-]sparse-index]\",\n \tNULL\n };\n \n@@ -800,7 +800,7 @@ static int sparse_checkout_reapply(int argc, const char **argv)\n }\n \n static char const * const builtin_sparse_checkout_disable_usage[] = {\n-\tN_(\"git sparse-checkout disable\"),\n+\t\"git sparse-checkout disable\",\n \tNULL\n };\n \ndiff --git a/builtin/stripspace.c b/builtin/stripspace.c\nindex be33eb83c1b..1e34cf2bebd 100644\n--- a/builtin/stripspace.c\n+++ b/builtin/stripspace.c\n@@ -15,8 +15,8 @@ static void comment_lines(struct strbuf *buf)\n }\n \n static const char * const stripspace_usage[] = {\n-\tN_(\"git stripspace [-s | --strip-comments]\"),\n-\tN_(\"git stripspace [-c | --comment-lines]\"),\n+\t\"git stripspace [-s | --strip-comments]\",\n+\t\"git stripspace [-c | --comment-lines]\",\n \tNULL\n };\n \ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex c5d3fc3817f..b80aa9898ac 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -2883,7 +2883,7 @@ static int module_config(int argc, const char **argv, const char *prefix)\n \tconst char *const git_submodule_helper_usage[] = {\n \t\tN_(\"git submodule--helper config <name> [<value>]\"),\n \t\tN_(\"git submodule--helper config --unset <name>\"),\n-\t\tN_(\"git submodule--helper config --check-writeable\"),\n+\t\t\"git submodule--helper config --check-writeable\",\n \t\tNULL\n \t};\n \ndiff --git a/builtin/update-server-info.c b/builtin/update-server-info.c\nindex 4321a344567..880fffec587 100644\n--- a/builtin/update-server-info.c\n+++ b/builtin/update-server-info.c\n@@ -4,7 +4,7 @@\n #include \"parse-options.h\"\n \n static const char * const update_server_info_usage[] = {\n-\tN_(\"git update-server-info [--force]\"),\n+\t\"git update-server-info [--force]\",\n \tNULL\n };\n \n-- \ngitgitgadget\n\n"},{"id":"447374","messageId":"f37b03128b25a18e3257239d20574cddce791bff.1643666870.git.gitgitgadget@gmail.com","threadId":"57287","inReplyTo":"pull.1123.v4.git.1643666870.gitgitgadget@gmail.com","subject":"[PATCH v4 4/4] i18n: fix some misformated placeholders in command synopsis","fromName":"Jean-Noël Avila via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2022-01-31T22:07:49Z","receivedAt":"2022-01-31T22:08:03Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n\n * add '<>' around arguments where missing\n * convert plurals into '...' forms\n\nThis applies the style guide for documentation.\n\nSigned-off-by: Jean-Noël Avila <jn.avila@free.fr>\nReviewed-by: Phillip Wood <phillip.wood123@gmail.com>\n---\n builtin/fast-export.c | 2 +-\n builtin/reflog.c      | 4 ++--\n builtin/rev-list.c    | 2 +-\n 3 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 9f1c730e587..510139e9b54 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -26,7 +26,7 @@\n #include \"commit-slab.h\"\n \n static const char *fast_export_usage[] = {\n-\tN_(\"git fast-export [rev-list-opts]\"),\n+\tN_(\"git fast-export [<rev-list-opts>]\"),\n \tNULL\n };\n \ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex ee5ee8d8cf6..343a10d371b 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -17,10 +17,10 @@ static const char reflog_expire_usage[] =\n N_(\"git reflog expire [--expire=<time>] \"\n    \"[--expire-unreachable=<time>] \"\n    \"[--rewrite] [--updateref] [--stale-fix] [--dry-run | -n] \"\n-   \"[--verbose] [--all] <refs>...\");\n+   \"[--verbose] [--all] <ref>...\");\n static const char reflog_delete_usage[] =\n N_(\"git reflog delete [--rewrite] [--updateref] \"\n-   \"[--dry-run | -n] [--verbose] <refs>...\");\n+   \"[--dry-run | -n] [--verbose] <ref>...\");\n static const char reflog_exists_usage[] =\n N_(\"git reflog exists <ref>\");\n \ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 777558e9b06..38528c7f156 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -20,7 +20,7 @@\n #include \"packfile.h\"\n \n static const char rev_list_usage[] =\n-\"git rev-list [OPTION] <commit-id>... [ -- paths... ]\\n\"\n+\"git rev-list [<options>] <commit-id>... [-- <path>...]\\n\"\n \"  limiting output:\\n\"\n \"    --max-count=<n>\\n\"\n \"    --max-age=<epoch>\\n\"\n-- \ngitgitgadget\n"},{"id":"447377","messageId":"xmqqv8xzh6iw.fsf@gitster.g","threadId":"57287","inReplyTo":"2eac2ef502b86d0c15513c8d0e69928ce2140b1f.1643666870.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v4 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-01-31T22:41:59Z","receivedAt":"2022-01-31T22:42:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Jean-Noël Avila via GitGitGadget\"  <gitgitgadget@gmail.com> writes:\n\n> +inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,\n> +\t\t\t\t      int opt2, const char *opt2_name,\n> +\t\t\t\t      int opt3, const char *opt3_name)\n> +{\n> +\tdie_for_incompatible_opt4(opt1, opt1_name,\n> +\t\t\t\t  opt2, opt2_name,\n> +\t\t\t\t  opt3, opt3_name,\n> +\t\t\t\t  0, \"\");\n> +}\n\nI haven't seen a non-static inline function defined in a common\nheader files.  Does this actually work?  In my build, ld choked on\nthis one.\n\nOtherwise make it \"static inline\"?  Or just\n\n#define die_for_incompatible_opt3(o1,n1,o2,n2,o3,n3) \\\n\tdie_for_incompatible_opt4((o1), (n1), \\\n\t\t\t\t  (o2), (n2), \\\n\t\t\t\t  (o3), (n3), \\\n\t\t\t\t  0, \"\")\n\nperhaps?\n"},{"id":"447397","messageId":"f8202cc7-be5c-5efa-2a3b-987114356c6b@kdbg.org","threadId":"57287","inReplyTo":"xmqqv8xzh6iw.fsf@gitster.g","subject":"Re: [PATCH v4 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-02-01T07:01:30Z","receivedAt":"2022-02-01T07:01:36Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 31.01.22 um 23:41 schrieb Junio C Hamano:\n> \"Jean-Noël Avila via GitGitGadget\"  <gitgitgadget@gmail.com> writes:\n> \n>> +inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,\n>> +\t\t\t\t      int opt2, const char *opt2_name,\n>> +\t\t\t\t      int opt3, const char *opt3_name)\n>> +{\n>> +\tdie_for_incompatible_opt4(opt1, opt1_name,\n>> +\t\t\t\t  opt2, opt2_name,\n>> +\t\t\t\t  opt3, opt3_name,\n>> +\t\t\t\t  0, \"\");\n>> +}\n> \n> I haven't seen a non-static inline function defined in a common\n> header files.  Does this actually work?  In my build, ld choked on\n> this one.\n> \n> Otherwise make it \"static inline\"?  Or just\n> \n> #define die_for_incompatible_opt3(o1,n1,o2,n2,o3,n3) \\\n> \tdie_for_incompatible_opt4((o1), (n1), \\\n> \t\t\t\t  (o2), (n2), \\\n> \t\t\t\t  (o3), (n3), \\\n> \t\t\t\t  0, \"\")\n> \n> perhaps?\n\nPlease no macros where they are not a clear advantage. Make it a\nfunction, either static inline, or out-of-line (the latter would be my\npersonal preference in this case because the function is not called in a\nhot path).\n\n-- Hannes\n"},{"id":"447437","messageId":"xmqqee4mfozc.fsf@gitster.g","threadId":"57287","inReplyTo":"f8202cc7-be5c-5efa-2a3b-987114356c6b@kdbg.org","subject":"Re: [PATCH v4 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-01T17:58:31Z","receivedAt":"2022-02-01T17:58:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Sixt <j6t@kdbg.org> writes:\n\n>> Otherwise make it \"static inline\"?  Or just\n>> \n>> #define die_for_incompatible_opt3(o1,n1,o2,n2,o3,n3) \\\n>> \tdie_for_incompatible_opt4((o1), (n1), \\\n>> \t\t\t\t  (o2), (n2), \\\n>> \t\t\t\t  (o3), (n3), \\\n>> \t\t\t\t  0, \"\")\n>> \n>> perhaps?\n>\n> Please no macros where they are not a clear advantage. Make it a\n> function, either static inline, or out-of-line (the latter would be my\n> personal preference in this case because the function is not called in a\n> hot path).\n\nIn this particular case, my personal preference is actually a macro,\nsince \n\n * there is no type safety lost\n\n * I find that it make it the most clear that \n\n   - the opt3 variant is a mere convenience wrapper, which does not\n     even deserve an entry in the symbol table, of the real thing, and\n\n   - our intention is to keep them closely in sync by not even\n     giving future developers a pair of { braces }, in which they\n     are tempted into writing extra code before or after calling the\n     opt4 variant and make them diverge.\n\nBut it is not very strong preference.\n\nA \"static inline\" wrapper would result in the same code as a macro,\nand I'd be almost equally happy with it.  The code is almost already\nwritten, and fixing it is just a matter of inserting \"static \" in\nfront.  My preference between \"static inline\" and macro is not\nstrong enough to insist on rewriting ;-)\n\nAn out-of-line wrapper has a slight disadvantage that it is not\nimmediately obvious that one is a mere wrapper of the other thing by\njust looking at what is in the *.h file, but I am OK with it as\nwell.  If the original patch were written as an out-of-line wrapper,\nmy preference is not strong enough to insist on rewriting, either.\n\nThanks.\n"},{"id":"447462","messageId":"220201.86a6fa9tmr.gmgdl@evledraar.gmail.com","threadId":"57287","inReplyTo":"844e01391e1198960072844536d736f51573cac6.1643408644.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-01T21:01:52Z","receivedAt":"2022-02-01T21:14:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Jan 28 2022, Jean-Noël Avila via GitGitGadget wrote:\n\n> From: =?UTF-8?q?Jean-No=C3=ABl=20Avila?= <jn.avila@free.fr>\n> [...]\n> +void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n> +\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n> +\t\t\t\t\t\t\t  int opt3, const char *opt3_name)\n> +{\n> +\tint count = 0;\n> +\tconst char *options[3];\n> +\n> +\tif (opt1)\n> +\t\toptions[count++] = opt1_name;\n> +\tif (opt2)\n> +\t\toptions[count++] = opt2_name;\n> +\tif (opt3)\n> +\t\toptions[count++] = opt3_name;\n> +\tif (count > 2)\n> +\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name);\n> +\telse if (count > 1)\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n> +}\n> +\n> +void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n> +\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n> +\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n> +\t\t\t\t\t\t\t  int opt4, const char *opt4_name)\n> +{\n> +\tint count = 0;\n> +\tconst char *options[4];\n> +\n> +\tif (opt1)\n> +\t\toptions[count++] = opt1_name;\n> +\tif (opt2)\n> +\t\toptions[count++] = opt2_name;\n> +\tif (opt3)\n> +\t\toptions[count++] = opt3_name;\n> +\tif (opt4)\n> +\t\toptions[count++] = opt4_name;\n> +\tswitch (count) {\n> +\tcase 4:\n> +\t\tdie(_(\"options '%s', '%s', '%s', and '%s' cannot be used together\"), opt1_name, opt2_name, opt3_name, opt4_name);\n> +\t\tbreak;\n> +\tcase 3:\n> +\t\tdie(_(\"options '%s', '%s', and '%s' cannot be used together\"), options[0], options[1], options[2]);\n> +\t\tbreak;\n> +\tcase 2:\n> +\t\tdie(_(\"options '%s' and '%s' cannot be used together\"), options[0], options[1]);\n> +\t\tbreak;\n> +\tdefault:\n> +\t\tbreak;\n> +\t}\n> +}\n> diff --git a/parse-options.h b/parse-options.h\n> index e22846d3b7b..cf393839ac4 100644\n> --- a/parse-options.h\n> +++ b/parse-options.h\n> @@ -339,4 +339,13 @@ int parse_opt_tracking_mode(const struct option *, const char *, int);\n>  #define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, \"pathspec-file-nul\", v, N_(\"with --pathspec-from-file, pathspec elements are separated with NUL character\"))\n>  #define OPT_AUTOSTASH(v) OPT_BOOL(0, \"autostash\", v, N_(\"automatically stash/stash pop before and after\"))\n>  \n> +void die_if_incompatible_opt3(int opt1, const char *opt1_name,\n> +\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n> +\t\t\t\t\t\t\t  int opt3, const char *opt3_name);\n> +\n> +void die_if_incompatible_opt4(int opt1, const char *opt1_name,\n> +\t\t\t\t\t\t\t  int opt2, const char *opt2_name,\n> +\t\t\t\t\t\t\t  int opt3, const char *opt3_name,\n> +\t\t\t\t\t\t\t  int opt4, const char *opt4_name);\n> +\n>  #endif\n> diff --git a/t/t7500-commit-template-squash-signoff.sh b/t/t7500-commit-template-squash-signoff.sh\n> index 91964653a0b..5fcaa0b4f2a 100755\n> --- a/t/t7500-commit-template-squash-signoff.sh\n> +++ b/t/t7500-commit-template-squash-signoff.sh\n> @@ -442,7 +442,7 @@ test_expect_success '--fixup=reword: give error with pathsec' '\n>  '\n>  \n>  test_expect_success '--fixup=reword: -F give error message' '\n> -\techo \"fatal: Only one of -c/-C/-F/--fixup can be used.\" >expect &&\n> +\techo \"fatal: options '\\''-F'\\'' and '\\''--fixup'\\'' cannot be used together\" >expect &&\n>  \ttest_must_fail git commit --fixup=reword:HEAD~ -F msg  2>actual &&\n>  \ttest_cmp expect actual\n>  '\n\nI've really wanted (and have been meaning to find time to work on) some\nexpansion of what we can get from the \"these are incompatible\" that\nOPT_CMDMODE gives us for a while.\n\nBut I think doing it like this is really going in the wrong direction,\ni.e. we have no need to run all of parse_options(), callbacks and all,\nand populate all the variables, only to after the fact die when we\nnotice both \"a\" and \"b\" were set, and those are incompatible.\n\nI.e. to have the API work like something resembling this:\n\t\n\tdiff --git a/builtin/grep.c b/builtin/grep.c\n\tindex 75e07b5623a..80ea323f957 100644\n\t--- a/builtin/grep.c\n\t+++ b/builtin/grep.c\n\t@@ -849,8 +849,11 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n\t \tstruct option options[] = {\n\t \t\tOPT_BOOL(0, \"cached\", &cached,\n\t \t\t\tN_(\"search in index instead of in the work tree\")),\n\t+\t\tOPT_INCOMPATIBLE(\"cached\", \"untracked\"),\n\t \t\tOPT_NEGBIT(0, \"no-index\", &use_index,\n\t \t\t\t N_(\"find in contents not managed by git\"), 1),\n\t+\t\tOPT_INCOMPATIBLE(\"no-index\", \"untracked\"),\n\t+\t\tOPT_INCOMPATIBLE(\"no-index\", \"cached\"),\n\t \t\tOPT_BOOL(0, \"untracked\", &untracked,\n\t \t\t\tN_(\"search in both tracked and untracked files\")),\n\t \t\tOPT_SET_INT(0, \"exclude-standard\", &opt_exclude,\n\t@@ -1167,12 +1170,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)\n\t \tif (!show_in_pager && !opt.status_only)\n\t \t\tsetup_pager();\n\t \n\t-\tif (!use_index && (untracked || cached))\n\t-\t\tdie(_(\"--cached or --untracked cannot be used with --no-index\"));\n\t-\n\t-\tif (untracked && cached)\n\t-\t\tdie(_(\"--untracked cannot be used with --cached\"));\n\t-\n\t \tif (!use_index || untracked) {\n\t \t\tint use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;\n\t \t\thit = grep_directory(&opt, &pathspec, use_exclude, use_index);\n\t\nI.e. to have this be a new parse_opt_type. Then we'd just make note of\nwhat's incompatible with what other stuff, and in parse_options_step()\ncheck if the option we're currently looking at (e.g. --no-index) is\nstill the only one set out of a list-of-lists incompatible options.\n\nAnd for i18n I really don't think we need to spend effort on detecting a\ncase where --foo --bar and --baz are all incompatible, and saying one\nof:\n\n    --bar is incompatible with --foo\n\nOr:\n\n    --bar is incompatible with --foo and --baz\n\nDepending on whether the command-line is \"--foo --bar\" or \"--foo --bar\n--baz\", let's just in both cases say:\n\n    --bar is incompatible with --foo\n\nThen if the user adjusts \"--foo --bar --baz\" to \"--foo --baz\" we'll just\nshow them a new error, using the same template:\n\n    --baz is incompatible with --foo\n\nI.e. doing this as we iterate options is Good Enough, it's not worth the\ncomplexity or translator time to try to exhaustively list all\nconflicting options at once.\n"},{"id":"447542","messageId":"29731fdd-ca0c-a3f6-84c7-7429ccef5673@gmail.com","threadId":"57287","inReplyTo":"xmqqv8xzh6iw.fsf@gitster.g","subject":"Re: [PATCH v4 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Jean-Noël Avila","fromEmail":"avila.jn@gmail.com","sentAt":"2022-02-02T16:05:10Z","receivedAt":"2022-02-02T16:05:28Z","isPatch":true,"sender":{"key":"jn.avila@free.fr","avatar":"https://avatars.githubusercontent.com/u/156172?v=4"},"body":"Le 31/01/2022 à 23:41, Junio C Hamano a écrit :\n> \"Jean-Noël Avila via GitGitGadget\"  <gitgitgadget@gmail.com> writes:\n>\n>> +inline void die_for_incompatible_opt3(int opt1, const char *opt1_name,\n>> +\t\t\t\t      int opt2, const char *opt2_name,\n>> +\t\t\t\t      int opt3, const char *opt3_name)\n>> +{\n>> +\tdie_for_incompatible_opt4(opt1, opt1_name,\n>> +\t\t\t\t  opt2, opt2_name,\n>> +\t\t\t\t  opt3, opt3_name,\n>> +\t\t\t\t  0, \"\");\n>> +}\n> I haven't seen a non-static inline function defined in a common\n> header files.  Does this actually work?  In my build, ld choked on\n> this one.\n\n\nThis is quite subtle: \"inline\" is just a hint to the compiler but the\n\ncompiler can choose not to inline, so there must be an external symbol\n\nto link to (which is not the case with this code).\n\n\nMy tests and the CI builds went smoothly, so I guess all these compilers\n\nluckily chose to inline, but not yours.\n\n\nWith \"static\", we ensure that either it is inlined or there is a static\n\nfunction (in which case, this bit of code will be duplicated across\n\ncompilation units).\n\n\n\n"},{"id":"447544","messageId":"bc8fb1ae-4ad0-b435-758c-d5ecc12ffe25@kdbg.org","threadId":"57287","inReplyTo":"29731fdd-ca0c-a3f6-84c7-7429ccef5673@gmail.com","subject":"Re: [PATCH v4 1/4] i18n: factorize more 'incompatible options' messages","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2022-02-02T17:29:35Z","receivedAt":"2022-02-02T17:29:40Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 02.02.22 um 17:05 schrieb Jean-Noël Avila:\n> With \"static\", we ensure that either it is inlined or there is a static\n> function (in which case, this bit of code will be duplicated across\n> compilation units).\n\nThat is the plan. Since the function is declared static, the compiler\nhas much more freedom to inline it because it knows that the function is\ninaccessible outside the current compilation unit.\n\nBesides, if you grep for 'inline', you will only find 'static inline'\n(except in some borrowed code). static inline it is, or you pick the\nmacro or out-of-line solution.\n\n-- Hannes\n"}]}