OSDN Git Service

Merge branch 'jn/parse-options-extra'
authorJunio C Hamano <gitster@pobox.com>
Mon, 13 Dec 2010 05:49:53 +0000 (21:49 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 13 Dec 2010 05:49:53 +0000 (21:49 -0800)
* jn/parse-options-extra:
  update-index: migrate to parse-options API
  setup: save prefix (original cwd relative to toplevel) in startup_info
  parse-options: make resuming easier after PARSE_OPT_STOP_AT_NON_OPTION
  parse-options: allow git commands to invent new option types
  parse-options: never suppress arghelp if LITERAL_ARGHELP is set
  parse-options: do not infer PARSE_OPT_NOARG from option type
  parse-options: sanity check PARSE_OPT_NOARG flag
  parse-options: move NODASH sanity checks to parse_options_check
  parse-options: clearer reporting of API misuse
  parse-options: Don't call parse_options_check() so much

1  2 
builtin/blame.c
builtin/update-index.c
cache.h
parse-options.h
setup.c

diff --combined builtin/blame.c
@@@ -83,7 -83,6 +83,7 @@@ struct origin 
        struct commit *commit;
        mmfile_t file;
        unsigned char blob_sha1[20];
 +      unsigned mode;
        char path[FLEX_ARRAY];
  };
  
@@@ -93,7 -92,6 +93,7 @@@
   * Return 1 if the conversion succeeds, 0 otherwise.
   */
  int textconv_object(const char *path,
 +                  unsigned mode,
                    const unsigned char *sha1,
                    char **buf,
                    unsigned long *buf_size)
        struct userdiff_driver *textconv;
  
        df = alloc_filespec(path);
 -      fill_filespec(df, sha1, S_IFREG | 0664);
 +      fill_filespec(df, sha1, mode);
        textconv = get_textconv(df);
        if (!textconv) {
                free_filespec(df);
@@@ -127,7 -125,7 +127,7 @@@ static void fill_origin_blob(struct dif
  
                num_read_blob++;
                if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 -                  textconv_object(o->path, o->blob_sha1, &file->ptr, &file_size))
 +                  textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
                        ;
                else
                        file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@@ -315,23 -313,21 +315,23 @@@ static struct origin *get_origin(struc
   * for an origin is also used to pass the blame for the entire file to
   * the parent to detect the case where a child's blob is identical to
   * that of its parent's.
 + *
 + * This also fills origin->mode for corresponding tree path.
   */
 -static int fill_blob_sha1(struct origin *origin)
 +static int fill_blob_sha1_and_mode(struct origin *origin)
  {
 -      unsigned mode;
        if (!is_null_sha1(origin->blob_sha1))
                return 0;
        if (get_tree_entry(origin->commit->object.sha1,
                           origin->path,
 -                         origin->blob_sha1, &mode))
 +                         origin->blob_sha1, &origin->mode))
                goto error_out;
        if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
                goto error_out;
        return 0;
   error_out:
        hashclr(origin->blob_sha1);
 +      origin->mode = S_IFINVALID;
        return -1;
  }
  
@@@ -364,14 -360,12 +364,14 @@@ static struct origin *find_origin(struc
                        /*
                         * If the origin was newly created (i.e. get_origin
                         * would call make_origin if none is found in the
 -                       * scoreboard), it does not know the blob_sha1,
 +                       * scoreboard), it does not know the blob_sha1/mode,
                         * so copy it.  Otherwise porigin was in the
 -                       * scoreboard and already knows blob_sha1.
 +                       * scoreboard and already knows blob_sha1/mode.
                         */
 -                      if (porigin->refcnt == 1)
 +                      if (porigin->refcnt == 1) {
                                hashcpy(porigin->blob_sha1, cached->blob_sha1);
 +                              porigin->mode = cached->mode;
 +                      }
                        return porigin;
                }
                /* otherwise it was not very useful; free it */
                /* The path is the same as parent */
                porigin = get_origin(sb, parent, origin->path);
                hashcpy(porigin->blob_sha1, origin->blob_sha1);
 +              porigin->mode = origin->mode;
        } else {
                /*
                 * Since origin->path is a pathspec, if the parent
                case 'M':
                        porigin = get_origin(sb, parent, origin->path);
                        hashcpy(porigin->blob_sha1, p->one->sha1);
 +                      porigin->mode = p->one->mode;
                        break;
                case 'A':
                case 'T':
  
                cached = make_origin(porigin->commit, porigin->path);
                hashcpy(cached->blob_sha1, porigin->blob_sha1);
 +              cached->mode = porigin->mode;
                parent->util = cached;
        }
        return porigin;
@@@ -495,7 -486,6 +495,7 @@@ static struct origin *find_rename(struc
                    !strcmp(p->two->path, origin->path)) {
                        porigin = get_origin(sb, parent, p->one->path);
                        hashcpy(porigin->blob_sha1, p->one->sha1);
 +                      porigin->mode = p->one->mode;
                        break;
                }
        }
@@@ -1109,7 -1099,6 +1109,7 @@@ static int find_copy_in_parent(struct s
  
                        norigin = get_origin(sb, parent, p->one->path);
                        hashcpy(norigin->blob_sha1, p->one->sha1);
 +                      norigin->mode = p->one->mode;
                        fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
                        if (!file_p.ptr)
                                continue;
@@@ -1617,7 -1606,6 +1617,7 @@@ static const char *format_time(unsigne
  #define OUTPUT_SHOW_NUMBER    040
  #define OUTPUT_SHOW_SCORE      0100
  #define OUTPUT_NO_AUTHOR       0200
 +#define OUTPUT_SHOW_EMAIL     0400
  
  static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
  {
@@@ -1683,17 -1671,12 +1683,17 @@@ static void emit_other(struct scoreboar
                }
  
                printf("%.*s", length, hex);
 -              if (opt & OUTPUT_ANNOTATE_COMPAT)
 -                      printf("\t(%10s\t%10s\t%d)", ci.author,
 +              if (opt & OUTPUT_ANNOTATE_COMPAT) {
 +                      const char *name;
 +                      if (opt & OUTPUT_SHOW_EMAIL)
 +                              name = ci.author_mail;
 +                      else
 +                              name = ci.author;
 +                      printf("\t(%10s\t%10s\t%d)", name,
                               format_time(ci.author_time, ci.author_tz,
                                           show_raw_time),
                               ent->lno + 1 + cnt);
 -              else {
 +              else {
                        if (opt & OUTPUT_SHOW_SCORE)
                                printf(" %*d %02d",
                                       max_score_digits, ent->score,
                                       ent->s_lno + 1 + cnt);
  
                        if (!(opt & OUTPUT_NO_AUTHOR)) {
 -                              int pad = longest_author - utf8_strwidth(ci.author);
 +                              const char *name;
 +                              int pad;
 +                              if (opt & OUTPUT_SHOW_EMAIL)
 +                                      name = ci.author_mail;
 +                              else
 +                                      name = ci.author;
 +                              pad = longest_author - utf8_strwidth(name);
                                printf(" (%s%*s %10s",
 -                                     ci.author, pad, "",
 +                                     name, pad, "",
                                       format_time(ci.author_time,
                                                   ci.author_tz,
                                                   show_raw_time));
@@@ -1852,10 -1829,7 +1852,10 @@@ static void find_alignment(struct score
                if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
                        suspect->commit->object.flags |= METAINFO_SHOWN;
                        get_commit_info(suspect->commit, &ci, 1);
 -                      num = utf8_strwidth(ci.author);
 +                      if (*option & OUTPUT_SHOW_EMAIL)
 +                              num = utf8_strwidth(ci.author_mail);
 +                      else
 +                              num = utf8_strwidth(ci.author);
                        if (longest_author < num)
                                longest_author = num;
                }
@@@ -2101,7 -2075,7 +2101,7 @@@ static struct commit *fake_working_tree
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
 -                          textconv_object(read_from, null_sha1, &buf.buf, &buf_len))
 +                          textconv_object(read_from, mode, null_sha1, &buf.buf, &buf_len))
                                buf.len = buf_len;
                        else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
                                die_errno("cannot open or read '%s'", read_from);
@@@ -2304,7 -2278,6 +2304,7 @@@ int cmd_blame(int argc, const char **ar
                OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
                OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
                OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
 +              OPT_BIT('e', "show-email", &output_option, "Show author email instead of name (Default: off)", OUTPUT_SHOW_EMAIL),
                OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
                OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
                OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
        save_commit_buffer = 0;
        dashdash_pos = 0;
  
-       parse_options_start(&ctx, argc, argv, prefix, PARSE_OPT_KEEP_DASHDASH |
-                           PARSE_OPT_KEEP_ARGV0);
+       parse_options_start(&ctx, argc, argv, prefix, options,
+                           PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
        for (;;) {
                switch (parse_options_step(&ctx, options, blame_opt_usage)) {
                case PARSE_OPT_HELP:
@@@ -2482,11 -2455,11 +2482,11 @@@ parse_done
        }
        else {
                o = get_origin(&sb, sb.final, path);
 -              if (fill_blob_sha1(o))
 +              if (fill_blob_sha1_and_mode(o))
                        die("no such path %s in %s", path, final_commit_name);
  
                if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
 -                  textconv_object(path, o->blob_sha1, (char **) &sb.final_buf,
 +                  textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
                                    &sb.final_buf_size))
                        ;
                else
diff --combined builtin/update-index.c
@@@ -10,6 -10,7 +10,7 @@@
  #include "builtin.h"
  #include "refs.h"
  #include "resolve-undo.h"
+ #include "parse-options.h"
  
  /*
   * Default to not allowing changes to the list of files. The
@@@ -397,8 -398,10 +398,10 @@@ static void read_index_info(int line_te
        strbuf_release(&uq);
  }
  
- static const char update_index_usage[] =
- "git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--skip-worktree|--no-skip-worktree] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] [<file>...]";
+ static const char * const update_index_usage[] = {
+       "git update-index [options] [--] [<file>...]",
+       NULL
+ };
  
  static unsigned char head_sha1[20];
  static unsigned char merge_head_sha1[20];
@@@ -578,20 -581,212 +581,215 @@@ static int do_reupdate(int ac, const ch
        return 0;
  }
  
+ struct refresh_params {
+       unsigned int flags;
+       int *has_errors;
+ };
+ static int refresh(struct refresh_params *o, unsigned int flag)
+ {
+       setup_work_tree();
+       *o->has_errors |= refresh_cache(o->flags | flag);
+       return 0;
+ }
+ static int refresh_callback(const struct option *opt,
+                               const char *arg, int unset)
+ {
+       return refresh(opt->value, 0);
+ }
+ static int really_refresh_callback(const struct option *opt,
+                               const char *arg, int unset)
+ {
+       return refresh(opt->value, REFRESH_REALLY);
+ }
+ static int chmod_callback(const struct option *opt,
+                               const char *arg, int unset)
+ {
+       char *flip = opt->value;
+       if ((arg[0] != '-' && arg[0] != '+') || arg[1] != 'x' || arg[2])
+               return error("option 'chmod' expects \"+x\" or \"-x\"");
+       *flip = arg[0];
+       return 0;
+ }
+ static int resolve_undo_clear_callback(const struct option *opt,
+                               const char *arg, int unset)
+ {
+       resolve_undo_clear();
+       return 0;
+ }
+ static int cacheinfo_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int unset)
+ {
+       unsigned char sha1[20];
+       unsigned int mode;
+       if (ctx->argc <= 3)
+               return error("option 'cacheinfo' expects three arguments");
+       if (strtoul_ui(*++ctx->argv, 8, &mode) ||
+           get_sha1_hex(*++ctx->argv, sha1) ||
+           add_cacheinfo(mode, sha1, *++ctx->argv, 0))
+               die("git update-index: --cacheinfo cannot add %s", *ctx->argv);
+       ctx->argc -= 3;
+       return 0;
+ }
+ static int stdin_cacheinfo_callback(struct parse_opt_ctx_t *ctx,
+                             const struct option *opt, int unset)
+ {
+       int *line_termination = opt->value;
+       if (ctx->argc != 1)
+               return error("option '%s' must be the last argument", opt->long_name);
+       allow_add = allow_replace = allow_remove = 1;
+       read_index_info(*line_termination);
+       return 0;
+ }
+ static int stdin_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int unset)
+ {
+       int *read_from_stdin = opt->value;
+       if (ctx->argc != 1)
+               return error("option '%s' must be the last argument", opt->long_name);
+       *read_from_stdin = 1;
+       return 0;
+ }
+ static int unresolve_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int flags)
+ {
+       int *has_errors = opt->value;
+       const char *prefix = startup_info->prefix;
+       /* consume remaining arguments. */
+       *has_errors = do_unresolve(ctx->argc, ctx->argv,
+                               prefix, prefix ? strlen(prefix) : 0);
+       if (*has_errors)
+               active_cache_changed = 0;
+       ctx->argv += ctx->argc - 1;
+       ctx->argc = 1;
+       return 0;
+ }
+ static int reupdate_callback(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int flags)
+ {
+       int *has_errors = opt->value;
+       const char *prefix = startup_info->prefix;
+       /* consume remaining arguments. */
+       setup_work_tree();
+       *has_errors = do_reupdate(ctx->argc, ctx->argv,
+                               prefix, prefix ? strlen(prefix) : 0);
+       if (*has_errors)
+               active_cache_changed = 0;
+       ctx->argv += ctx->argc - 1;
+       ctx->argc = 1;
+       return 0;
+ }
  int cmd_update_index(int argc, const char **argv, const char *prefix)
  {
-       int i, newfd, entries, has_errors = 0, line_termination = '\n';
-       int allow_options = 1;
+       int newfd, entries, has_errors = 0, line_termination = '\n';
        int read_from_stdin = 0;
        int prefix_length = prefix ? strlen(prefix) : 0;
        char set_executable_bit = 0;
-       unsigned int refresh_flags = 0;
+       struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
        struct lock_file *lock_file;
+       struct parse_opt_ctx_t ctx;
+       int parseopt_state = PARSE_OPT_UNKNOWN;
+       struct option options[] = {
+               OPT_BIT('q', NULL, &refresh_args.flags,
+                       "continue refresh even when index needs update",
+                       REFRESH_QUIET),
+               OPT_BIT(0, "ignore-submodules", &refresh_args.flags,
+                       "refresh: ignore submodules",
+                       REFRESH_IGNORE_SUBMODULES),
+               OPT_SET_INT(0, "add", &allow_add,
+                       "do not ignore new files", 1),
+               OPT_SET_INT(0, "replace", &allow_replace,
+                       "let files replace directories and vice-versa", 1),
+               OPT_SET_INT(0, "remove", &allow_remove,
+                       "notice files missing from worktree", 1),
+               OPT_BIT(0, "unmerged", &refresh_args.flags,
+                       "refresh even if index contains unmerged entries",
+                       REFRESH_UNMERGED),
+               {OPTION_CALLBACK, 0, "refresh", &refresh_args, NULL,
+                       "refresh stat information",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       refresh_callback},
+               {OPTION_CALLBACK, 0, "really-refresh", &refresh_args, NULL,
+                       "like --refresh, but ignore assume-unchanged setting",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       really_refresh_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 0, "cacheinfo", NULL,
+                       "<mode> <object> <path>",
+                       "add the specified entry to the index",
+                       PARSE_OPT_NOARG |       /* disallow --cacheinfo=<mode> form */
+                       PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+                       (parse_opt_cb *) cacheinfo_callback},
+               {OPTION_CALLBACK, 0, "chmod", &set_executable_bit, "(+/-)x",
+                       "override the executable bit of the listed files",
+                       PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
+                       chmod_callback},
+               {OPTION_SET_INT, 0, "assume-unchanged", &mark_valid_only, NULL,
+                       "mark files as \"not changing\"",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+               {OPTION_SET_INT, 0, "no-assume-unchanged", &mark_valid_only, NULL,
+                       "clear assumed-unchanged bit",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+               {OPTION_SET_INT, 0, "skip-worktree", &mark_skip_worktree_only, NULL,
+                       "mark files as \"index-only\"",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, MARK_FLAG},
+               {OPTION_SET_INT, 0, "no-skip-worktree", &mark_skip_worktree_only, NULL,
+                       "clear skip-worktree bit",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG, NULL, UNMARK_FLAG},
+               OPT_SET_INT(0, "info-only", &info_only,
+                       "add to index only; do not add content to object database", 1),
+               OPT_SET_INT(0, "force-remove", &force_remove,
+                       "remove named paths even if present in worktree", 1),
+               OPT_SET_INT('z', NULL, &line_termination,
+                       "with --stdin: input lines are terminated by null bytes", '\0'),
+               {OPTION_LOWLEVEL_CALLBACK, 0, "stdin", &read_from_stdin, NULL,
+                       "read list of paths to be updated from standard input",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) stdin_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 0, "index-info", &line_termination, NULL,
+                       "add entries from standard input to the index",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) stdin_cacheinfo_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 0, "unresolve", &has_errors, NULL,
+                       "repopulate stages #2 and #3 for the listed paths",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) unresolve_callback},
+               {OPTION_LOWLEVEL_CALLBACK, 'g', "again", &has_errors, NULL,
+                       "only update entries that differ from HEAD",
+                       PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                       (parse_opt_cb *) reupdate_callback},
+               OPT_BIT(0, "ignore-missing", &refresh_args.flags,
+                       "ignore files missing from worktree",
+                       REFRESH_IGNORE_MISSING),
+               OPT_SET_INT(0, "verbose", &verbose,
+                       "report actions to standard output", 1),
+               {OPTION_CALLBACK, 0, "clear-resolve-undo", NULL, NULL,
+                       "(for porcelains) forget saved unresolved conflicts",
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       resolve_undo_clear_callback},
+               OPT_END()
+       };
  
-               usage(update_index_usage);
 +      if (argc == 2 && !strcmp(argv[1], "-h"))
++              usage(update_index_usage[0]);
 +
        git_config(git_default_config, NULL);
  
        /* We can't free this memory, it becomes part of a linked list parsed atexit() */
        if (entries < 0)
                die("cache corrupted");
  
-       for (i = 1 ; i < argc; i++) {
-               const char *path = argv[i];
-               const char *p;
+       /*
+        * Custom copy of parse_options() because we want to handle
+        * filename arguments as they come.
+        */
+       parse_options_start(&ctx, argc, argv, prefix,
+                           options, PARSE_OPT_STOP_AT_NON_OPTION);
+       while (ctx.argc) {
+               if (parseopt_state != PARSE_OPT_DONE)
+                       parseopt_state = parse_options_step(&ctx, options,
+                                                           update_index_usage);
+               if (!ctx.argc)
+                       break;
+               switch (parseopt_state) {
+               case PARSE_OPT_HELP:
+                       exit(129);
+               case PARSE_OPT_NON_OPTION:
+               case PARSE_OPT_DONE:
+               {
+                       const char *path = ctx.argv[0];
+                       const char *p;
  
-               if (allow_options && *path == '-') {
-                       if (!strcmp(path, "--")) {
-                               allow_options = 0;
-                               continue;
-                       }
-                       if (!strcmp(path, "-q")) {
-                               refresh_flags |= REFRESH_QUIET;
-                               continue;
-                       }
-                       if (!strcmp(path, "--ignore-submodules")) {
-                               refresh_flags |= REFRESH_IGNORE_SUBMODULES;
-                               continue;
-                       }
-                       if (!strcmp(path, "--add")) {
-                               allow_add = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--replace")) {
-                               allow_replace = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--remove")) {
-                               allow_remove = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--unmerged")) {
-                               refresh_flags |= REFRESH_UNMERGED;
-                               continue;
-                       }
-                       if (!strcmp(path, "--refresh")) {
-                               setup_work_tree();
-                               has_errors |= refresh_cache(refresh_flags);
-                               continue;
-                       }
-                       if (!strcmp(path, "--really-refresh")) {
-                               setup_work_tree();
-                               has_errors |= refresh_cache(REFRESH_REALLY | refresh_flags);
-                               continue;
-                       }
-                       if (!strcmp(path, "--cacheinfo")) {
-                               unsigned char sha1[20];
-                               unsigned int mode;
-                               if (i+3 >= argc)
-                                       die("git update-index: --cacheinfo <mode> <sha1> <path>");
-                               if (strtoul_ui(argv[i+1], 8, &mode) ||
-                                   get_sha1_hex(argv[i+2], sha1) ||
-                                   add_cacheinfo(mode, sha1, argv[i+3], 0))
-                                       die("git update-index: --cacheinfo"
-                                           " cannot add %s", argv[i+3]);
-                               i += 3;
-                               continue;
-                       }
-                       if (!strcmp(path, "--chmod=-x") ||
-                           !strcmp(path, "--chmod=+x")) {
-                               if (argc <= i+1)
-                                       die("git update-index: %s <path>", path);
-                               set_executable_bit = path[8];
-                               continue;
-                       }
-                       if (!strcmp(path, "--assume-unchanged")) {
-                               mark_valid_only = MARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--no-assume-unchanged")) {
-                               mark_valid_only = UNMARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--no-skip-worktree")) {
-                               mark_skip_worktree_only = UNMARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--skip-worktree")) {
-                               mark_skip_worktree_only = MARK_FLAG;
-                               continue;
-                       }
-                       if (!strcmp(path, "--info-only")) {
-                               info_only = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--force-remove")) {
-                               force_remove = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "-z")) {
-                               line_termination = 0;
-                               continue;
-                       }
-                       if (!strcmp(path, "--stdin")) {
-                               if (i != argc - 1)
-                                       die("--stdin must be at the end");
-                               read_from_stdin = 1;
-                               break;
-                       }
-                       if (!strcmp(path, "--index-info")) {
-                               if (i != argc - 1)
-                                       die("--index-info must be at the end");
-                               allow_add = allow_replace = allow_remove = 1;
-                               read_index_info(line_termination);
-                               break;
-                       }
-                       if (!strcmp(path, "--unresolve")) {
-                               has_errors = do_unresolve(argc - i, argv + i,
-                                                         prefix, prefix_length);
-                               if (has_errors)
-                                       active_cache_changed = 0;
-                               goto finish;
-                       }
-                       if (!strcmp(path, "--again") || !strcmp(path, "-g")) {
-                               setup_work_tree();
-                               has_errors = do_reupdate(argc - i, argv + i,
-                                                        prefix, prefix_length);
-                               if (has_errors)
-                                       active_cache_changed = 0;
-                               goto finish;
-                       }
-                       if (!strcmp(path, "--ignore-missing")) {
-                               refresh_flags |= REFRESH_IGNORE_MISSING;
-                               continue;
-                       }
-                       if (!strcmp(path, "--verbose")) {
-                               verbose = 1;
-                               continue;
-                       }
-                       if (!strcmp(path, "--clear-resolve-undo")) {
-                               resolve_undo_clear();
-                               continue;
-                       }
-                       if (!strcmp(path, "-h") || !strcmp(path, "--help"))
-                               usage(update_index_usage);
-                       die("unknown option %s", path);
+                       setup_work_tree();
+                       p = prefix_path(prefix, prefix_length, path);
+                       update_one(p, NULL, 0);
+                       if (set_executable_bit)
+                               chmod_path(set_executable_bit, p);
+                       if (p < path || p > path + strlen(path))
+                               free((char *)p);
+                       ctx.argc--;
+                       ctx.argv++;
+                       break;
+               }
+               case PARSE_OPT_UNKNOWN:
+                       if (ctx.argv[0][1] == '-')
+                               error("unknown option '%s'", ctx.argv[0] + 2);
+                       else
+                               error("unknown switch '%c'", *ctx.opt);
+                       usage_with_options(update_index_usage, options);
                }
-               setup_work_tree();
-               p = prefix_path(prefix, prefix_length, path);
-               update_one(p, NULL, 0);
-               if (set_executable_bit)
-                       chmod_path(set_executable_bit, p);
-               if (p < path || p > path + strlen(path))
-                       free((char *)p);
        }
+       argc = parse_options_end(&ctx);
        if (read_from_stdin) {
                struct strbuf buf = STRBUF_INIT, nbuf = STRBUF_INIT;
  
                strbuf_release(&buf);
        }
  
-  finish:
        if (active_cache_changed) {
                if (newfd < 0) {
-                       if (refresh_flags & REFRESH_QUIET)
+                       if (refresh_args.flags & REFRESH_QUIET)
                                exit(128);
                        unable_to_lock_index_die(get_index_file(), lock_error);
                }
diff --combined cache.h
+++ b/cache.h
@@@ -545,7 -545,6 +545,7 @@@ extern int assume_unchanged
  extern int prefer_symlink_refs;
  extern int log_all_ref_updates;
  extern int warn_ambiguous_refs;
 +extern int unique_abbrev_extra_length;
  extern int shared_repository;
  extern const char *apply_default_whitespace;
  extern const char *apply_default_ignorewhitespace;
@@@ -860,7 -859,7 +860,7 @@@ struct cache_def 
  
  extern int has_symlink_leading_path(const char *name, int len);
  extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 -extern int has_symlink_or_noent_leading_path(const char *name, int len);
 +extern int check_leading_path(const char *name, int len);
  extern int has_dirs_only_path(const char *name, int len, int prefix_len);
  extern void schedule_dir_for_removal(const char *name, int len);
  extern void remove_scheduled_dirs(void);
@@@ -1004,9 -1003,6 +1004,9 @@@ extern int git_env_bool(const char *, i
  extern int git_config_system(void);
  extern int git_config_global(void);
  extern int config_error_nonbool(const char *);
 +extern const char *get_log_output_encoding(void);
 +extern const char *get_commit_output_encoding(void);
 +
  extern const char *config_exclusive_filename;
  
  #define MAX_GITNAME (1000)
@@@ -1091,17 -1087,15 +1091,17 @@@ void shift_tree_by(const unsigned char 
  /*
   * whitespace rules.
   * used by both diff and apply
 + * last two digits are tab width
   */
 -#define WS_BLANK_AT_EOL         01
 -#define WS_SPACE_BEFORE_TAB   02
 -#define WS_INDENT_WITH_NON_TAB        04
 -#define WS_CR_AT_EOL           010
 -#define WS_BLANK_AT_EOF        020
 -#define WS_TAB_IN_INDENT       040
 +#define WS_BLANK_AT_EOL         0100
 +#define WS_SPACE_BEFORE_TAB     0200
 +#define WS_INDENT_WITH_NON_TAB  0400
 +#define WS_CR_AT_EOL           01000
 +#define WS_BLANK_AT_EOF        02000
 +#define WS_TAB_IN_INDENT       04000
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
 -#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
 +#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
 +#define WS_TAB_WIDTH_MASK        077
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
@@@ -1110,7 -1104,6 +1110,7 @@@ extern void ws_check_emit(const char *l
  extern char *whitespace_error_string(unsigned ws);
  extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
  extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 +#define ws_tab_width(rule)     ((rule) & WS_TAB_WIDTH_MASK)
  
  /* ls-files */
  int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
@@@ -1124,6 -1117,7 +1124,7 @@@ const char *split_cmdline_strerror(int 
  /* git.c */
  struct startup_info {
        int have_repository;
+       const char *prefix;
  };
  extern struct startup_info *startup_info;
  
diff --combined parse-options.h
@@@ -17,6 -17,7 +17,7 @@@ enum parse_opt_type 
        OPTION_STRING,
        OPTION_INTEGER,
        OPTION_CALLBACK,
+       OPTION_LOWLEVEL_CALLBACK,
        OPTION_FILENAME
  };
  
@@@ -43,6 -44,10 +44,10 @@@ enum parse_opt_option_flags 
  struct option;
  typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
  
+ struct parse_opt_ctx_t;
+ typedef int parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
+                               const struct option *opt, int unset);
  /*
   * `type`::
   *   holds the type of the option, you must have an OPTION_END last in your
@@@ -87,7 -92,8 +92,8 @@@
   *                            useful for users of OPTION_NEGBIT.
   *
   * `callback`::
-  *   pointer to the callback to use for OPTION_CALLBACK.
+  *   pointer to the callback to use for OPTION_CALLBACK or
+  *   OPTION_LOWLEVEL_CALLBACK.
   *
   * `defval`::
   *   default value to fill (*->value) with for PARSE_OPT_OPTARG.
@@@ -161,6 -167,7 +167,7 @@@ extern NORETURN void usage_msg_opt(cons
  enum {
        PARSE_OPT_HELP = -1,
        PARSE_OPT_DONE,
+       PARSE_OPT_NON_OPTION,
        PARSE_OPT_UNKNOWN
  };
  
@@@ -180,7 -187,7 +187,7 @@@ struct parse_opt_ctx_t 
  
  extern void parse_options_start(struct parse_opt_ctx_t *ctx,
                                int argc, const char **argv, const char *prefix,
-                               int flags);
+                               const struct option *options, int flags);
  
  extern int parse_options_step(struct parse_opt_ctx_t *ctx,
                              const struct option *options,
@@@ -198,15 -205,14 +205,15 @@@ extern int parse_opt_verbosity_cb(cons
  extern int parse_opt_with_commit(const struct option *, const char *, int);
  extern int parse_opt_tertiary(const struct option *, const char *, int);
  
 -#define OPT__VERBOSE(var)  OPT_BOOLEAN('v', "verbose", (var), "be verbose")
 -#define OPT__QUIET(var)    OPT_BOOLEAN('q', "quiet",   (var), "be quiet")
 +#define OPT__VERBOSE(var, h)  OPT_BOOLEAN('v', "verbose", (var), (h))
 +#define OPT__QUIET(var, h)    OPT_BOOLEAN('q', "quiet",   (var), (h))
  #define OPT__VERBOSITY(var) \
        { OPTION_CALLBACK, 'v', "verbose", (var), NULL, "be more verbose", \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }, \
        { OPTION_CALLBACK, 'q', "quiet", (var), NULL, "be more quiet", \
          PARSE_OPT_NOARG, &parse_opt_verbosity_cb, 0 }
 -#define OPT__DRY_RUN(var)  OPT_BOOLEAN('n', "dry-run", (var), "dry run")
 +#define OPT__DRY_RUN(var, h)  OPT_BOOLEAN('n', "dry-run", (var), (h))
 +#define OPT__FORCE(var, h)    OPT_BOOLEAN('f', "force",   (var), (h))
  #define OPT__ABBREV(var)  \
        { OPTION_CALLBACK, 0, "abbrev", (var), "n", \
          "use <n> digits to display SHA-1s", \
diff --combined setup.c
+++ b/setup.c
@@@ -46,7 -46,7 +46,7 @@@ const char *prefix_filename(const char 
  {
        static char path[PATH_MAX];
  #ifndef WIN32
 -      if (!pfx || !*pfx || is_absolute_path(arg))
 +      if (!pfx_len || is_absolute_path(arg))
                return arg;
        memcpy(path, pfx, pfx_len);
        strcpy(path + pfx_len, arg);
@@@ -55,7 -55,7 +55,7 @@@
        /* don't add prefix to absolute paths, but still replace '\' by '/' */
        if (is_absolute_path(arg))
                pfx_len = 0;
 -      else
 +      else if (pfx_len)
                memcpy(path, pfx, pfx_len);
        strcpy(path + pfx_len, arg);
        for (p = path + pfx_len; *p; p++)
@@@ -512,8 -512,10 +512,10 @@@ const char *setup_git_directory_gently(
        const char *prefix;
  
        prefix = setup_git_directory_gently_1(nongit_ok);
-       if (startup_info)
+       if (startup_info) {
                startup_info->have_repository = !nongit_ok || !*nongit_ok;
+               startup_info->prefix = prefix;
+       }
        return prefix;
  }