]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jk/end-of-options' into jc/sparse-checkout-set-add-end-of-options
authorJunio C Hamano <gitster@pobox.com>
Thu, 21 Dec 2023 05:49:33 +0000 (21:49 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 21 Dec 2023 05:49:33 +0000 (21:49 -0800)
* jk/end-of-options:
  parse-options: decouple "--end-of-options" and "--"

1  2 
parse-options.c
t/t7102-reset.sh

diff --combined parse-options.c
index e0c94b0546b5487c5b324c4c2b76d668289e8b10,813f769870870b676f2f48d53690c11fa77407b3..d50962062ead7dfb4d831199a5dca2e0b4316227
@@@ -1,12 -1,9 +1,12 @@@
  #include "git-compat-util.h"
  #include "parse-options.h"
 -#include "cache.h"
 -#include "config.h"
 +#include "abspath.h"
 +#include "parse.h"
  #include "commit.h"
  #include "color.h"
 +#include "gettext.h"
 +#include "strbuf.h"
 +#include "string-list.h"
  #include "utf8.h"
  
  static int disallow_abbreviated_options;
@@@ -62,18 -59,50 +62,18 @@@ static enum parse_opt_result get_arg(st
        return 0;
  }
  
 -static void fix_filename(const char *prefix, const char **file)
 +static void fix_filename(const char *prefix, char **file)
  {
 -      if (!file || !*file || !prefix || is_absolute_path(*file)
 -          || !strcmp("-", *file))
 -              return;
 -      *file = prefix_filename(prefix, *file);
 -}
 -
 -static enum parse_opt_result opt_command_mode_error(
 -      const struct option *opt,
 -      const struct option *all_opts,
 -      enum opt_parsed flags)
 -{
 -      const struct option *that;
 -      struct strbuf that_name = STRBUF_INIT;
 -
 -      /*
 -       * Find the other option that was used to set the variable
 -       * already, and report that this is not compatible with it.
 -       */
 -      for (that = all_opts; that->type != OPTION_END; that++) {
 -              if (that == opt ||
 -                  !(that->flags & PARSE_OPT_CMDMODE) ||
 -                  that->value != opt->value ||
 -                  that->defval != *(int *)opt->value)
 -                      continue;
 -
 -              if (that->long_name)
 -                      strbuf_addf(&that_name, "--%s", that->long_name);
 -              else
 -                      strbuf_addf(&that_name, "-%c", that->short_name);
 -              error(_("%s is incompatible with %s"),
 -                    optname(opt, flags), that_name.buf);
 -              strbuf_release(&that_name);
 -              return PARSE_OPT_ERROR;
 -      }
 -      return error(_("%s : incompatible with something else"),
 -                   optname(opt, flags));
 +      if (!file || !*file)
 +              ; /* leave as NULL */
 +      else
 +              *file = prefix_filename_except_for_dash(prefix, *file);
  }
  
 -static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
 -                                     const struct option *opt,
 -                                     const struct option *all_opts,
 -                                     enum opt_parsed flags)
 +static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
 +                                        const struct option *opt,
 +                                        enum opt_parsed flags,
 +                                        const char **argp)
  {
        const char *s, *arg;
        const int unset = flags & OPT_UNSET;
        if (!(flags & OPT_SHORT) && p->opt && (opt->flags & PARSE_OPT_NOARG))
                return error(_("%s takes no value"), optname(opt, flags));
  
 -      /*
 -       * Giving the same mode option twice, although unnecessary,
 -       * is not a grave error, so let it pass.
 -       */
 -      if ((opt->flags & PARSE_OPT_CMDMODE) &&
 -          *(int *)opt->value && *(int *)opt->value != opt->defval)
 -              return opt_command_mode_error(opt, all_opts, flags);
 -
        switch (opt->type) {
        case OPTION_LOWLEVEL_CALLBACK:
                return opt->ll_callback(p, opt, NULL, unset);
                        err = get_arg(p, opt, flags, (const char **)opt->value);
  
                if (!err)
 -                      fix_filename(p->prefix, (const char **)opt->value);
 +                      fix_filename(p->prefix, (char **)opt->value);
                return err;
  
        case OPTION_CALLBACK:
                        p_unset = 0;
                        p_arg = arg;
                }
 +              if (opt->flags & PARSE_OPT_CMDMODE)
 +                      *argp = p_arg;
                if (opt->callback)
                        return (*opt->callback)(opt, p_arg, p_unset) ? (-1) : 0;
                else
        }
  }
  
 +struct parse_opt_cmdmode_list {
 +      int value, *value_ptr;
 +      const struct option *opt;
 +      const char *arg;
 +      enum opt_parsed flags;
 +      struct parse_opt_cmdmode_list *next;
 +};
 +
 +static void build_cmdmode_list(struct parse_opt_ctx_t *ctx,
 +                             const struct option *opts)
 +{
 +      ctx->cmdmode_list = NULL;
 +
 +      for (; opts->type != OPTION_END; opts++) {
 +              struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list;
 +              int *value_ptr = opts->value;
 +
 +              if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr)
 +                      continue;
 +
 +              while (elem && elem->value_ptr != value_ptr)
 +                      elem = elem->next;
 +              if (elem)
 +                      continue;
 +
 +              CALLOC_ARRAY(elem, 1);
 +              elem->value_ptr = value_ptr;
 +              elem->value = *value_ptr;
 +              elem->next = ctx->cmdmode_list;
 +              ctx->cmdmode_list = elem;
 +      }
 +}
 +
 +static char *optnamearg(const struct option *opt, const char *arg,
 +                      enum opt_parsed flags)
 +{
 +      if (flags & OPT_SHORT)
 +              return xstrfmt("-%c%s", opt->short_name, arg ? arg : "");
 +      return xstrfmt("--%s%s%s%s", flags & OPT_UNSET ? "no-" : "",
 +                     opt->long_name, arg ? "=" : "", arg ? arg : "");
 +}
 +
 +static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
 +                                     const struct option *opt,
 +                                     enum opt_parsed flags)
 +{
 +      const char *arg = NULL;
 +      enum parse_opt_result result = do_get_value(p, opt, flags, &arg);
 +      struct parse_opt_cmdmode_list *elem = p->cmdmode_list;
 +      char *opt_name, *other_opt_name;
 +
 +      for (; elem; elem = elem->next) {
 +              if (*elem->value_ptr == elem->value)
 +                      continue;
 +
 +              if (elem->opt &&
 +                  (elem->opt->flags | opt->flags) & PARSE_OPT_CMDMODE)
 +                      break;
 +
 +              elem->opt = opt;
 +              elem->arg = arg;
 +              elem->flags = flags;
 +              elem->value = *elem->value_ptr;
 +      }
 +
 +      if (result || !elem)
 +              return result;
 +
 +      opt_name = optnamearg(opt, arg, flags);
 +      other_opt_name = optnamearg(elem->opt, elem->arg, elem->flags);
 +      error(_("%s is incompatible with %s"), opt_name, other_opt_name);
 +      free(opt_name);
 +      free(other_opt_name);
 +      return -1;
 +}
 +
  static enum parse_opt_result parse_short_opt(struct parse_opt_ctx_t *p,
                                             const struct option *options)
  {
 -      const struct option *all_opts = options;
        const struct option *numopt = NULL;
  
        for (; options->type != OPTION_END; options++) {
                if (options->short_name == *p->opt) {
                        p->opt = p->opt[1] ? p->opt + 1 : NULL;
 -                      return get_value(p, options, all_opts, OPT_SHORT);
 +                      return get_value(p, options, OPT_SHORT);
                }
  
                /*
@@@ -355,6 -315,7 +355,6 @@@ static enum parse_opt_result parse_long
        struct parse_opt_ctx_t *p, const char *arg,
        const struct option *options)
  {
 -      const struct option *all_opts = options;
        const char *arg_end = strchrnul(arg, '=');
        const struct option *abbrev_option = NULL, *ambiguous_option = NULL;
        enum opt_parsed abbrev_flags = OPT_LONG, ambiguous_flags = OPT_LONG;
@@@ -423,7 -384,7 +423,7 @@@ is_abbreviated
                                continue;
                        p->opt = rest + 1;
                }
 -              return get_value(p, options, all_opts, flags ^ opt_flags);
 +              return get_value(p, options, flags ^ opt_flags);
        }
  
        if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
                return PARSE_OPT_HELP;
        }
        if (abbrev_option)
 -              return get_value(p, abbrev_option, all_opts, abbrev_flags);
 +              return get_value(p, abbrev_option, abbrev_flags);
        return PARSE_OPT_UNKNOWN;
  }
  
@@@ -449,11 -410,13 +449,11 @@@ static enum parse_opt_result parse_noda
                                              const char *arg,
                                              const struct option *options)
  {
 -      const struct option *all_opts = options;
 -
        for (; options->type != OPTION_END; options++) {
                if (!(options->flags & PARSE_OPT_NODASH))
                        continue;
                if (options->short_name == arg[0] && arg[1] == '\0')
 -                      return get_value(p, options, all_opts, OPT_SHORT);
 +                      return get_value(p, options, OPT_SHORT);
        }
        return PARSE_OPT_ERROR;
  }
@@@ -515,9 -478,6 +515,9 @@@ static void parse_options_check(const s
                     opts->long_name))
                        optbug(opts, "uses feature "
                               "not supported for dashless options");
 +              if (opts->type == OPTION_SET_INT && !opts->defval &&
 +                  opts->long_name && !(opts->flags & PARSE_OPT_NONEG))
 +                      optbug(opts, "OPTION_SET_INT 0 should not be negatable");
                switch (opts->type) {
                case OPTION_COUNTUP:
                case OPTION_BIT:
@@@ -608,7 -568,6 +608,7 @@@ static void parse_options_start_1(struc
            (flags & PARSE_OPT_KEEP_ARGV0))
                BUG("Can't keep argv0 if you don't have it");
        parse_options_check(options);
 +      build_cmdmode_list(ctx, options);
  }
  
  void parse_options_start(struct parse_opt_ctx_t *ctx,
@@@ -929,13 -888,18 +929,18 @@@ enum parse_opt_result parse_options_ste
                        continue;
                }
  
-               if (!arg[2] /* "--" */ ||
-                   !strcmp(arg + 2, "end-of-options")) {
+               if (!arg[2] /* "--" */) {
                        if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
                                ctx->argc--;
                                ctx->argv++;
                        }
                        break;
+               } else if (!strcmp(arg + 2, "end-of-options")) {
+                       if (!(ctx->flags & PARSE_OPT_KEEP_UNKNOWN_OPT)) {
+                               ctx->argc--;
+                               ctx->argv++;
+                       }
+                       break;
                }
  
                if (internal_help && !strcmp(arg + 2, "help-all"))
@@@ -1041,11 -1005,6 +1046,11 @@@ int parse_options(int argc, const char 
        precompose_argv_prefix(argc, argv, NULL);
        free_preprocessed_options(real_options);
        free(ctx.alias_groups);
 +      for (struct parse_opt_cmdmode_list *elem = ctx.cmdmode_list; elem;) {
 +              struct parse_opt_cmdmode_list *next = elem->next;
 +              free(elem);
 +              elem = next;
 +      }
        return parse_options_end(&ctx);
  }
  
@@@ -1064,37 -1023,14 +1069,37 @@@ static int usage_argh(const struct opti
        return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("..."));
  }
  
 -#define USAGE_OPTS_WIDTH 24
 -#define USAGE_GAP         2
 +static int usage_indent(FILE *outfile)
 +{
 +      return fprintf(outfile, "    ");
 +}
 +
 +#define USAGE_OPTS_WIDTH 26
 +
 +static void usage_padding(FILE *outfile, size_t pos)
 +{
 +      if (pos < USAGE_OPTS_WIDTH)
 +              fprintf(outfile, "%*s", USAGE_OPTS_WIDTH - (int)pos, "");
 +      else
 +              fprintf(outfile, "\n%*s", USAGE_OPTS_WIDTH, "");
 +}
 +
 +static const struct option *find_option_by_long_name(const struct option *opts,
 +                                                   const char *long_name)
 +{
 +      for (; opts->type != OPTION_END; opts++) {
 +              if (opts->long_name && !strcmp(opts->long_name, long_name))
 +                      return opts;
 +      }
 +      return NULL;
 +}
  
  static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx,
                                                         const char * const *usagestr,
                                                         const struct option *opts,
                                                         int full, int err)
  {
 +      const struct option *all_opts = opts;
        FILE *outfile = err ? stderr : stdout;
        int need_newline;
  
  
        for (; opts->type != OPTION_END; opts++) {
                size_t pos;
 -              int pad;
 +              const char *cp, *np;
 +              const char *positive_name = NULL;
  
                if (opts->type == OPTION_SUBCOMMAND)
                        continue;
                        need_newline = 0;
                }
  
 -              pos = fprintf(outfile, "    ");
 +              pos = usage_indent(outfile);
                if (opts->short_name) {
                        if (opts->flags & PARSE_OPT_NODASH)
                                pos += fprintf(outfile, "%c", opts->short_name);
                }
                if (opts->long_name && opts->short_name)
                        pos += fprintf(outfile, ", ");
 -              if (opts->long_name)
 -                      pos += fprintf(outfile, "--%s", opts->long_name);
 +              if (opts->long_name) {
 +                      const char *long_name = opts->long_name;
 +                      if ((opts->flags & PARSE_OPT_NONEG) ||
 +                          skip_prefix(long_name, "no-", &positive_name))
 +                              pos += fprintf(outfile, "--%s", long_name);
 +                      else
 +                              pos += fprintf(outfile, "--[no-]%s", long_name);
 +              }
 +
                if (opts->type == OPTION_NUMBER)
                        pos += utf8_fprintf(outfile, _("-NUM"));
  
                    !(opts->flags & PARSE_OPT_NOARG))
                        pos += usage_argh(opts, outfile);
  
 -              if (pos <= USAGE_OPTS_WIDTH)
 -                      pad = USAGE_OPTS_WIDTH - pos;
 -              else {
 -                      fputc('\n', outfile);
 -                      pad = USAGE_OPTS_WIDTH;
 -              }
                if (opts->type == OPTION_ALIAS) {
 -                      fprintf(outfile, "%*s", pad + USAGE_GAP, "");
 +                      usage_padding(outfile, pos);
                        fprintf_ln(outfile, _("alias of --%s"),
                                   (const char *)opts->value);
                        continue;
                }
 -              fprintf(outfile, "%*s%s\n", pad + USAGE_GAP, "", _(opts->help));
 +
 +              for (cp = opts->help ? _(opts->help) : ""; *cp; cp = np) {
 +                      np = strchrnul(cp, '\n');
 +                      if (*np)
 +                              np++;
 +                      usage_padding(outfile, pos);
 +                      fwrite(cp, 1, np - cp, outfile);
 +                      pos = 0;
 +              }
 +              fputc('\n', outfile);
 +
 +              if (positive_name) {
 +                      if (find_option_by_long_name(all_opts, positive_name))
 +                              continue;
 +                      pos = usage_indent(outfile);
 +                      pos += fprintf(outfile, "--%s", positive_name);
 +                      usage_padding(outfile, pos);
 +                      fprintf_ln(outfile, _("opposite of --no-%s"),
 +                                 positive_name);
 +              }
        }
        fputc('\n', outfile);
  
diff --combined t/t7102-reset.sh
index 4287863ae6cab9a67564c9328effd68d2c186ab6,2a3d22592f9b83810978a43650594c8e7442ccb3..62d9f846ce86c54745eb87619d14fefc005d6fe3
@@@ -71,16 -71,6 +71,16 @@@ check_changes () 
        done | test_cmp .cat_expect -
  }
  
 +# no negated form for various type of resets
 +for opt in soft mixed hard merge keep
 +do
 +      test_expect_success "no 'git reset --no-$opt'" '
 +              test_when_finished "rm -f err" &&
 +              test_must_fail git reset --no-$opt 2>err &&
 +              grep "error: unknown option .no-$opt." err
 +      '
 +done
 +
  test_expect_success 'reset --hard message' '
        hex=$(git log -1 --format="%h") &&
        git reset --hard >.actual &&
@@@ -616,4 -606,12 +616,12 @@@ test_expect_success 'reset --mixed set
        test_must_be_empty actual
  '
  
+ test_expect_success 'reset handles --end-of-options' '
+       git update-ref refs/heads/--foo HEAD^ &&
+       git log -1 --format=%s refs/heads/--foo >expect &&
+       git reset --hard --end-of-options --foo &&
+       git log -1 --format=%s HEAD >actual &&
+       test_cmp expect actual
+ '
  test_done