{"thread":{"id":"4135","subject":"[PATCH/RFC] gitopt - command-line parsing enhancements (take #2)","startedAt":"2006-05-14T15:19:17Z","lastAt":"2006-05-16T13:25:41Z","messageCount":12,"participants":["Eric Wong","Linus Torvalds","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"19909","messageId":"11476199622462-git-send-email-normalperson@yhbt.net","threadId":"4135","inReplyTo":null,"subject":"[PATCH/RFC] gitopt - command-line parsing enhancements (take #2)","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-14T15:19:17Z","receivedAt":"2006-05-14T15:19:17Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This should be a less scary series of patches for gitopt.\n\n[PATCH 1/5] gitopt: a new command-line option parser for git\n\n[PATCH 2/5] gitopt: convert ls-files, ls-tree, update-index\n\tSimple conversions.\n\n[PATCH 3/5] gitopt: convert setup_revisions() and friends\n\tThis one is pretty big, some extra testing + review would be\n\tnice.\n\n[PATCH 4/5] commit: allow --pretty= args to be abbreviated\n\tNot strictly related to gitopt, but finger-friendly nevertheless.\n\n[PATCH 5/5] diff: parse U/u/unified options with an optional integer arg\n\tOriginally, this was bundled into:\n\t\t<11471512123005-git-send-email-normalperson@yhbt.net>,\n\tThen Linus did a more correct one that didn't forget combine-diff:\n\t\t<Pine.LNX.4.64.0605131317200.3866@g5.osdl.org>\n\tThis one combines both of them.\n"},{"id":"19910","messageId":"11476199621990-git-send-email-normalperson@yhbt.net","threadId":"4135","inReplyTo":"11476199622462-git-send-email-normalperson@yhbt.net","subject":"[PATCH 1/5] gitopt: a new command-line option parser for git","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-14T15:19:18Z","receivedAt":"2006-05-14T15:19:18Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"It was initially conceived as an addition to the git.c wrapper,\nand not affect other programs.  But it turns out existing C\nprograms can use it pretty easily, too.\n\nFeatures include:\n\n * getopt-style permuting (can easily be disabled for\n   things like update-index)\n\n * command-line compatibile with existing usage:\n   -S=pickaxe-arg-with-leading-equals is unchanged\n\n * printf-style rewriting (for front-ending shell scripts)\n\n * unbundling of short options: -un20z => -u -n20 -z\n\n * automatically understands unambiguous abbreviations\n\n * optional argument handling (-C<num>, -M<num>)\n   -C <num> (with a space between them) has not changed,\n   however, <num> can be a sha1, or a path\n\nChanges from the initial patch:\n\n * automatically handle rewrites when not in pass-through mode (pass-through\n   mode is used for git wrapper only, usually for shell scripts).\n\n * Fixed an off-by-one error in parse_bundled that could cause a segfault\n\n * Supports being called as an iterator mode, as suggested by\n   Junio, meaning:\n\n * no additional global variables required for converting\n   existing C programs\n\n * no more scary macros :)\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n .gitignore           |    1 \n Makefile             |   10 +\n git.c                |   11 +\n gitopt.c             |  662 ++++++++++++++++++++++++++++++++++++++++++++++++++\n gitopt.h             |  120 +++++++++\n gitopt/git_wrapper.h |   45 +++\n gitopt/sh.h          |   45 +++\n t/t0200-gitopt.sh    |  295 ++++++++++++++++++++++\n test-gitopt.c        |  112 ++++++++\n 9 files changed, 1296 insertions(+), 5 deletions(-)\n create mode 100644 gitopt.c\n create mode 100644 gitopt.h\n create mode 100644 gitopt/git_wrapper.h\n create mode 100644 gitopt/sh.h\n create mode 100755 t/t0200-gitopt.sh\n create mode 100644 test-gitopt.c\n\neea531f2f17355161d58b61c3371f496f12c5364\ndiff --git a/.gitignore b/.gitignore\nindex b5959d6..b2d8b06 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -123,6 +123,7 @@ git-write-tree\n git-core-*/?*\n test-date\n test-delta\n+test-gitopt\n common-cmds.h\n *.tar.gz\n *.dsc\ndiff --git a/Makefile b/Makefile\nindex 37fbe78..8fd3e13 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -197,7 +197,7 @@ LIB_H = \\\n \tblob.h cache.h commit.h csum-file.h delta.h \\\n \tdiff.h object.h pack.h pkt-line.h quote.h refs.h \\\n \trun-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \\\n-\ttree-walk.h log-tree.h\n+\ttree-walk.h log-tree.h gitopt.h\n \n DIFF_OBJS = \\\n \tdiff.o diff-lib.o diffcore-break.o diffcore-order.o \\\n@@ -212,6 +212,7 @@ LIB_OBJS = \\\n \tserver-info.o setup.o sha1_file.o sha1_name.o strbuf.o \\\n \ttag.o tree.o usage.o config.o environment.o ctype.o copy.o \\\n \tfetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \\\n+\tgitopt.o \\\n \t$(DIFF_OBJS)\n \n BUILTIN_OBJS = \\\n@@ -470,6 +471,8 @@ all:\n strip: $(PROGRAMS) git$X\n \t$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X\n \n+gitopt.o: gitopt.c gitopt.h gitopt/*.h\n+\n git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS)\n \t$(CC) -DGIT_VERSION='\"$(GIT_VERSION)\"' \\\n \t\t$(ALL_CFLAGS) -o $@ $(filter %.c,$^) \\\n@@ -600,7 +603,7 @@ # with that.\n \n export NO_PYTHON\n \n-test: all\n+test: all test-gitopt$X\n \t$(MAKE) -C t/ all\n \n test-date$X: test-date.c date.o ctype.o\n@@ -609,6 +612,9 @@ test-date$X: test-date.c date.o ctype.o\n test-delta$X: test-delta.c diff-delta.o patch-delta.o\n \t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^\n \n+test-gitopt$X: test-gitopt.c gitopt.o ctype.o usage.o\n+\t$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^\n+\n check:\n \tfor i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done\n \ndiff --git a/git.c b/git.c\nindex 49ba518..785d97f 100644\n--- a/git.c\n+++ b/git.c\n@@ -11,6 +11,7 @@ #include <stdarg.h>\n #include \"git-compat-util.h\"\n #include \"exec_cmd.h\"\n \n+#include \"gitopt/git_wrapper.h\"\n #include \"builtin.h\"\n \n static void prepend_to_path(const char *dir, int len)\n@@ -72,6 +73,7 @@ int main(int argc, const char **argv, ch\n \tchar *slash = strrchr(cmd, '/');\n \tchar git_command[PATH_MAX + 1];\n \tconst char *exec_path = NULL;\n+\tstruct exec_args *ea;\n \n \t/*\n \t * Take the basename of argv[0] as the command\n@@ -99,7 +101,8 @@ int main(int argc, const char **argv, ch\n \tif (!strncmp(cmd, \"git-\", 4)) {\n \t\tcmd += 4;\n \t\targv[0] = cmd;\n-\t\thandle_internal_command(argc, argv, envp);\n+\t\tea = gitopt_parse_git(argc, argv);\n+\t\thandle_internal_command(ea->argc, ea->argv, envp);\n \t\tdie(\"cannot handle %s internally\", cmd);\n \t}\n \n@@ -144,6 +147,8 @@ int main(int argc, const char **argv, ch\n \t}\n \targv[0] = cmd;\n \n+\tea = gitopt_parse_git(argc, argv);\n+\n \t/*\n \t * We search for git commands in the following order:\n \t *  - git_exec_path()\n@@ -157,10 +162,10 @@ int main(int argc, const char **argv, ch\n \tprepend_to_path(exec_path, strlen(exec_path));\n \n \t/* See if it's an internal command */\n-\thandle_internal_command(argc, argv, envp);\n+\thandle_internal_command(ea->argc, ea->argv, envp);\n \n \t/* .. then try the external ones */\n-\texecv_git_cmd(argv);\n+\texecv_git_cmd(ea->argv);\n \n \tif (errno == ENOENT)\n \t\tcmd_usage(0, exec_path, \"'%s' is not a git-command\", cmd);\ndiff --git a/gitopt.c b/gitopt.c\nnew file mode 100644\nindex 0000000..9e85247\n--- /dev/null\n+++ b/gitopt.c\n@@ -0,0 +1,662 @@\n+#include \"git-compat-util.h\"\n+#include \"gitopt.h\"\n+#include \"cache.h\"\n+\n+/* whether or not to pass-through unrecognized switches or to report an error.\n+ * This is only intended to be set to true for use in the git.c wrapper */\n+int gitopt_pass_through = 0;\n+static int gitopt_errno = 0;\n+int gitopt_dd_seen = 0;\t/* double-dash seen flag */\n+\n+struct exec_args *new_exec_args(const int argc)\n+{\n+\tstruct exec_args *ea = xmalloc(sizeof(*ea));\n+\tea->argc = argc;\n+\tea->argv = xcalloc((argc+1), sizeof(char*));\n+\n+\treturn ea;\n+}\n+\n+struct exec_args *nil_exec_args(struct exec_args *ea)\n+{\n+\tint i;\n+\tfor (i = 0; i < ea->argc; i++)\n+\t\tea->argv[i] = NULL;\n+\tea->argc = 0;\n+\treturn ea;\n+}\n+\n+static int combine_exec_args(struct exec_args *dest, struct exec_args *from)\n+{\n+\tint i;\n+\tsize_t nr = 4 + dest->argc + from->argc;\n+\n+\tdest->argv = xrealloc(dest->argv, nr * sizeof(char*));\n+\n+\tfor (i = 0; i < from->argc; i++)\n+\t\tdest->argv[dest->argc++] = from->argv[i];\n+\n+\tdest->argv[dest->argc] = NULL;\n+\n+\treturn i;\n+}\n+\n+static struct exec_args *rewrite_args(const char *rewrite_fmt, const char *arg)\n+{\n+\tstruct exec_args *ea;\n+\tsize_t len = strlen(rewrite_fmt) + (arg ? strlen(arg) : 0);\n+\tchar *dest = xmalloc(len); /* don't free this */\n+\tint nr_ws = 0;\n+\tchar *a, *b;\n+\n+\tif (!arg) {\n+\t\tstrcpy(dest, rewrite_fmt);\n+\t\tif ((a = strstr(dest,\"=%s\")) || (a = strstr(dest,\" %s\")))\n+\t\t\ta[0] = a[1] = a[2] = '\\0';\n+\t\telse if ((a = strstr(dest,\"%s\")))\n+\t\t\ta[0] = a[1] = '\\0';\n+\t} else {\n+\t\tconst char *c = rewrite_fmt;\n+\n+\t\tdo { if (isspace(*c++)) nr_ws++; } while (*c);\n+\t\tsnprintf(dest, len, rewrite_fmt, arg);\n+\t}\n+\n+\tea = new_exec_args(1 + nr_ws);\n+\ta = b = dest;\n+\n+\tfor (ea->argc = 0; nr_ws && *b; b++) {\n+\t\tif (isspace(*b)) {\n+\t\t\t*b = '\\0';\n+\t\t\tif (strlen(a))\n+\t\t\t\tea->argv[ea->argc++] = a;\n+\t\t\ta = b + 1;\n+\t\t}\n+\t}\n+\tif (strlen(a))\n+\t\tea->argv[ea->argc++] = a;\n+\tea->argv[ea->argc] = NULL;\n+\n+\treturn ea;\n+}\n+\n+static struct exec_args *one_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tconst char *c = argv[*argc_pos];\n+\tstruct exec_args *ea = NULL;\n+\tconst char *a = NULL;\n+\tsize_t l_len = s->l ? strlen(s->l) : 0;\n+\tint so = 0; /* short option passed flag */\n+\n+\tif (*c == '-')  {\n+\t\tif (s->s && c[1] != '-') {\n+\t\t\tif (isspace(s->s)) {\n+\t\t\t\ta = c + 1;\n+\t\t\t\tso = 1;\n+\t\t\t} else if (c[1] == s->s) {\n+\t\t\t\ta = c + 2;\n+\t\t\t\tso = 1;\n+\t\t\t}\n+\t\t} else if (s->l && c[1] == '-' && !strncmp(c+2, s->l, l_len))\n+\t\t\ta = c + 2 + l_len;\n+\t}\n+\n+\tif (!a)\n+\t\treturn NULL;\n+\n+\tswitch(a[0]) {\n+\tcase '\\0':\n+\t\tif (((*argc_pos + 1) < argc) && (a = argv[*argc_pos + 1])) {\n+\t\t\t/* optional arguments must be attached to the switch\n+\t\t\t * so that there are no abiguities */\n+\t\t\tif (s->arg_fl & ARG_IS_OPT)\n+\t\t\t\tbreak;\n+\t\t\t*argc_pos += 1;\n+\t\t\tif (s->rewrite_fmt)\n+\t\t\t\tea = rewrite_args(s->rewrite_fmt, a);\n+\t\t\telse if (gitopt_pass_through) {\n+\t\t\t\tea = new_exec_args(2);\n+\t\t\t\tea->argv[0] = c;\n+\t\t\t\tea->argv[1] = a;\n+\t\t\t} else if (s->arg_fl & ARG_ONE)\n+\t\t\t\tea = rewrite_args(\"%s\", a);\n+\t\t}\n+\t\tbreak;\n+\tdefault:\n+\t\tif (!so) {\n+\t\t\t/* only long options get to use \"=\" to denote an arg */\n+\t\t\t/* Make sure -S'= assigned_val;' still works */\n+\t\t\tif (*a == '=')\n+\t\t\t\ta++;\n+\t\t\telse\n+\t\t\t\treturn NULL;\n+\t\t}\n+\t\tif (s->rewrite_fmt)\n+\t\t\tea = rewrite_args(s->rewrite_fmt, a);\n+\t\telse if (gitopt_pass_through) {\n+\t\t\tea = new_exec_args(1);\n+\t\t\tea->argv[0] = c;\n+\t\t} else {\n+\t\t\tif (s->arg_fl & ARG_IS_OPT) {\n+\t\t\t\tea = new_exec_args(2);\n+\t\t\t\tea->argv[0] = \"ignore\";\n+\t\t\t\tea->argv[1] = a;\n+\t\t\t} else if (s->arg_fl & ARG_ONE) {\n+\t\t\t\tea = new_exec_args(1);\n+\t\t\t\tea->argv[0] = a;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn ea;\n+}\n+\n+static struct exec_args *optional_arg_common(struct exec_args *ea,\n+\t\t\tconst struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tif (!ea) {\n+\t\tif (s->rewrite_fmt)\n+\t\t\tea = rewrite_args(s->rewrite_fmt, NULL);\n+\t\telse {\n+\t\t\tea = new_exec_args(1);\n+\t\t\tea->argv[0] = argv[*argc_pos];\n+\t\t}\n+\t}\n+\treturn ea;\n+}\n+\n+struct exec_args *optional_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\treturn optional_arg_common( one_arg(s, argc, argv, argc_pos),\n+\t\t\t\t\t\ts, argc, argv, argc_pos);\n+}\n+\n+static struct exec_args *int_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tstruct exec_args *ea = one_arg(s, argc, argv, argc_pos);\n+\n+\tif (ea) {\n+\t\tconst char *c = ea->argv[ea->argc - 1];\n+\t\tchar *endptr;\n+\t\tlong int tmp;\n+\n+\t\t/* -C<num>: */\n+\t\tif (ea->argc == 1) {\n+\t\t\twhile (*c && !isdigit(*c)) c++;\n+\t\t\tif (!c) goto err;\n+\t\t}\n+\n+\t\tendptr = (char *)c;\n+\t\ttmp = strtol(c, &endptr, 10);\n+\t\tif (endptr == c) {\n+err:\n+\t\t\tif (s->arg_fl & ARG_IS_INT)\n+\t\t\t\t(*argc_pos)--;\n+\t\t\tfree_exec_args(ea);\n+\t\t\treturn NULL;\n+\t\t}\n+\t}\n+\treturn ea;\n+}\n+\n+static struct exec_args *optional_int_arg(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\treturn optional_arg_common( int_arg(s, argc, argv, argc_pos),\n+\t\t\t\t\t\ts, argc, argv, argc_pos);\n+}\n+\n+static struct exec_args *nr_args(int nr, const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tstruct exec_args *ea;\n+\n+\tnr++;\n+\tea = new_exec_args(nr);\n+\tea->argc = 1;\n+\tea->argv[0] = argv[*argc_pos];\n+\twhile (*argc_pos < (argc-1) && ea->argc < nr)\n+\t\tea->argv[ea->argc++] = argv[++(*argc_pos)];\n+\tif (ea->argc != nr)\n+\t\tgitopt_errno = error(\"%s needed %d arguments, to %d\",\n+\t\t\t\tea->argv[0], nr-1, ea->argc - 1);\n+\treturn ea;\n+}\n+\n+static struct exec_args *run_proc(const struct opt_spec *s,\n+\t\t\tconst int argc, const char **argv, int *argc_pos)\n+{\n+\tswitch (s->arg_fl) {\n+\tcase ARG_ONE:\n+\t\treturn one_arg(s, argc, argv, argc_pos);\n+\tcase ARG_INT:\n+\t\treturn int_arg(s, argc, argv, argc_pos);\n+\tcase ARG_OPT:\n+\t\treturn optional_arg(s, argc, argv, argc_pos);\n+\tcase ARG_OPTINT:\n+\t\treturn optional_int_arg(s, argc, argv, argc_pos);\n+\tcase ARG_THREE:\n+\t\treturn nr_args(3, s, argc, argv, argc_pos);\n+\tcase ARG_TWO:\n+\t\treturn nr_args(2, s, argc, argv, argc_pos);\n+\tdefault:\n+\t\treturn NULL;\n+\t}\n+}\n+\n+static const char * parse_bundled(struct gitopt_iterator *gi,\n+\t\t\tconst struct opt_spec *s, const char *cur)\n+{\n+\tstruct exec_args *ea = NULL;\n+\tconst char *orig = cur;\n+\tchar *c = xmalloc(strlen(cur) + 2); /* don't free this */\n+\tconst char *tmp_argv[] = { c };\n+\tint i = 0;\n+\n+\t*c++ = '-';\n+\t*c++ = *cur++;\n+\tif (!s || !s->arg_fl) {\n+\t\tea = new_exec_args(1);\n+\t\tif (s && s->rewrite_fmt)\n+\t\t\tea->argv[0] = s->rewrite_fmt;\n+\t\telse {\n+\t\t\tea->argv[0] = tmp_argv[0];\n+\t\t\t*c = '\\0';\n+\t\t}\n+\t} else if (s->arg_fl) {\n+\t\tif (*cur) {\n+\t\t\t/* no space between the arg and opt switch: */\n+\t\t\tif (s->arg_fl & ARG_IS_INT) {\n+\t\t\t\t/* we know to handle stuff like:\n+\t\t\t\t * -h24w80 => -h=24 -w=80 */\n+\t\t\t\tchar *endptr = (char *)cur;\n+\t\t\t\tstrtol(cur, &endptr, 10);\n+\n+\t\t\t\twhile (cur < endptr)\n+\t\t\t\t\t*c++ = *cur++;\n+\t\t\t} else if (s->arg_fl & ARG_ONE) {\n+\t\t\t\t/* unfortunately, other args are less\n+\t\t\t\t * clear-cut */\n+\t\t\t\twhile (*cur)\n+\t\t\t\t\t*c++ = *cur++;\n+\t\t\t}\n+\t\t\t*c = '\\0';\n+\t\t\tea = run_proc(s, 1, tmp_argv, &i);\n+\t\t} else if ((gi->pos + 1) < gi->argc) {\n+\t\t\tint j = gi->pos;\n+\t\t\tint x = gi->argc - j + 1;\n+\t\t\tconst char **argv2 = xmalloc(x * sizeof(char *));\n+\n+\t\t\t*c = '\\0';\n+\t\t\targv2[i++] = tmp_argv[0];\n+\t\t\twhile (i < x) argv2[i++] = gi->argv[++j];\n+\n+\t\t\ti = 0;\n+\t\t\tea = run_proc(s, x, argv2, &i);\n+\t\t\tgi->pos += i;\n+\t\t\tfree(argv2);\n+\t\t}\n+\t}\n+\tif (ea) {\n+\t\tcombine_exec_args(gi->ea, ea);\n+\t\tfree_exec_args(ea);\n+\t} else\n+\t\tgitopt_errno = error(\"Failed to parse bundled arguments in: %s\",\n+\t\t\t\t\t\t\t\t\torig);\n+\treturn cur;\n+}\n+\n+static int unbundle_iter(struct gitopt_iterator *gi, const struct opt_spec *ost)\n+{\n+\tconst struct opt_spec *s;\n+\tconst char *c, *cur = gi->argv[gi->pos];\n+\n+\tif (!gi->upos || gi->upos < cur || gi->upos > strrchr(cur,0))\n+\t\tgi->upos = cur + 1; /* only short options */\n+\tc = gi->upos;\n+\n+\twhile (*c) {\n+\t\tint i;\n+\t\tfor (i = 0; ost[i].s || ost[i].l; i++) {\n+\t\t\ts = ost + i;\n+\t\t\tif (!s->s || isspace(s->s) || s->s != *c)\n+\t\t\t\tcontinue;\n+\t\t\tc = parse_bundled(gi, s, c);\n+\t\t\tif (c != gi->upos) {\n+\t\t\t\tgi->upos = c;\n+\t\t\t\treturn s->id;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (ost[i].l || ost[i].s) continue;\n+\t\t/* pass-through while unbundling and creating switches:\n+\t\t * this means that if we see -abc here, but we only\n+\t\t * had -a defined in ost (-a defined to not accept args),\n+\t\t * then we'd create switches\n+\t\t * for -b and -c here (since we already knew -a)\n+\t\t * and we're assuming -b and -c were just forgotten\n+\t\t * in the ost.  If we had gotten -bac, that would\n+\t\t * be passed through as -bac in gitopt_parse_ost()\n+\t\t * as an unknown option if -b is undefined in the ost\n+\t\t */\n+\t\tif (gitopt_pass_through) {\n+\t\t\tc = parse_bundled(gi, NULL, c);\n+\t\t\tif (c != gi->upos) {\n+\t\t\t\tgi->upos = c;\n+\t\t\t\treturn GITOPT_ERROR;\n+\t\t\t}\n+\t\t} else {\n+\t\t\t/* non-fatal error, should it be non-fatal? */\n+\t\t\tgitopt_errno = error(\"Unknown option '%s' in '%s'\",\n+\t\t\t\t\t\t\tc, gi->argv[gi->pos]);\n+\t\t\tc++;\n+\t\t}\n+\t}\n+\n+\tgi->upos = c;\n+\treturn GITOPT_ERROR;\n+}\n+\n+\n+static int push_one_opt(struct gitopt_iterator *gi, const struct opt_spec *s)\n+{\n+\tstruct exec_args *ea;\n+\n+\tif (!s->arg_fl) {\n+\t\tgi->ea->argv[gi->ea->argc++] = s->rewrite_fmt ? s->rewrite_fmt\n+\t\t\t\t\t\t\t: gi->argv[gi->pos];\n+\t\treturn s->id;\n+\t}\n+\n+\tif ((ea = run_proc(s, gi->argc, gi->argv, &(gi->pos)))) {\n+\t\tcombine_exec_args(gi->ea, ea);\n+\t\tfree_exec_args(ea);\n+\t\treturn s->id;\n+\t}\n+\tgitopt_errno = error(\"Failed to parse arguments for: %s %s\",\n+\t\t\t\tgi->argv[gi->pos],\n+\t\t\t\tgi->argv[gi->pos+1] ? gi->argv[gi->pos+1] : \"\");\n+\treturn GITOPT_ERROR;\n+}\n+\n+/* look for a prefix abbreviation */\n+static int opt_abbrev_match(const struct opt_spec *s, const char *p)\n+{\n+\tconst char *l = s->l;\n+\n+\twhile (*p) {\n+\t\tif (*l++ != *p++) return 0;\n+\t\tif (!*p || (s->arg_fl && *p == '=')) return 1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* match a short option switch */\n+static int opt_char_match(const struct opt_spec *s, const char *p)\n+{\n+\treturn ((s->s == p[0]) && ((!s->arg_fl && p[1] == '\\0')\n+\t\t\t\t||\n+\t\t\t(s->arg_fl && (p[1] == '\\0' || p[1] == '='))));\n+}\n+\n+/* tokenize on '-' and look for a prefix abbreviation match */\n+static int opt_token_match(const struct opt_spec *s, const char *p0)\n+{\n+\tconst char *l = s->l;\n+\tconst char *p;\n+\n+\twhile ((l = strchr(l,'-'))) {\n+\t\tl++;\n+\t\tp = p0;\n+\t\twhile (*p) {\n+\t\t\tif (*l++ != *p++) break;\n+\t\t\tif (!*p || (s->arg_fl && *p == '=')) return 1;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/* look for unambigious abbreviated switches, if it can't be found,\n+ * assume it's a non-option and pass it to b */\n+static int fallback_long(struct gitopt_iterator *gi,\n+\t\t\tconst struct opt_spec *ost, const char *cur)\n+{\n+\tconst struct opt_spec *s, *found = NULL;\n+\tint i;\n+\n+\t/* look for abbreviations: */\n+\tfor (i = 0; ost[i].l || ost[i].s; i++) {\n+\t\ts = &(ost[i]);\n+\t\t/* maybe they wanted to use a short option\n+\t\t * (normally a single-dash) but typed two dashes instead.\n+\t\t * note: even if we find a short option here, we do not\n+\t\t * attempt to unbundle in this case */\n+\t\tif ((s->s && opt_char_match(s, cur)) ||\n+\t\t\t\t\t(s->l && opt_abbrev_match(s, cur))) {\n+\t\t\tif (found && found->id != s->id)\n+\t\t\t\tgoto pass_through;\n+\t\t\tfound = s;\n+\t\t}\n+\t}\n+\n+\t/* ok, try harder, based on tokenization on '-' */\n+\tif (!found && getenv(\"GIT_ABBREV_HARDER\")) {\n+\t\tfor (i = 0; ost[i].l || ost[i].s; i++) {\n+\t\t\ts = &(ost[i]);\n+\t\t\tif (s->l && opt_token_match(s,cur)) {\n+\t\t\t\tif (found && found->id != s->id)\n+\t\t\t\t\tgoto pass_through;\n+\t\t\t\tfound = s;\n+\t\t\t}\n+\t\t}\n+\t}\n+\t/* should I add a strstr() matcher here for the desperate? */\n+\n+\t/* rewrite the abbreviated switch in it's unabbreviated form: */\n+\tif (found) {\n+\t\tchar *tmp;\n+\t\tsize_t len = 3 + strlen(cur); /* --cur=potential_args\\0 */\n+\t\tsize_t l_len;\n+\n+\t\t/* favor long options: */\n+\t\tl_len = found->l ? strlen(found->l) : 0;\n+\t\ttmp = xcalloc(len + l_len, 1); /* don't free this */\n+\t\ttmp[0] = '-';\n+\n+\t\tif (found->l) {\n+\t\t\ttmp[1] = '-';\n+\t\t\tmemcpy(tmp + 2, found->l, l_len);\n+\t\t} else\n+\t\t\ttmp[1] = found->s;\n+\t\tif (found->arg_fl) {\n+\t\t\tconst char *c;\n+\t\t\tif ((c = strchr(cur,'='))) {\n+\t\t\t\t/* skip '=' for short opts masquerading as\n+\t\t\t\t * long opts: --S=foo */\n+\t\t\t\tif (!l_len) c++;\n+\t\t\t\tstrcpy(tmp + 2 + l_len, c);\n+\t\t\t}\n+\t\t}\n+\n+\t\tgi->argv[gi->pos] = tmp;\n+\t\treturn push_one_opt(gi, found);\n+\t}\n+\n+pass_through:\n+\tif (gitopt_pass_through)\n+\t\treturn GITOPT_NON_OPTION;\n+\tgitopt_errno = error(\"Unknown option: '%s'\",gi->argv[gi->pos]);\n+\treturn GITOPT_ERROR;\n+}\n+\n+static int opt_complete_match(const struct opt_spec *s, const char *p)\n+{\n+\tif (s->arg_fl) {\n+\t\tsize_t len = strlen(s->l);\n+\n+\t\treturn (!strncmp(s->l,p,len) &&\n+\t\t\t\t(p[len] == '\\0' || (p[len] == '=')));\n+\t}\n+\treturn !strcmp(s->l,p);\n+}\n+\n+int gitopt_verify_b_args(const struct exec_args *b)\n+{\n+\tconst char **arg;\n+\n+\tfor (arg = b->argv; *arg; arg++) {\n+\t\t/* anything goes after a double dash */\n+\t\tif (!memcmp(\"--\",*arg,3))\n+\t\t\treturn 1;\n+\t\tif (*arg[0] == '-')\n+\t\t\treturn 0;\n+\t}\n+\treturn 1;\n+}\n+\n+void gitopt_iter_setup(struct gitopt_iterator *gi,\n+\t\t\tconst int argc, const char **argv)\n+{\n+\tgi->upos = NULL;\n+\tgi->pos = 1;\n+\tgi->ea = new_exec_args(4); /* most we currently have is ARG_THREE */\n+\tgi->b = new_exec_args(argc);\n+\tgi->b->argc = 0;\n+\tgi->argc = argc;\n+\tgi->argv = argv;\n+}\n+\n+int gitopt_iter_parse(struct gitopt_iterator *gi,\n+\t\t\tconst struct opt_spec *ost)\n+{\n+\tconst char *c = gi->argv[gi->pos];\n+\tconst struct opt_spec *s;\n+\tint i;\n+\n+\tgitopt_errno = 0;\n+\tif (!c) return 0;\n+\tnil_exec_args(gi->ea);\n+\n+\tif (!gitopt_dd_seen && !memcmp(\"--\",c,3)) {\n+\t\tgitopt_dd_seen = 1;\n+\t\treturn GITOPT_DD;\n+\t}\n+\tif (gitopt_dd_seen)\n+\t\treturn GITOPT_NON_OPTION;\n+\tif (!memcmp(\"--\",c,2)) {\t/* long options */\n+\t\tc += 2;\n+\t\tfor (i = 0; ost[i].l || ost[i].s; i++) {\n+\t\t\ts = &(ost[i]);\n+\t\t\tif (!s->l || !opt_complete_match(s, c)) continue;\n+\t\t\treturn push_one_opt(gi, s);\n+\t\t}\n+\t\t/* undefined --option: */\n+\t\treturn fallback_long(gi, ost, c);\n+\t}\n+\tif ((c[0] == '-') && (c[1] != '-')) { /* short option */\n+\t\tc++;\n+\t\tfor (i = 0; ost[i].l || ost[i].s; i++) {\n+\t\t\ts = &(ost[i]);\n+\t\t\tif (!s->s) continue;\n+\t\t\tif (isspace(s->s) && (*c == '\\0' || isdigit(*c))) {\n+\t\t\t\t/* special case for -<num> */\n+\t\t\t\treturn push_one_opt(gi, s);\n+\t\t\t}\n+\t\t\tif (s->s != *c) continue;\n+\t\t\tif ((c[1] != '\\0') && (c[1] != '='))\n+\t\t\t\treturn unbundle_iter(gi, ost);\n+\t\t\treturn push_one_opt(gi, s);\n+\t\t}\n+\t\t/* undefined: */\n+\t\tif (gitopt_pass_through)\n+\t\t\treturn GITOPT_NON_OPTION;\n+\t\treturn GITOPT_ERROR;\n+\t}\n+\treturn GITOPT_NON_OPTION;\n+}\n+\n+void gitopt_iter_next(struct gitopt_iterator *gi)\n+{\n+\tif (!gi->upos || !gi->upos[0])\n+\t\tgi->pos++;\n+}\n+\n+static int gitopt_parse_ost_split(struct exec_args *a, struct exec_args *b,\n+\t\t\tconst struct opt_spec *ost,\n+\t\t\tconst int argc, const char **argv)\n+{\n+\tstruct gitopt_iterator gi;\n+\n+\tgitopt_dd_seen = 0;\n+\tb->argc = 0;\n+\ta->argv[0] = argv[0];\n+\ta->argc = 1;\n+\n+\tgitopt_iter_setup(&gi, argc, argv);\n+\tfor (; gi.pos < argc; gitopt_iter_next(&gi)) {\n+\t\tswitch (gitopt_iter_parse(&gi, ost)) {\n+\t\tcase GITOPT_ERROR:\n+\t\tcase GITOPT_DD:\n+\t\t\tif (!gitopt_pass_through)\n+\t\t\t\tbreak;\n+\t\tcase GITOPT_NON_OPTION:\n+\t\t\tb->argv[b->argc++] = gi.argv[gi.pos];\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tcombine_exec_args(a, gi.ea);\n+\t\t}\n+\t}\n+\n+\tfree_exec_args(gi.ea);\n+\tfree_exec_args(gi.b);\n+\treturn gitopt_errno;\n+}\n+\n+/* You should really only use this in git (wrapper) and test-gitopt: */\n+struct exec_args *gitopt_parse_ost(const struct opt_spec *ost,\n+\t\t\tconst int argc, const char **argv)\n+{\n+\tstruct exec_args *a = new_exec_args(argc); /* argv[0] and options: */\n+\tstruct exec_args *b = new_exec_args(argc); /* non-option args */\n+\n+\tgitopt_pass_through = 1;\n+\n+\tif (gitopt_parse_ost_split(a, b, ost, argc, argv) < 0)\n+\t\tdie(\"gitopt argument parsing failed\");\n+\tcombine_exec_args(a, b);\n+\tfree_exec_args(b);\n+\n+\treturn a;\n+}\n+\n+void free_exec_args(struct exec_args *ea)\n+{\n+\tfree(ea->argv);\n+\tfree(ea);\n+}\n+\n+struct opt_spec *combine_opt_spec(const struct opt_spec *a,\n+\t\t\t\t\tconst struct opt_spec *b)\n+{\n+\tstruct opt_spec *rv, *tmp;\n+\tsize_t a_size = 0, b_size = 0;\n+\n+\twhile (a[a_size].l || a[a_size].s) a_size++;\n+\twhile (b[b_size].l || b[b_size].s) b_size++;\n+\n+\ttmp = rv = xmalloc( (a_size + b_size + 1) * sizeof(*a) );\n+\n+\twhile (a->s || a->l) memcpy(tmp++, a++, sizeof(*a));\n+\twhile (b->s || b->l) memcpy(tmp++, b++, sizeof(*b));\n+\n+\tmemcpy(tmp, b, sizeof(*b));\n+\n+\treturn rv;\n+}\n+\ndiff --git a/gitopt.h b/gitopt.h\nnew file mode 100644\nindex 0000000..4108cf5\n--- /dev/null\n+++ b/gitopt.h\n@@ -0,0 +1,120 @@\n+#ifndef GITOPT_H\n+#define GITOPT_H\n+\n+/* gitopt_* functions will return this structure\n+ * the elements in this struct can then be treated just\n+ * like their counterparts from main(). */\n+struct exec_args {\n+\tconst char **argv;\n+\tint argc;\n+};\n+\n+#define GITOPT_DIFF_BASE\t\t64\n+#define GITOPT_SR_BASE\t\t\t128\n+\n+enum gitopt_status {\n+\tGITOPT_DD = 253,\n+\tGITOPT_NON_OPTION,\n+\tGITOPT_ERROR\n+};\n+\n+/* @l: long option string (without the leading \"--\")\n+ *\n+ * @s: single option char, ' ' has a special meaning for accepting a\n+ * single '-' (dash), which can also accept an integer argument This is\n+ * for things like \"-5\" => \"--max-count=5\" or \"-\" => \"--stdin\"\n+ *\n+ * @rewrite_fmt: rewrite the passed argument(s) (if any) into this\n+ * (*printf) style string.  Only a single %s can be accepted and handled\n+ * by the default fn() handlers included in gitopt.\n+ *\n+ * Do not use this if you need to use more than one \"%s\", you'll need to\n+ * define and use a custom fn().  rewrite_fmt is only intended for\n+ * the common argument rewriting cases.\n+ *\n+ * If rewrite_fmt has a \"%s\", \" %s\" or \"=%s\" in it, it will be stripped\n+ * out if no arguments are passed to it (this can be the case where\n+ * fn() (see below) is defined to optional_arg).\n+ *\n+ * Any single space between non-space characters will be interpreted as\n+ * break in the option and the options will be split out into seperate\n+ * elements in argv.\n+ *\n+ * @arg_fl: argument flags, see ARG_* #defines\n+ *\n+ * @id: unique identifier to return, must be non-zero and < 64\n+ */\n+struct opt_spec {\n+\tconst char *l;\n+\tconst char s;\n+\tconst char *rewrite_fmt;\n+\tconst unsigned int arg_fl;\n+\tconst int id;\n+};\n+\n+/* internal use: */\n+#define ARG_IS_INT\t0x08\n+#define ARG_IS_OPT\t0x10\n+\n+/* use these for opt_spec flags: */\n+#define ARG_NIL\t\t0x00\n+#define ARG_ONE\t\t0x01\n+#define ARG_TWO\t\t0x02\t/* not really supported yet */\n+#define ARG_THREE\t0x04\t/* not really supported yet */\n+#define ARG_TRE\t\tARG_THREE\n+#define ARG_INT\t\t(ARG_ONE | ARG_IS_INT)\n+#define ARG_OPT\t\t(ARG_ONE | ARG_IS_OPT)\n+#define ARG_OPTINT\t(ARG_ONE | ARG_IS_OPT | ARG_IS_INT)\n+\n+extern int gitopt_pass_through;\n+extern int gitopt_dd_seen;\t/* double-dash seen flag */\n+\n+struct exec_args *new_exec_args(const int argc);\n+void free_exec_args(struct exec_args *ea);\n+struct exec_args *nil_exec_args(struct exec_args *ea);\n+\n+/* You should really only use this in the git wrapper or tests: */\n+struct exec_args *gitopt_parse_ost(const struct opt_spec *ost,\n+\t\t\tconst int argc, const char **argv);\n+\n+/* used for debugging */\n+static inline void dump_ea(const char *pfx, struct exec_args *ea)\n+{\n+\tconst char **arg;\n+\tint i = 0;\n+\tfor (arg = ea->argv; *arg; arg++)\n+\t\tfprintf(stderr,\"[%d] %s: %s\\n\",i++,pfx,*arg);\n+}\n+\n+struct gitopt_iterator {\n+\tstruct exec_args *ea;\t/* tmp, for passing --opt args */\n+\tstruct exec_args *b;\t/* unrecognized arguments */\n+\tconst char *upos;\t/* unbundle position */\n+\tconst char **argv;\n+\tint argc;\n+\tint pos;\t\t/* argc position */\n+};\n+\n+void gitopt_iter_setup(struct gitopt_iterator *gi,\n+\t\t\tconst int argc, const char **argv);\n+int gitopt_iter_parse(struct gitopt_iterator *gi,\n+\t\t\tconst struct opt_spec *ost);\n+void gitopt_iter_next(struct gitopt_iterator *gi);\n+\n+static inline void gitopt_iter_done(struct gitopt_iterator *gi)\n+{\n+\tfree_exec_args(gi->ea);\n+}\n+\n+/* returns a newly allocated opt_spec struct, can be free()-ed: */\n+struct opt_spec *combine_opt_spec(const struct opt_spec *a,\n+\t\t\t\t\tconst struct opt_spec *b);\n+\n+struct gitopt_extra {\n+\tconst struct opt_spec *ost;\n+\tint (*opt_handler)(struct gitopt_iterator *gi,\n+\t\t\tconst int id, void *args);\n+\tvoid *args;\n+};\n+\n+#endif /* GITOPT_H */\ndiff --git a/gitopt/git_wrapper.h b/gitopt/git_wrapper.h\nnew file mode 100644\nindex 0000000..5f27bf4\n--- /dev/null\n+++ b/gitopt/git_wrapper.h\n@@ -0,0 +1,45 @@\n+/* opt_spec table mappings for the git.c wrapper */\n+\n+#include \"../gitopt.h\"\n+#include \"../gitopt/sh.h\"\n+\n+static const struct opt_spec ost_null[] = { { 0, 0 } };\n+\n+static const struct opt_spec_map {\n+\tconst char *cmd;\n+\tconst struct opt_spec *ost;\n+} opt_specs[] = {\n+\t{ \"checkout\",\t\tost_checkout },\n+\t{ \"commit\",\t\tost_commit },\n+\t{ \"am\",\t\t\tost_am},\n+};\n+\n+static const struct opt_spec *find_cmd_ost(const int argc, const char **argv)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opt_specs); i++) {\n+\t\tconst char *cmd = opt_specs[i].cmd;\n+\t\tif (strcmp(cmd, *argv))\n+\t\t\tcontinue;\n+\t\treturn opt_specs[i].ost;\n+\t}\n+\treturn NULL;\n+}\n+\n+static struct exec_args *gitopt_parse_git(const int argc, const char **argv)\n+{\n+\tconst struct opt_spec *ost;\n+\n+\tif (!strcmp(argv[0],\"help\") || !strcmp(argv[0],\"version\") ||\n+\t\t\t\t!(ost = find_cmd_ost(argc, argv))) {\n+\t\tstruct exec_args *ea = new_exec_args(argc);\n+\t\tint i;\n+\n+\t\tfor (i = 0; i <= argc; i++) /* argv[argc] == NULL */\n+\t\t\tea->argv[i] = argv[i];\n+\n+\t\treturn ea;\n+\t}\n+\treturn gitopt_parse_ost(ost, argc, argv);\n+}\ndiff --git a/gitopt/sh.h b/gitopt/sh.h\nnew file mode 100644\nindex 0000000..0ce6620\n--- /dev/null\n+++ b/gitopt/sh.h\n@@ -0,0 +1,45 @@\n+#ifndef GITOPT_SH_H\n+#define GITOPT_SH_H\n+\n+/* opt_spec tables for some git programs written in shell that don't\n+ * have too many options */\n+\n+static const struct opt_spec ost_am[] = {\n+\t{ \"dotest\",\t\t'd',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"interactive\",\t'i',\t0,\t\t0,\t\t0 },\n+\t{ \"binary\",\t\t'b',\t0,\t\t0,\t\t0 },\n+\t{ \"3way\",\t\t'3',\t0,\t\t0,\t\t0 },\n+\t{ \"signoff\",\t\t's',\t0,\t\t0,\t\t0 },\n+\t{ \"utf8\",\t\t'u',\t0,\t\t0,\t\t0 },\n+\t{ \"keep\",\t\t'k',\t0,\t\t0,\t\t0 },\n+\t{ \"resolved\",\t\t'r',\t0,\t\t0,\t\t0 },\n+\t{ \"skip\",\t\t0,\t0,\t\t0,\t\t0 },\n+\t{ \"whitespace\",\t\t0,\t0,\t\tARG_ONE,\t0 },\n+\t{ 0, 0 }\n+};\n+\n+static const struct opt_spec ost_checkout[] = {\n+\t{ 0,\t\t\t'f',\t0,\t\t0,\t\t0 },\n+\t{ 0,\t\t\t'm',\t0,\t\t0,\t\t0 },\n+\t{ 0,\t\t\t'b',\t\"-b %s\",\tARG_ONE,\t0 },\n+\t{ 0, 0 }\n+};\n+\n+static const struct opt_spec ost_commit[] = {\n+\t{ \"file\",\t\t'F',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"all\",\t\t'a',\t0,\t\t0,\t\t0 },\n+\t{ \"author\",\t\t0,\t0,\t\tARG_ONE,\t0 },\n+\t{ \"edit\",\t\t'e',\t0,\t\t0,\t\t0 },\n+\t{ \"include\",\t\t'i',\t0,\t\t0,\t\t0 },\n+\t{ \"only\",\t\t'o',\t0,\t\t0,\t\t0 },\n+\t{ \"message\",\t\t'm',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"no-verify\",\t\t'n',\t0,\t\t0,\t\t0 },\n+\t{ \"amend\",\t\t0,\t0,\t\t0,\t\t0 },\n+\t{ \"reedit\",\t\t'c',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"reuse-message\",\t'C',\t0,\t\tARG_ONE,\t0 },\n+\t{ \"signoff\",\t\t's',\t0,\t\t0,\t\t0 },\n+\t{ \"verbose\",\t\t'v',\t0,\t\t0,\t\t0 },\n+\t{ 0, 0 }\n+};\n+\n+#endif /* GITOPT_SH_H */\ndiff --git a/t/t0200-gitopt.sh b/t/t0200-gitopt.sh\nnew file mode 100755\nindex 0000000..7e140a0\n--- /dev/null\n+++ b/t/t0200-gitopt.sh\n@@ -0,0 +1,295 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Eric Wong\n+#\n+\n+test_description='gitopt command-line pa{ss,rs}er'\n+\n+. ./test-lib.sh\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '(null)'\n+EOF\n+test_expect_success 'single command' \\\n+\t'test-gitopt commit > output && cmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '(null)'\n+EOF\n+test_expect_success 'simple command with switches' \\\n+\t'test-gitopt commit -a -s > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '(null)'\n+EOF\n+test_expect_success 'command with bundled switches' \\\n+\t'test-gitopt commit -as > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '-mhello world'\n+04 '(null)'\n+EOF\n+test_expect_success 'bundle with args for last (no space)' \\\n+\t'test-gitopt commit -asm\"hello world\" > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '-m'\n+04 'hello world'\n+05 '(null)'\n+EOF\n+test_expect_success 'unbundle with args (space)' \\\n+\t'test-gitopt commit -asm \"hello world\" > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '-m'\n+04 'hello world'\n+05 'file1'\n+06 'file2'\n+07 '(null)'\n+EOF\n+test_expect_success 'unbundle and reorder switches and command w/args' \\\n+\t'test-gitopt commit file1 file2 -asm \"hello world\" > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-a'\n+02 '-s'\n+03 '--edit'\n+04 'file2'\n+05 '--'\n+06 'file1'\n+07 '-as'\n+08 '--all'\n+09 '(null)'\n+EOF\n+test_expect_success 'reorder up to and pass-through \"--\"' \\\n+\t'test-gitopt commit -as file2 --edit -- file1 -as --all > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '-s'\n+02 '-m'\n+03 'message'\n+04 '--'\n+05 'file1'\n+06 'file2'\n+07 '(null)'\n+EOF\n+test_expect_success 'reorder up to and pass-through \"--\"' \\\n+\t'test-gitopt commit -sm message -- file1 file2 > output &&\n+\tcmp expect output'\n+\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '--find-copies-harder'\n+02 '(null)'\n+EOF\n+test_expect_success 'abbreviation finder (prefix)' \\\n+\t'test-gitopt whatchanged --find-c > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '--patch-with-raw'\n+02 '(null)'\n+EOF\n+test_expect_success 'abbreviation finder (substring on \"-\" token)' \\\n+\t'GIT_ABBREV_HARDER=1 test-gitopt whatchanged --raw > output &&\n+\t cmp expect output'\n+\n+# we assume unknown switches that cannot be resolved to a single known\n+# switch to just be an new argument we do not know about, so we pass\n+# it to the underlying command\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '--zzzz'\n+02 '(null)'\n+EOF\n+test_expect_success 'ambiguous abbreviation (pass-through)' \\\n+\t'test-gitopt whatchanged --zzzz > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '--diff-filter=MCT'\n+02 'file1'\n+03 '(null)'\n+EOF\n+test_expect_success 'rewrite on long argument' \\\n+\t'test-gitopt whatchanged file1 --diff-filter MCT > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-Shello'\n+02 '(null)'\n+EOF\n+test_expect_success 'rewrite on short argument (#1)' \\\n+\t'test-gitopt whatchanged -Shello > output &&\n+\t cmp expect output'\n+test_expect_success 'rewrite on short argument (#2)' \\\n+\t'test-gitopt whatchanged -S hello > output &&\n+\t cmp expect output'\n+test_expect_success 'rewrite on --short argument (#3)' \\\n+\t'test-gitopt whatchanged --S hello > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-Shello'\n+02 '(null)'\n+EOF\n+test_expect_success 'rewrite on short argument (leading \"=\" arg) (#1)' \\\n+\t'test-gitopt whatchanged --S=hello > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-C20'\n+02 '-M'\n+03 '(null)'\n+EOF\n+test_expect_success 'pass optional arg (#1)' \\\n+\t'test-gitopt whatchanged -C20 -M > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-C'\n+02 '--find-copies-harder'\n+03 '(null)'\n+EOF\n+test_expect_success 'detect optional arg bogus (#1)' \\\n+\t'test-gitopt whatchanged -C --find-copies-harder > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-C20'\n+02 '(null)'\n+EOF\n+test_expect_success 'pass optional arg (#2)' \\\n+\t'test-gitopt whatchanged -C20 > output &&\n+\t cmp expect output'\n+# test_expect_success 'pass optional arg (#3)' \\\n+\t# 'test-gitopt whatchanged -C=20 > output &&\n+\t # cmp expect output'\n+\n+cat > expect <<EOF\n+00 'checkout'\n+01 '-b'\n+02 'newbranch'\n+03 '(null)'\n+EOF\n+test_expect_success 'rewrite short split arg (#1)' \\\n+\t'test-gitopt checkout -bnewbranch > output &&\n+\t cmp expect output'\n+# test_expect_success 'rewrite short split arg (#2)' \\\n+\t# 'test-gitopt checkout --b=newbranch > output &&\n+\t # cmp expect output'\n+test_expect_success 'rewrite short sanity check' \\\n+\t'test-gitopt checkout -b newbranch > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'log'\n+01 '--default'\n+02 'dunno'\n+03 '--all'\n+04 '(null)'\n+EOF\n+test_expect_success 'rewrite long split arg' \\\n+\t'test-gitopt log --default=dunno --all > output &&\n+\t cmp expect output'\n+test_expect_success 'rewrite long sanity check' \\\n+\t'test-gitopt log --default dunno --all > output &&\n+\t cmp expect output'\n+\n+cat > expect <<EOF\n+00 'log'\n+01 '--max-count=56'\n+02 '(null)'\n+EOF\n+test_expect_success 'rewrite -<num> => --max-count=<num>' \\\n+\t'test-gitopt log -56 > output && cmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-u'\n+02 '-l20'\n+03 '-p'\n+04 '-Spicktoken'\n+05 '(null)'\n+EOF\n+test_expect_success 'bundle options with integer args mixed in' \\\n+\t'test-gitopt whatchanged -ul20pSpicktoken > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-u'\n+02 '-C20'\n+03 '-p'\n+04 '(null)'\n+EOF\n+test_expect_success 'bundle options with optional integer args used' \\\n+\t'test-gitopt whatchanged -uC20p > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-u'\n+02 '-C'\n+03 '-p'\n+04 '(null)'\n+EOF\n+test_expect_success 'bundle options with optional integer args not used' \\\n+\t'test-gitopt whatchanged -uCp > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'whatchanged'\n+01 '-C'\n+02 '20'\n+03 '(null)'\n+EOF\n+test_expect_success 'optional integer arg switch must be attached' \\\n+\t'test-gitopt whatchanged -C 20 > output &&\n+\tcmp expect output'\n+\n+cat > expect <<EOF\n+00 'commit'\n+01 '--message'\n+02 'hello world'\n+03 '-s'\n+04 'file'\n+05 '(null)'\n+EOF\n+test_expect_success 'long option argument parsing when short option can work' \\\n+\t'test-gitopt commit --message \"hello world\" -s file > output &&\n+\tcmp expect output'\n+\n+test_done\ndiff --git a/test-gitopt.c b/test-gitopt.c\nnew file mode 100644\nindex 0000000..2692e2b\n--- /dev/null\n+++ b/test-gitopt.c\n@@ -0,0 +1,112 @@\n+\n+#include \"git-compat-util.h\"\n+#include \"gitopt.h\"\n+#include \"gitopt/sh.h\"\n+\n+#define _rev \\\n+{ \"max-count\",\t\t'n',\t\"--max-count=%s\",\tARG_INT,\t0 }, \\\n+{ 0,\t\t\t' ',\t\"--max-count=%s\",\tARG_INT,\t0 }, \\\n+{ \"max-age\",\t\t0,\t\"--max-age=%s\",\t\tARG_INT,\t0 }, \\\n+{ \"min-age\",\t\t0,\t\"--min-age=%s\",\t\tARG_INT,\t0 }, \\\n+{ \"since\",\t\t0,\t\"--since=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"after\",\t\t0,\t\"--after=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"before\",\t\t0,\t\"--before=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"until\",\t\t0,\t\"--until=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"all\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"not\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"default\",\t\t0,\t\"--default %s\",\t\tARG_ONE,\t0 }, \\\n+{ \"topo-order\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"date-order\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"parents\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"dense\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"sparse\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"remove-empty\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"no-merges\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"boundary\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"objects\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"objects-edge\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"unpacked\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'r',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t't',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'm',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'c',\t0,\t\t\t0,\t\t0 }, \\\n+{ \"cc\",\t\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'v',\t0,\t\t\t0,\t\t0 }, \\\n+{ \"pretty\",\t\t0,\t\"--pretty=%s\",\t\tARG_ONE,\t0 }, \\\n+{ \"root\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"no-commit-id\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"always\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"no-abbrev\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"abbrev\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"abbrev-commit\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"full-diff\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+\n+#define _diff \\\n+{ 0,\t\t\t'u',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'p',\t0,\t\t\t0,\t\t0 }, \\\n+{ \"patch-with-raw\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"stat\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"patch-with-stat\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'z',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'l',\t\"-l%s\",\t\t\tARG_INT,\t0 }, \\\n+{ \"full-index\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"name-only\",\t\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"name-status\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'R',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'S',\t\"-S%s\",\t\t\tARG_ONE,\t0 }, \\\n+{ 0,\t\t\t's',\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'O',\t\"-O%s\",\t\t\tARG_ONE,\t0 }, \\\n+{ \"diff-filter\",\t0,\t\"--diff-filter=%s\",\tARG_ONE,\t0 }, \\\n+{ \"pickaxe-all\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"pickaxe-regex\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ 0,\t\t\t'B',\t\"-B%s\",\t\t\tARG_OPTINT,\t0 }, \\\n+{ 0,\t\t\t'M',\t\"-M%s\",\t\t\tARG_OPTINT,\t0 }, \\\n+{ 0,\t\t\t'C',\t\"-C%s\",\t\t\tARG_OPTINT,\t0 }, \\\n+{ \"find-copies-harder\",\t0,\t0,\t\t\t0,\t\t0 }, \\\n+{ \"abbrev\",\t\t0,\t\"--abbrev=%s\",\t\tARG_OPT,\t0 }, \\\n+\n+#define end {0,0}\n+\n+static const struct opt_spec ost_log[] = { _rev end };\n+static const struct opt_spec ost_rev_list[] = { _rev end };\n+static const struct opt_spec ost_whatchanged[] = { _diff _rev end };\n+static const struct opt_spec ost_show[] = { _diff _rev end };\n+\n+static const struct opt_spec_map {\n+\tconst char *cmd;\n+\tconst struct opt_spec *ost;\n+} opt_specs[] = {\n+\t{ \"checkout\",\t\tost_checkout },\n+\t{ \"commit\",\t\tost_commit },\n+\t{ \"log\",\t\tost_log },\n+\t{ \"rev-list\",\t\tost_rev_list },\n+\t{ \"show\",\t\tost_show },\n+\t{ \"whatchanged\",\tost_whatchanged },\n+};\n+\n+\n+int main(int argc, const char **argv, char **envp)\n+{\n+\tint i;\n+\tstruct exec_args *ea;\n+\tconst struct opt_spec *ost = NULL;\n+\n+\tif (!argv[1])\n+\t\tusage(\"test-gitopt [<options>] <command> [<options>]\");\n+\n+\tfor (i = 0; i < ARRAY_SIZE(opt_specs); i++) {\n+\t\tif (!strcmp(argv[1], opt_specs[i].cmd)) {\n+\t\t\tost = opt_specs[i].ost;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (!ost)\n+\t\tusage(\"test-gitopt [<options>] <command> [<options>]\");\n+\n+\tea = gitopt_parse_ost(ost, argc - 1, argv + 1);\n+\n+\tfor (i = 0; i <= ea->argc; i++)\n+\t\tprintf(\"%02d '%s'\\n\", i, ea->argv[i] ? ea->argv[i] : \"(null)\");\n+\n+\treturn 0;\n+}\n-- \n1.3.2.g102e322\n"},{"id":"19911","messageId":"11476199623781-git-send-email-normalperson@yhbt.net","threadId":"4135","inReplyTo":"11476199622462-git-send-email-normalperson@yhbt.net","subject":"[PATCH 2/5] gitopt: convert ls-files, ls-tree, update-index","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-14T15:19:19Z","receivedAt":"2006-05-14T15:19:19Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"All of these are pretty straighforward conversions\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n ls-files.c     |  184 +++++++++++++++++++++-------------------\n ls-tree.c      |   82 ++++++++++++------\n update-index.c |  257 ++++++++++++++++++++++++++++++--------------------------\n 3 files changed, 287 insertions(+), 236 deletions(-)\n\n962175b2be70eab14997a10ebb9c7cd719d72c1b\ndiff --git a/ls-files.c b/ls-files.c\nindex 4a4af1c..9f4d328 100644\n--- a/ls-files.c\n+++ b/ls-files.c\n@@ -10,6 +10,7 @@ #include <fnmatch.h>\n \n #include \"cache.h\"\n #include \"quote.h\"\n+#include \"gitopt.h\"\n \n static int abbrev = 0;\n static int show_deleted = 0;\n@@ -648,133 +649,140 @@ static const char ls_files_usage[] =\n \t\"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] \"\n \t\"[--] [<file>]*\";\n \n+enum ls_files_ids {\n+\topt_z = 1, opt_v, opt_t, opt_c, opt_d, opt_m, opt_o, opt_i, opt_s,\n+\topt_k, opt_directory, opt_no_empty_directory,\n+\topt_u, opt_x, opt_X, opt_exclude_per_dir,\n+\topt_abbrev, opt_full_name, opt_error_unmatch\n+};\n+\n+static const struct opt_spec ls_files_ost[] = {\n+\t{ 0,\t\t\t'z',\t0,\t0,\topt_z },\n+\t{ 0,\t\t\t'v',\t0,\t0,\topt_v },\n+\t{ 0,\t\t\t't',\t0,\t0,\topt_t },\n+\t{ \"cached\",\t\t'c',\t0,\t0,\topt_c },\n+\t{ \"deleted\",\t\t'd',\t0,\t0,\topt_d },\n+\t{ \"modified\",\t\t'm',\t0,\t0,\topt_m },\n+\t{ \"others\",\t\t'o',\t0,\t0,\topt_o },\n+\t{ \"ignored\",\t\t'i',\t0,\t0,\topt_i },\n+\t{ \"stage\",\t\t's',\t0,\t0,\topt_s },\n+\t{ \"killed\",\t\t'k',\t0,\t0,\topt_k },\n+\t{ \"directory\",\t\t0,\t0,\t0,\topt_directory },\n+\t{ \"no-empty-directory\",\t0,\t0,\t0,\topt_no_empty_directory},\n+\t{ \"unmerged\",\t\t'u',\t0,\t0,\topt_u },\n+\t{ \"exclude\",\t\t'x',\t0,\tARG_ONE,\topt_x },\n+\t{ \"exclude-from\",\t'X',\t0,\tARG_ONE,\topt_X },\n+\t{ \"exclude-per-directory\",0,\t0,\tARG_ONE, opt_exclude_per_dir },\n+\t{ \"full-name\",\t\t0,\t0,\t0,\topt_full_name },\n+\t{ \"error-unmatch\",\t0,\t0,\t0,\topt_error_unmatch },\n+\t{ \"abbrev\",\t\t0,\t0,\tARG_OPTINT, opt_abbrev },\n+\t{ 0, 0 }\n+};\n+\n int main(int argc, const char **argv)\n {\n \tint i;\n \tint exc_given = 0;\n+\tstruct gitopt_iterator gi;\n+\tstruct exec_args *ea, *b;\n \n \tprefix = setup_git_directory();\n \tif (prefix)\n \t\tprefix_offset = strlen(prefix);\n \tgit_config(git_default_config);\n \n-\tfor (i = 1; i < argc; i++) {\n-\t\tconst char *arg = argv[i];\n-\n-\t\tif (!strcmp(arg, \"--\")) {\n-\t\t\ti++;\n-\t\t\tbreak;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-z\")) {\n+\tgitopt_iter_setup(&gi, argc, argv);\n+\tea = gi.ea;\n+\tb = gi.b;\n+\tfor (; (i = gitopt_iter_parse(&gi, ls_files_ost));\n+\t\t\t\tgitopt_iter_next(&gi)) {\n+\t\tswitch (i) {\n+\t\tcase opt_z:\n \t\t\tline_terminator = 0;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-t\") || !strcmp(arg, \"-v\")) {\n+\t\t\tbreak;\n+\t\tcase opt_v:\n+\t\t\tshow_valid_bit = 1; /* fall through */\n+\t\tcase opt_t:\n \t\t\ttag_cached = \"H \";\n \t\t\ttag_unmerged = \"M \";\n \t\t\ttag_removed = \"R \";\n \t\t\ttag_modified = \"C \";\n \t\t\ttag_other = \"? \";\n \t\t\ttag_killed = \"K \";\n-\t\t\tif (arg[1] == 'v')\n-\t\t\t\tshow_valid_bit = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-c\") || !strcmp(arg, \"--cached\")) {\n+\t\t\tbreak;\n+\t\tcase opt_c:\n \t\t\tshow_cached = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-d\") || !strcmp(arg, \"--deleted\")) {\n+\t\t\tbreak;\n+\t\tcase opt_d:\n \t\t\tshow_deleted = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-m\") || !strcmp(arg, \"--modified\")) {\n+\t\t\tbreak;\n+\t\tcase opt_m:\n \t\t\tshow_modified = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-o\") || !strcmp(arg, \"--others\")) {\n+\t\t\tbreak;\n+\t\tcase opt_o:\n \t\t\tshow_others = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-i\") || !strcmp(arg, \"--ignored\")) {\n+\t\t\tbreak;\n+\t\tcase opt_i:\n \t\t\tshow_ignored = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-s\") || !strcmp(arg, \"--stage\")) {\n+\t\t\tbreak;\n+\t\tcase opt_s:\n \t\t\tshow_stage = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-k\") || !strcmp(arg, \"--killed\")) {\n+\t\t\tbreak;\n+\t\tcase opt_k:\n \t\t\tshow_killed = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--directory\")) {\n+\t\t\tbreak;\n+\t\tcase opt_directory:\n \t\t\tshow_other_directories = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--no-empty-directory\")) {\n+\t\t\tbreak;\n+\t\tcase opt_no_empty_directory:\n \t\t\thide_empty_directories = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-u\") || !strcmp(arg, \"--unmerged\")) {\n+\t\t\tbreak;\n+\t\tcase opt_u:\n \t\t\t/* There's no point in showing unmerged unless\n \t\t\t * you also show the stage information.\n \t\t\t */\n \t\t\tshow_stage = 1;\n \t\t\tshow_unmerged = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-x\") && i+1 < argc) {\n-\t\t\texc_given = 1;\n-\t\t\tadd_exclude(argv[++i], \"\", 0, &exclude_list[EXC_CMDL]);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strncmp(arg, \"--exclude=\", 10)) {\n-\t\t\texc_given = 1;\n-\t\t\tadd_exclude(arg+10, \"\", 0, &exclude_list[EXC_CMDL]);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"-X\") && i+1 < argc) {\n+\t\t\tbreak;\n+\t\tcase opt_x:\n \t\t\texc_given = 1;\n-\t\t\tadd_excludes_from_file(argv[++i]);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strncmp(arg, \"--exclude-from=\", 15)) {\n+\t\t\tadd_exclude(ea->argv[0], \"\", 0, &exclude_list[EXC_CMDL]);\n+\t\t\tbreak;\n+\t\tcase opt_X:\n \t\t\texc_given = 1;\n-\t\t\tadd_excludes_from_file(arg+15);\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strncmp(arg, \"--exclude-per-directory=\", 24)) {\n+\t\t\tadd_excludes_from_file(ea->argv[0]);\n+\t\t\tbreak;\n+\t\tcase opt_exclude_per_dir:\n \t\t\texc_given = 1;\n-\t\t\texclude_per_dir = arg + 24;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--full-name\")) {\n+\t\t\texclude_per_dir = ea->argv[0];\n+\t\t\tbreak;\n+\t\tcase opt_full_name:\n \t\t\tprefix_offset = 0;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--error-unmatch\")) {\n+\t\t\tbreak;\n+\t\tcase opt_error_unmatch:\n \t\t\terror_unmatch = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strncmp(arg, \"--abbrev=\", 9)) {\n-\t\t\tabbrev = strtoul(arg+9, NULL, 10);\n-\t\t\tif (abbrev && abbrev < MINIMUM_ABBREV)\n-\t\t\t\tabbrev = MINIMUM_ABBREV;\n-\t\t\telse if (abbrev > 40)\n-\t\t\t\tabbrev = 40;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--abbrev\")) {\n-\t\t\tabbrev = DEFAULT_ABBREV;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (*arg == '-')\n+\t\t\tbreak;\n+\t\tcase opt_abbrev:\n+\t\t\tif (ea->argc == 1)\n+\t\t\t\tabbrev = DEFAULT_ABBREV;\n+\t\t\telse {\n+\t\t\t\tif (abbrev && abbrev < MINIMUM_ABBREV)\n+\t\t\t\t\tabbrev = MINIMUM_ABBREV;\n+\t\t\t\telse if (abbrev > 40)\n+\t\t\t\t\tabbrev = 40;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tcase GITOPT_DD:\n+\t\t\tbreak;\n+\t\tcase GITOPT_NON_OPTION:\n+\t\t\tb->argv[b->argc++] = argv[gi.pos];\n+\t\t\tbreak;\n+\t\tcase GITOPT_ERROR:\n \t\t\tusage(ls_files_usage);\n-\t\tbreak;\n+\t\t}\n \t}\n \n-\tpathspec = get_pathspec(prefix, argv + i);\n+\tpathspec = get_pathspec(prefix, b->argv);\n \n \t/* Verify that the pathspec matches the prefix */\n \tif (pathspec)\ndiff --git a/ls-tree.c b/ls-tree.c\nindex f2b3bc1..3de85bc 100644\n--- a/ls-tree.c\n+++ b/ls-tree.c\n@@ -7,6 +7,7 @@ #include \"cache.h\"\n #include \"blob.h\"\n #include \"tree.h\"\n #include \"quote.h\"\n+#include \"gitopt.h\"\n \n static int line_termination = '\\n';\n #define LS_RECURSIVE 1\n@@ -84,56 +85,79 @@ static int show_tree(unsigned char *sha1\n \treturn retval;\n }\n \n+enum ls_tree_ids {\n+\topt_z = 1, opt_r, opt_d, opt_t,\n+\topt_name_only_status, opt_full_name, opt_abbrev\n+};\n+\n+static const struct opt_spec ls_tree_ost[] = {\n+\t{ 0,\t\t\t'z',\t0,\t0,\topt_z },\n+\t{ 0,\t\t\t'r',\t0,\t0,\topt_r },\n+\t{ 0,\t\t\t'd',\t0,\t0,\topt_d },\n+\t{ 0,\t\t\t't',\t0,\t0,\topt_t },\n+\t{ \"name-only\",\t\t0,\t0,\t0,\topt_name_only_status },\n+\t{ \"name-status\",\t0,\t0,\t0,\topt_name_only_status },\n+\t{ \"full-name\",\t\t0,\t0,\t0,\topt_full_name },\n+\t{ \"abbrev\",\t\t0,\t0,\tARG_OPTINT,\topt_abbrev },\n+\t{ 0, 0 }\n+};\n+\n int main(int argc, const char **argv)\n {\n \tunsigned char sha1[20];\n \tstruct tree *tree;\n+\tstruct exec_args *b, *ea;\n+\tstruct gitopt_iterator gi;\n+\tint i;\n \n \tprefix = setup_git_directory();\n \tgit_config(git_default_config);\n \tif (prefix && *prefix)\n \t\tchomp_prefix = strlen(prefix);\n-\twhile (1 < argc && argv[1][0] == '-') {\n-\t\tswitch (argv[1][1]) {\n-\t\tcase 'z':\n+\n+\tgitopt_iter_setup(&gi, argc, argv);\n+\tea = gi.ea;\n+\tb = gi.b;\n+\tfor (; (i = gitopt_iter_parse(&gi, ls_tree_ost));\n+\t\t\t\tgitopt_iter_next(&gi)) {\n+\t\tswitch (i) {\n+\t\tcase opt_z:\n \t\t\tline_termination = 0;\n \t\t\tbreak;\n-\t\tcase 'r':\n+\t\tcase opt_r:\n \t\t\tls_options |= LS_RECURSIVE;\n \t\t\tbreak;\n-\t\tcase 'd':\n+\t\tcase opt_d:\n \t\t\tls_options |= LS_TREE_ONLY;\n \t\t\tbreak;\n-\t\tcase 't':\n+\t\tcase opt_t:\n \t\t\tls_options |= LS_SHOW_TREES;\n \t\t\tbreak;\n-\t\tcase '-':\n-\t\t\tif (!strcmp(argv[1]+2, \"name-only\") ||\n-\t\t\t    !strcmp(argv[1]+2, \"name-status\")) {\n-\t\t\t\tls_options |= LS_NAME_ONLY;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strcmp(argv[1]+2, \"full-name\")) {\n-\t\t\t\tchomp_prefix = 0;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strncmp(argv[1]+2, \"abbrev=\",7)) {\n-\t\t\t\tabbrev = strtoul(argv[1]+9, NULL, 10);\n+\t\tcase opt_name_only_status:\n+\t\t\tls_options |= LS_NAME_ONLY;\n+\t\t\tbreak;\n+\t\tcase opt_full_name:\n+\t\t\tchomp_prefix = 0;\n+\t\t\tbreak;\n+\t\tcase opt_abbrev:\n+\t\t\tif (ea->argc == 1)\n+\t\t\t\tabbrev = DEFAULT_ABBREV;\n+\t\t\telse {\n+\t\t\t\tabbrev = strtoul(ea->argv[1], NULL, 10);\n \t\t\t\tif (abbrev && abbrev < MINIMUM_ABBREV)\n \t\t\t\t\tabbrev = MINIMUM_ABBREV;\n \t\t\t\telse if (abbrev > 40)\n \t\t\t\t\tabbrev = 40;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strcmp(argv[1]+2, \"abbrev\")) {\n-\t\t\t\tabbrev = DEFAULT_ABBREV;\n-\t\t\t\tbreak;\n \t\t\t}\n-\t\t\t/* otherwise fallthru */\n-\t\tdefault:\n+\t\t\tbreak;\n+\t\tcase GITOPT_DD:\n+\t\t\tbreak;\n+\t\tcase GITOPT_NON_OPTION:\n+\t\t\tb->argv[b->argc++] = argv[gi.pos];\n+\t\t\tbreak;\n+\t\tcase GITOPT_ERROR:\n \t\t\tusage(ls_tree_usage);\n \t\t}\n-\t\targc--; argv++;\n \t}\n \t/* -d -r should imply -t, but -d by itself should not have to. */\n \tif ( (LS_TREE_ONLY|LS_RECURSIVE) ==\n@@ -142,10 +166,10 @@ int main(int argc, const char **argv)\n \n \tif (argc < 2)\n \t\tusage(ls_tree_usage);\n-\tif (get_sha1(argv[1], sha1))\n-\t\tdie(\"Not a valid object name %s\", argv[1]);\n+\tif (get_sha1(b->argv[0], sha1))\n+\t\tdie(\"Not a valid object name %s\", b->argv[0]);\n \n-\tpathspec = get_pathspec(prefix, argv + 2);\n+\tpathspec = get_pathspec(prefix, b->argv + 1);\n \ttree = parse_tree_indirect(sha1);\n \tif (!tree)\n \t\tdie(\"not a tree object\");\ndiff --git a/update-index.c b/update-index.c\nindex 3d7e02d..f4e15d7 100644\n--- a/update-index.c\n+++ b/update-index.c\n@@ -7,6 +7,7 @@ #include \"cache.h\"\n #include \"strbuf.h\"\n #include \"quote.h\"\n #include \"tree-walk.h\"\n+#include \"gitopt.h\"\n \n /*\n  * Default to not allowing changes to the list of files. The\n@@ -645,6 +646,39 @@ static int do_reupdate(int ac, const cha\n \treturn 0;\n }\n \n+enum update_index_ids {\n+\topt_q = 1, opt_add, opt_replace, opt_remove, opt_unmerged,\n+\topt_refresh, opt_really_refresh, opt_cacheinfo, opt_chmod,\n+\topt_assume_unchanged, opt_no_assume_unchanged, opt_info_only,\n+\topt_force_remove, opt_z, opt_stdin, opt_index_info, opt_unresolve,\n+\topt_again, opt_ignore_missing, opt_verbose, opt_h\n+};\n+\n+static const struct opt_spec update_index_ost[] = {\n+\t{ 0,\t\t\t'q',\t0,\t0,\topt_q },\n+\t{ \"add\",\t\t0,\t0,\t0,\topt_add },\n+\t{ \"replace\",\t\t0,\t0,\t0,\topt_replace },\n+\t{ \"remove\",\t\t0,\t0,\t0,\topt_remove },\n+\t{ \"unmerged\",\t\t0,\t0,\t0,\topt_unmerged },\n+\t{ \"refresh\",\t\t0,\t0,\t0,\topt_refresh },\n+\t{ \"really-refresh\",\t0,\t0,\t0,\topt_really_refresh },\n+\t{ \"cacheinfo\",\t\t0,\t0,\tARG_TRE, opt_cacheinfo},\n+\t{ \"chmod\",\t\t0,\t\"%s\",\tARG_ONE, opt_chmod },\n+\t{ \"assume-unchanged\",\t0,\t0,\t0,\topt_assume_unchanged },\n+\t{ \"no-assume-unchanged\",0,\t0,\t0,\topt_no_assume_unchanged},\n+\t{ \"info-only\",\t\t0,\t0,\t0,\topt_info_only },\n+\t{ \"force-remove\",\t0,\t0,\t0,\topt_force_remove },\n+\t{ 0,\t\t\t'z',\t0,\t0,\topt_z },\n+\t{ \"stdin\",\t\t0,\t0,\t0,\topt_stdin },\n+\t{ \"index-info\",\t\t0,\t0,\t0,\topt_index_info },\n+\t{ \"unresolve\",\t\t0,\t0,\t0,\topt_unresolve },\n+\t{ \"again\",\t\t0,\t0,\t0,\topt_again },\n+\t{ \"ignore-missing\",\t0,\t0,\t0,\topt_ignore_missing },\n+\t{ \"verbose\",\t\t0,\t0,\t0,\topt_verbose },\n+\t{ \"help\",\t\t'h',\t0,\t0,\topt_h },\n+\t{ 0, 0 }\n+};\n+\n int main(int argc, const char **argv)\n {\n \tint i, newfd, entries, has_errors = 0, line_termination = '\\n';\n@@ -653,6 +687,10 @@ int main(int argc, const char **argv)\n \tconst char *prefix = setup_git_directory();\n \tint prefix_length = prefix ? strlen(prefix) : 0;\n \tchar set_executable_bit = 0;\n+\tstruct exec_args *ea;\n+\tstruct gitopt_iterator gi;\n+\tunsigned char sha1[20];\n+\tunsigned int mode;\n \n \tgit_config(git_default_config);\n \n@@ -664,126 +702,107 @@ int main(int argc, const char **argv)\n \tif (entries < 0)\n \t\tdie(\"cache corrupted\");\n \n-\tfor (i = 1 ; i < argc; i++) {\n-\t\tconst char *path = argv[i];\n-\n-\t\tif (allow_options && *path == '-') {\n-\t\t\tif (!strcmp(path, \"--\")) {\n-\t\t\t\tallow_options = 0;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"-q\")) {\n-\t\t\t\tquiet = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--add\")) {\n-\t\t\t\tallow_add = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--replace\")) {\n-\t\t\t\tallow_replace = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--remove\")) {\n-\t\t\t\tallow_remove = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--unmerged\")) {\n-\t\t\t\tallow_unmerged = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--refresh\")) {\n-\t\t\t\thas_errors |= refresh_cache(0);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--really-refresh\")) {\n-\t\t\t\thas_errors |= refresh_cache(1);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--cacheinfo\")) {\n-\t\t\t\tunsigned char sha1[20];\n-\t\t\t\tunsigned int mode;\n-\n-\t\t\t\tif (i+3 >= argc)\n-\t\t\t\t\tdie(\"git-update-index: --cacheinfo <mode> <sha1> <path>\");\n-\n-\t\t\t\tif ((sscanf(argv[i+1], \"%o\", &mode) != 1) ||\n-\t\t\t\t    get_sha1_hex(argv[i+2], sha1) ||\n-\t\t\t\t    add_cacheinfo(mode, sha1, argv[i+3], 0))\n-\t\t\t\t\tdie(\"git-update-index: --cacheinfo\"\n-\t\t\t\t\t    \" cannot add %s\", argv[i+3]);\n-\t\t\t\ti += 3;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--chmod=-x\") ||\n-\t\t\t    !strcmp(path, \"--chmod=+x\")) {\n-\t\t\t\tif (argc <= i+1)\n-\t\t\t\t\tdie(\"git-update-index: %s <path>\", path);\n-\t\t\t\tset_executable_bit = path[8];\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--assume-unchanged\")) {\n-\t\t\t\tmark_valid_only = MARK_VALID;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--no-assume-unchanged\")) {\n-\t\t\t\tmark_valid_only = UNMARK_VALID;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--info-only\")) {\n-\t\t\t\tinfo_only = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--force-remove\")) {\n-\t\t\t\tforce_remove = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"-z\")) {\n-\t\t\t\tline_termination = 0;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--stdin\")) {\n-\t\t\t\tif (i != argc - 1)\n-\t\t\t\t\tdie(\"--stdin must be at the end\");\n-\t\t\t\tread_from_stdin = 1;\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--index-info\")) {\n-\t\t\t\tif (i != argc - 1)\n-\t\t\t\t\tdie(\"--index-info must be at the end\");\n-\t\t\t\tallow_add = allow_replace = allow_remove = 1;\n-\t\t\t\tread_index_info(line_termination);\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--unresolve\")) {\n-\t\t\t\thas_errors = do_unresolve(argc - i, argv + i,\n-\t\t\t\t\t\t\t  prefix, prefix_length);\n-\t\t\t\tif (has_errors)\n-\t\t\t\t\tactive_cache_changed = 0;\n-\t\t\t\tgoto finish;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--again\")) {\n-\t\t\t\thas_errors = do_reupdate(argc - i, argv + i,\n-\t\t\t\t\t\t\t prefix, prefix_length);\n-\t\t\t\tif (has_errors)\n-\t\t\t\t\tactive_cache_changed = 0;\n-\t\t\t\tgoto finish;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--ignore-missing\")) {\n-\t\t\t\tnot_new = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"--verbose\")) {\n-\t\t\t\tverbose = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(path, \"-h\") || !strcmp(path, \"--help\"))\n-\t\t\t\tusage(update_index_usage);\n-\t\t\tdie(\"unknown option %s\", path);\n+\tgitopt_iter_setup(&gi, argc, argv);\n+\tea = gi.ea;\n+\tfor (; (i = gitopt_iter_parse(&gi, update_index_ost));\n+\t\t\t\tgitopt_iter_next(&gi)) {\n+\t\tswitch (i) {\n+\t\tcase opt_q:\n+\t\t\tquiet = 1;\n+\t\t\tbreak;\n+\t\tcase opt_add:\n+\t\t\tallow_add = 1;\n+\t\t\tbreak;\n+\t\tcase opt_replace:\n+\t\t\tallow_replace = 1;\n+\t\t\tbreak;\n+\t\tcase opt_remove:\n+\t\t\tallow_remove = 1;\n+\t\t\tbreak;\n+\t\tcase opt_unmerged:\n+\t\t\tallow_unmerged = 1;\n+\t\t\tbreak;\n+\t\tcase opt_refresh:\n+\t\t\thas_errors |= refresh_cache(0);\n+\t\t\tbreak;\n+\t\tcase opt_really_refresh:\n+\t\t\thas_errors |= refresh_cache(1);\n+\t\t\tbreak;\n+\t\tcase opt_cacheinfo:\n+\t\t\tif (ea->argc != 4)\n+\t\t\t\tdie(\"git-update-index: --cacheinfo \"\n+\t\t\t\t    \"<mode> <sha1> <path>\");\n+\t\t\tif ((sscanf(ea->argv[1], \"%o\", &mode) != 1) ||\n+\t\t\t    get_sha1_hex(ea->argv[2], sha1) ||\n+\t\t\t    add_cacheinfo(mode, sha1, ea->argv[3], 0))\n+\t\t\t\tdie(\"git-update-index: --cacheinfo\"\n+\t\t\t\t    \" cannot add %s\", ea->argv[3]);\n+\t\t\tbreak;\n+\t\tcase opt_chmod:\n+\t\t\tif (i+1 >= argc)\n+\t\t\t\tdie(\"git-update-index: --chmod=%s <path>\",\n+\t\t\t\t\t\t\t\tea->argv[0]);\n+\t\t\tset_executable_bit = ea->argv[0][0];\n+\t\t\tbreak;\n+\t\tcase opt_assume_unchanged:\n+\t\t\tmark_valid_only = MARK_VALID;\n+\t\t\tbreak;\n+\t\tcase opt_no_assume_unchanged:\n+\t\t\tmark_valid_only = UNMARK_VALID;\n+\t\t\tbreak;\n+\t\tcase opt_info_only:\n+\t\t\tinfo_only = 1;\n+\t\t\tbreak;\n+\t\tcase opt_force_remove:\n+\t\t\tforce_remove = 1;\n+\t\t\tbreak;\n+\t\tcase opt_z:\n+\t\t\tline_termination = 0;\n+\t\t\tbreak;\n+\t\tcase opt_stdin:\n+\t\t\tif (gi.pos != argc - 1)\n+\t\t\t\tdie(\"--stdin must be at the end\");\n+\t\t\tread_from_stdin = 1;\n+\t\t\tbreak;\n+\t\tcase opt_index_info:\n+\t\t\tif (gi.pos != argc - 1)\n+\t\t\t\tdie(\"--index-info must be at the end\");\n+\t\t\tallow_add = allow_replace = allow_remove = 1;\n+\t\t\tread_index_info(line_termination);\n+\t\t\tbreak;\n+\t\tcase opt_unresolve:\n+\t\t\thas_errors = do_unresolve(argc - gi.pos, argv + gi.pos,\n+\t\t\t\t\t\t  prefix, prefix_length);\n+\t\t\tif (has_errors)\n+\t\t\t\tactive_cache_changed = 0;\n+\t\t\tgoto finish;\n+\t\tcase opt_again:\n+\t\t\thas_errors = do_reupdate(argc - gi.pos, argv + gi.pos,\n+\t\t\t\t\t\t prefix, prefix_length);\n+\t\t\tif (has_errors)\n+\t\t\t\tactive_cache_changed = 0;\n+\t\t\tgoto finish;\n+\t\tcase opt_ignore_missing:\n+\t\t\tnot_new = 1;\n+\t\t\tbreak;\n+\t\tcase opt_verbose:\n+\t\t\tverbose = 1;\n+\t\t\tbreak;\n+\t\tcase opt_h:\n+\t\t\tusage(update_index_usage);\n+\t\t\tbreak;\n+\t\tcase GITOPT_DD:\n+\t\t\tallow_options = 0;\n+\t\t\tbreak;\n+\t\tcase GITOPT_NON_OPTION:\n+\t\t\tupdate_one(argv[gi.pos], prefix, prefix_length);\n+\t\t\tif (set_executable_bit)\n+\t\t\t\tchmod_path(set_executable_bit, argv[gi.pos]);\n+\t\t\tbreak;\n+\t\tcase GITOPT_ERROR:\n+\t\t\tdie(\"unknown option %s\", argv[gi.pos]);\n+\t\t\tbreak;\n \t\t}\n-\t\tupdate_one(path, prefix, prefix_length);\n-\t\tif (set_executable_bit)\n-\t\t\tchmod_path(set_executable_bit, path);\n \t}\n \tif (read_from_stdin) {\n \t\tstruct strbuf buf;\n-- \n1.3.2.g102e322\n"},{"id":"19914","messageId":"11476199622704-git-send-email-normalperson@yhbt.net","threadId":"4135","inReplyTo":"11476199622462-git-send-email-normalperson@yhbt.net","subject":"[PATCH 3/5] gitopt: convert setup_revisions() and friends","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-14T15:19:20Z","receivedAt":"2006-05-14T15:19:20Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"\tdiff_opt_parse => diff_opt_handle\n\nI've added --raw to diff_opt_handle() for consistency's sake\n\nLightly tested, some bugs in the original series of submitted\npatches were fixed wrt argument parsing for -C,-M,-B).\n\nPasses all tests so that's a good sign.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n builtin-diff.c      |  152 +++++++-------\n builtin-log.c       |    7 -\n commit.c            |   14 +\n diff-files.c        |   25 +-\n diff-index.c        |   18 +-\n diff-stages.c       |   56 +++--\n diff-tree.c         |   15 +\n diff.c              |  183 ++++++++++++-----\n diff.h              |    7 -\n gitopt/diff-files.h |   45 ++++\n gitopt/diff-index.h |   22 ++\n gitopt/diff-tree.h  |   22 ++\n gitopt/diff.h       |   19 ++\n http-push.c         |    2 \n rev-list.c          |   51 +++--\n revision.c          |  542 +++++++++++++++++++++++++++------------------------\n revision.h          |    4 \n 17 files changed, 704 insertions(+), 480 deletions(-)\n create mode 100644 gitopt/diff-files.h\n create mode 100644 gitopt/diff-index.h\n create mode 100644 gitopt/diff-tree.h\n create mode 100644 gitopt/diff.h\n\n946397f743f363198d154efaa74d5cc5bd9d6aae\ndiff --git a/builtin-diff.c b/builtin-diff.c\nindex d3ac581..3aefd3c 100644\n--- a/builtin-diff.c\n+++ b/builtin-diff.c\n@@ -12,6 +12,9 @@ #include \"diffcore.h\"\n #include \"revision.h\"\n #include \"log-tree.h\"\n #include \"builtin.h\"\n+#include \"gitopt/diff-index.h\"\n+#include \"gitopt/diff-files.h\"\n+#include \"gitopt/diff-tree.h\"\n \n /* NEEDSWORK: struct object has place for name but we _do_\n  * know mode when we extracted the blob out of a tree, which\n@@ -25,26 +28,25 @@ struct blobinfo {\n static const char builtin_diff_usage[] =\n \"diff <options> <rev>{0,2} -- <path>*\";\n \n+static int extra_diff_flags[LAST_DIFF_EXTRA_ID] = { 0 };\n+\n static int builtin_diff_files(struct rev_info *revs,\n \t\t\t      int argc, const char **argv)\n {\n-\tint silent = 0;\n-\twhile (1 < argc) {\n-\t\tconst char *arg = argv[1];\n-\t\tif (!strcmp(arg, \"--base\"))\n-\t\t\trevs->max_count = 1;\n-\t\telse if (!strcmp(arg, \"--ours\"))\n-\t\t\trevs->max_count = 2;\n-\t\telse if (!strcmp(arg, \"--theirs\"))\n-\t\t\trevs->max_count = 3;\n-\t\telse if (!strcmp(arg, \"-q\"))\n-\t\t\tsilent = 1;\n-\t\telse if (!strcmp(arg, \"--raw\"))\n-\t\t\trevs->diffopt.output_format = DIFF_FORMAT_RAW;\n-\t\telse\n+\tint i;\n+\tstruct df_extra_opts dfeo = { revs, 0 };\n+\n+\tif (argc)\n+\t\tusage(builtin_diff_usage);\n+\n+\tfor (i = 1; i < ARRAY_SIZE(extra_diff_flags); i++) {\n+\t\tif (extra_diff_flags[i]) {\n+\t\t\tif (diff_files_opt_handler(NULL, i, &dfeo) > 0)\n+\t\t\t\tcontinue;\n \t\t\tusage(builtin_diff_usage);\n-\t\targv++; argc--;\n+\t\t}\n \t}\n+\n \t/*\n \t * Make sure there are NO revision (i.e. pending object) parameter,\n \t * specified rev.max_count is reasonable (0 <= n <= 3), and\n@@ -65,7 +67,7 @@ static int builtin_diff_files(struct rev\n \t */\n \tif (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)\n \t\trevs->diffopt.output_format = DIFF_FORMAT_RAW;\n-\treturn run_diff_files(revs, silent);\n+\treturn run_diff_files(revs, dfeo.silent);\n }\n \n static void stuff_change(struct diff_options *opt,\n@@ -107,14 +109,9 @@ static int builtin_diff_b_f(struct rev_i\n \t/* Blob vs file in the working tree*/\n \tstruct stat st;\n \n-\twhile (1 < argc) {\n-\t\tconst char *arg = argv[1];\n-\t\tif (!strcmp(arg, \"--raw\"))\n-\t\t\trevs->diffopt.output_format = DIFF_FORMAT_RAW;\n-\t\telse\n-\t\t\tusage(builtin_diff_usage);\n-\t\targv++; argc--;\n-\t}\n+\tif (argc)\n+\t\tusage(builtin_diff_usage);\n+\n \tif (lstat(path, &st))\n \t\tdie(\"'%s': %s\", path, strerror(errno));\n \tif (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))\n@@ -135,14 +132,9 @@ static int builtin_diff_blobs(struct rev\n \t/* Blobs */\n \tunsigned mode = canon_mode(S_IFREG | 0644);\n \n-\twhile (1 < argc) {\n-\t\tconst char *arg = argv[1];\n-\t\tif (!strcmp(arg, \"--raw\"))\n-\t\t\trevs->diffopt.output_format = DIFF_FORMAT_RAW;\n-\t\telse\n-\t\t\tusage(builtin_diff_usage);\n-\t\targv++; argc--;\n-\t}\n+\tif (argc)\n+\t\tusage(builtin_diff_usage);\n+\n \tstuff_change(&revs->diffopt,\n \t\t     mode, mode,\n \t\t     blob[0].sha1, blob[1].sha1,\n@@ -155,17 +147,19 @@ static int builtin_diff_blobs(struct rev\n static int builtin_diff_index(struct rev_info *revs,\n \t\t\t      int argc, const char **argv)\n {\n-\tint cached = 0;\n-\twhile (1 < argc) {\n-\t\tconst char *arg = argv[1];\n-\t\tif (!strcmp(arg, \"--cached\"))\n-\t\t\tcached = 1;\n-\t\telse if (!strcmp(arg, \"--raw\"))\n-\t\t\trevs->diffopt.output_format = DIFF_FORMAT_RAW;\n-\t\telse\n+\tint i, cached = 0;\n+\n+\tif (argc)\n+\t\tusage(builtin_diff_usage);\n+\n+\tfor (i = 1; i < ARRAY_SIZE(extra_diff_flags); i++) {\n+\t\tif (extra_diff_flags[i]) {\n+\t\t\tif (diff_index_opt_handler(NULL, i, &cached) > 0)\n+\t\t\t\tcontinue;\n \t\t\tusage(builtin_diff_usage);\n-\t\targv++; argc--;\n+\t\t}\n \t}\n+\n \t/*\n \t * Make sure there is one revision (i.e. pending object),\n \t * and there is no revision filtering parameters.\n@@ -183,14 +177,9 @@ static int builtin_diff_tree(struct rev_\n {\n \tconst unsigned char *(sha1[2]);\n \tint swap = 1;\n-\twhile (1 < argc) {\n-\t\tconst char *arg = argv[1];\n-\t\tif (!strcmp(arg, \"--raw\"))\n-\t\t\trevs->diffopt.output_format = DIFF_FORMAT_RAW;\n-\t\telse\n-\t\t\tusage(builtin_diff_usage);\n-\t\targv++; argc--;\n-\t}\n+\n+\tif (argc)\n+\t\tusage(builtin_diff_usage);\n \n \t/* We saw two trees, ent[0] and ent[1].\n \t * unless ent[0] is unintesting, they are swapped\n@@ -212,14 +201,9 @@ static int builtin_diff_combined(struct \n \tconst unsigned char (*parent)[20];\n \tint i;\n \n-\twhile (1 < argc) {\n-\t\tconst char *arg = argv[1];\n-\t\tif (!strcmp(arg, \"--raw\"))\n-\t\t\trevs->diffopt.output_format = DIFF_FORMAT_RAW;\n-\t\telse\n-\t\t\tusage(builtin_diff_usage);\n-\t\targv++; argc--;\n-\t}\n+\tif (argc)\n+\t\tusage(builtin_diff_usage);\n+\n \tif (!revs->dense_combined_merges && !revs->combine_merges)\n \t\trevs->dense_combined_merges = revs->combine_merges = 1;\n \tparent = xmalloc(ents * sizeof(*parent));\n@@ -243,6 +227,27 @@ static void add_head(struct rev_info *re\n \tadd_object(obj, &revs->pending_objects, NULL, \"HEAD\");\n }\n \n+static struct opt_spec * diff_combined_ost()\n+{\n+\tstruct opt_spec *rv, *tmp;\n+\n+\ttmp = combine_opt_spec(diff_files_ost, diff_tree_ost);\n+\trv = combine_opt_spec(tmp, diff_index_ost);\n+\tfree(tmp);\n+\treturn rv;\n+}\n+\n+/* just set flags in here for use by the builtin_diff_* functions  */\n+static int diff_combined_handler(struct gitopt_iterator *gi,\n+\t\t\t\tconst int id, void *args)\n+{\n+\tif (id > 0 && id < LAST_DIFF_EXTRA_ID) {\n+\t\textra_diff_flags[id] = 1;\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n int cmd_diff(int argc, const char **argv, char **envp)\n {\n \tstruct rev_info rev;\n@@ -250,6 +255,9 @@ int cmd_diff(int argc, const char **argv\n \tint ents = 0, blobs = 0, paths = 0;\n \tconst char *path = NULL;\n \tstruct blobinfo blob[2];\n+\tstruct exec_args *b;\n+\tstruct opt_spec *ost = diff_combined_ost();\n+\tstruct gitopt_extra ge = { ost, diff_combined_handler, NULL };\n \n \t/*\n \t * We could get N tree-ish in the rev.pending_objects list.\n@@ -275,22 +283,14 @@ int cmd_diff(int argc, const char **argv\n \tinit_revisions(&rev);\n \trev.diffopt.output_format = DIFF_FORMAT_PATCH;\n \n-\targc = setup_revisions(argc, argv, &rev, NULL);\n+\tb = setup_revisions(argc, argv, &rev, NULL, &ge);\n+\tfree(ost);\n+\n \t/* Do we have --cached and not have a pending object, then\n \t * default to HEAD by hand.  Eek.\n \t */\n-\tif (!rev.pending_objects) {\n-\t\tint i;\n-\t\tfor (i = 1; i < argc; i++) {\n-\t\t\tconst char *arg = argv[i];\n-\t\t\tif (!strcmp(arg, \"--\"))\n-\t\t\t\tbreak;\n-\t\t\telse if (!strcmp(arg, \"--cached\")) {\n-\t\t\t\tadd_head(&rev);\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t}\n-\t}\n+\tif (!rev.pending_objects && extra_diff_flags[opt_cached])\n+\t\tadd_head(&rev);\n \n \tfor (list = rev.pending_objects; list; list = list->next) {\n \t\tstruct object *obj = list->item;\n@@ -340,17 +340,17 @@ int cmd_diff(int argc, const char **argv\n \tif (!ents) {\n \t\tswitch (blobs) {\n \t\tcase 0:\n-\t\t\treturn builtin_diff_files(&rev, argc, argv);\n+\t\t\treturn builtin_diff_files(&rev, b->argc, b->argv);\n \t\t\tbreak;\n \t\tcase 1:\n \t\t\tif (paths != 1)\n \t\t\t\tusage(builtin_diff_usage);\n-\t\t\treturn builtin_diff_b_f(&rev, argc, argv, blob, path);\n+\t\t\treturn builtin_diff_b_f(&rev, b->argc, b->argv, blob, path);\n \t\t\tbreak;\n \t\tcase 2:\n \t\t\tif (paths)\n \t\t\t\tusage(builtin_diff_usage);\n-\t\t\treturn builtin_diff_blobs(&rev, argc, argv, blob);\n+\t\t\treturn builtin_diff_blobs(&rev, b->argc, b->argv, blob);\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tusage(builtin_diff_usage);\n@@ -359,10 +359,10 @@ int cmd_diff(int argc, const char **argv\n \telse if (blobs)\n \t\tusage(builtin_diff_usage);\n \telse if (ents == 1)\n-\t\treturn builtin_diff_index(&rev, argc, argv);\n+\t\treturn builtin_diff_index(&rev, b->argc, b->argv);\n \telse if (ents == 2)\n-\t\treturn builtin_diff_tree(&rev, argc, argv, ent);\n+\t\treturn builtin_diff_tree(&rev, b->argc, b->argv, ent);\n \telse\n-\t\treturn builtin_diff_combined(&rev, argc, argv, ent, ents);\n+\t\treturn builtin_diff_combined(&rev, b->argc, b->argv, ent, ents);\n \tusage(builtin_diff_usage);\n }\ndiff --git a/builtin-log.c b/builtin-log.c\nindex 69f2911..3cfc8ac 100644\n--- a/builtin-log.c\n+++ b/builtin-log.c\n@@ -14,14 +14,15 @@ static int cmd_log_wc(int argc, const ch\n \t\t      struct rev_info *rev)\n {\n \tstruct commit *commit;\n+\tstruct exec_args *b;\n \n \trev->abbrev = DEFAULT_ABBREV;\n \trev->commit_format = CMIT_FMT_DEFAULT;\n \trev->verbose_header = 1;\n-\targc = setup_revisions(argc, argv, rev, \"HEAD\");\n+\tb = setup_revisions(argc, argv, rev, \"HEAD\", NULL);\n \n-\tif (argc > 1)\n-\t\tdie(\"unrecognized argument: %s\", argv[1]);\n+\tif (b->argc > 1)\n+\t\tdie(\"unrecognized argument: %s\", b->argv[0]);\n \n \tprepare_revision_walk(rev);\n \tsetup_pager();\ndiff --git a/commit.c b/commit.c\nindex 2717dd8..2343729 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -24,19 +24,19 @@ const char *commit_type = \"commit\";\n \n enum cmit_fmt get_commit_format(const char *arg)\n {\n-\tif (!*arg)\n+\tif (!arg)\n \t\treturn CMIT_FMT_DEFAULT;\n-\tif (!strcmp(arg, \"=raw\"))\n+\tif (!strcmp(arg, \"raw\"))\n \t\treturn CMIT_FMT_RAW;\n-\tif (!strcmp(arg, \"=medium\"))\n+\tif (!strcmp(arg, \"medium\"))\n \t\treturn CMIT_FMT_MEDIUM;\n-\tif (!strcmp(arg, \"=short\"))\n+\tif (!strcmp(arg, \"short\"))\n \t\treturn CMIT_FMT_SHORT;\n-\tif (!strcmp(arg, \"=full\"))\n+\tif (!strcmp(arg, \"full\"))\n \t\treturn CMIT_FMT_FULL;\n-\tif (!strcmp(arg, \"=fuller\"))\n+\tif (!strcmp(arg, \"fuller\"))\n \t\treturn CMIT_FMT_FULLER;\n-\tif (!strcmp(arg, \"=oneline\"))\n+\tif (!strcmp(arg, \"oneline\"))\n \t\treturn CMIT_FMT_ONELINE;\n \tdie(\"invalid --pretty format\");\n }\ndiff --git a/diff-files.c b/diff-files.c\nindex b9d193d..431487b 100644\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -7,6 +7,7 @@ #include \"cache.h\"\n #include \"diff.h\"\n #include \"commit.h\"\n #include \"revision.h\"\n+#include \"gitopt/diff-files.h\"\n \n static const char diff_files_usage[] =\n \"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]\"\n@@ -15,26 +16,18 @@ COMMON_DIFF_OPTIONS_HELP;\n int main(int argc, const char **argv)\n {\n \tstruct rev_info rev;\n-\tint silent = 0;\n+\tstruct exec_args *b;\n+\tstruct df_extra_opts dfeo = { &rev, 0 };\n+\tstruct gitopt_extra ge = { diff_files_ost, diff_files_opt_handler,\n+\t\t\t\t   &dfeo };\n \n \tgit_config(git_diff_config);\n \tinit_revisions(&rev);\n \trev.abbrev = 0;\n \n-\targc = setup_revisions(argc, argv, &rev, NULL);\n-\twhile (1 < argc && argv[1][0] == '-') {\n-\t\tif (!strcmp(argv[1], \"--base\"))\n-\t\t\trev.max_count = 1;\n-\t\telse if (!strcmp(argv[1], \"--ours\"))\n-\t\t\trev.max_count = 2;\n-\t\telse if (!strcmp(argv[1], \"--theirs\"))\n-\t\t\trev.max_count = 3;\n-\t\telse if (!strcmp(argv[1], \"-q\"))\n-\t\t\tsilent = 1;\n-\t\telse\n-\t\t\tusage(diff_files_usage);\n-\t\targv++; argc--;\n-\t}\n+\tb = setup_revisions(argc, argv, &rev, NULL, &ge);\n+\tif (b->argc)\n+\t\tusage(diff_files_usage);\n \t/*\n \t * Make sure there are NO revision (i.e. pending object) parameter,\n \t * rev.max_count is reasonable (0 <= n <= 3),\n@@ -50,5 +43,5 @@ int main(int argc, const char **argv)\n \t */\n \tif (rev.diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\treturn run_diff_files(&rev, silent);\n+\treturn run_diff_files(&rev, dfeo.silent);\n }\ndiff --git a/diff-index.c b/diff-index.c\nindex 8c9f601..987a00a 100644\n--- a/diff-index.c\n+++ b/diff-index.c\n@@ -2,6 +2,7 @@ #include \"cache.h\"\n #include \"diff.h\"\n #include \"commit.h\"\n #include \"revision.h\"\n+#include \"gitopt/diff-index.h\"\n \n static const char diff_cache_usage[] =\n \"git-diff-index [-m] [--cached] \"\n@@ -12,21 +13,18 @@ int main(int argc, const char **argv)\n {\n \tstruct rev_info rev;\n \tint cached = 0;\n-\tint i;\n+\tstruct exec_args *b;\n+\tstruct gitopt_extra ge = { diff_index_ost,\n+\t\t\t\tdiff_index_opt_handler, &cached };\n \n \tgit_config(git_diff_config);\n \tinit_revisions(&rev);\n \trev.abbrev = 0;\n \n-\targc = setup_revisions(argc, argv, &rev, NULL);\n-\tfor (i = 1; i < argc; i++) {\n-\t\tconst char *arg = argv[i];\n-\t\t\t\n-\t\tif (!strcmp(arg, \"--cached\"))\n-\t\t\tcached = 1;\n-\t\telse\n-\t\t\tusage(diff_cache_usage);\n-\t}\n+\tb = setup_revisions(argc, argv, &rev, NULL, &ge);\n+\tif (b->argc)\n+\t\tusage(diff_cache_usage);\n+\n \t/*\n \t * Make sure there is one revision (i.e. pending object),\n \t * and there is no revision filtering parameters.\ndiff --git a/diff-stages.c b/diff-stages.c\nindex dcd20e7..05129c5 100644\n--- a/diff-stages.c\n+++ b/diff-stages.c\n@@ -4,6 +4,13 @@\n \n #include \"cache.h\"\n #include \"diff.h\"\n+#include \"gitopt.h\"\n+\n+enum diff_stages_ost { opt_r = 1 };\n+static const struct opt_spec diff_stages_ost[] = {\n+\t{ 0,\t'r',\t0,\t0,\topt_r },\n+\t{ 0, 0 }\n+};\n \n static struct diff_options diff_options;\n \n@@ -59,40 +66,47 @@ int main(int ac, const char **av)\n \tint stage1, stage2;\n \tconst char *prefix = setup_git_directory();\n \tconst char **pathspec = NULL;\n+\tstruct exec_args *b;\n+\tstruct opt_spec *ost;\n+\tstruct gitopt_iterator gi;\n+\tint i;\n \n \tgit_config(git_diff_config);\n \tread_cache();\n \tdiff_setup(&diff_options);\n-\twhile (1 < ac && av[1][0] == '-') {\n-\t\tconst char *arg = av[1];\n-\t\tif (!strcmp(arg, \"-r\"))\n-\t\t\t; /* as usual */\n-\t\telse {\n-\t\t\tint diff_opt_cnt;\n-\t\t\tdiff_opt_cnt = diff_opt_parse(&diff_options,\n-\t\t\t\t\t\t      av+1, ac-1);\n-\t\t\tif (diff_opt_cnt < 0)\n-\t\t\t\tusage(diff_stages_usage);\n-\t\t\telse if (diff_opt_cnt) {\n-\t\t\t\tav += diff_opt_cnt;\n-\t\t\t\tac -= diff_opt_cnt;\n-\t\t\t\tcontinue;\n-\t\t\t}\n+\n+\tost = combine_opt_spec(diff_ost, diff_stages_ost);\n+\tgitopt_iter_setup(&gi, ac, av);\n+\n+\tfor (b=gi.b; (i=gitopt_iter_parse(&gi, ost)); gitopt_iter_next(&gi)) {\n+\t\tswitch (i) {\n+\t\tcase GITOPT_NON_OPTION:\n+\t\t\tb->argv[b->argc++] = gi.argv[gi.pos];\n+\t\tcase GITOPT_DD:\n+\t\tcase opt_r: /* as usual */\n+\t\t\tbreak;\n+\t\tcase GITOPT_ERROR:\n+\t\t\tusage(diff_stages_usage);\n+\t\tdefault:\n+\t\t\ti = diff_opt_handler(&gi, i, &diff_options);\n+\t\t\tif (i > 0)\n+\t\t\t\tgi.pos += i - 1;\n \t\t\telse\n \t\t\t\tusage(diff_stages_usage);\n \t\t}\n-\t\tac--; av++;\n \t}\n+\tgitopt_iter_done(&gi);\n+\tfree(ost);\n \n-\tif (ac < 3 ||\n-\t    sscanf(av[1], \"%d\", &stage1) != 1 ||\n+\tif (b->argc < 2 ||\n+\t    sscanf(b->argv[0], \"%d\", &stage1) != 1 ||\n \t    ! (0 <= stage1 && stage1 <= 3) ||\n-\t    sscanf(av[2], \"%d\", &stage2) != 1 ||\n+\t    sscanf(b->argv[1], \"%d\", &stage2) != 1 ||\n \t    ! (0 <= stage2 && stage2 <= 3))\n \t\tusage(diff_stages_usage);\n \n-\tav += 3; /* The rest from av[0] are for paths restriction. */\n-\tpathspec = get_pathspec(prefix, av);\n+\tb->argv += 2; /* The rest from b->argv[0] are for paths restriction. */\n+\tpathspec = get_pathspec(prefix, b->argv);\n \n \tif (diff_setup_done(&diff_options) < 0)\n \t\tusage(diff_stages_usage);\ndiff --git a/diff-tree.c b/diff-tree.c\nindex 7207867..b5c6072 100644\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -2,6 +2,7 @@ #include \"cache.h\"\n #include \"diff.h\"\n #include \"commit.h\"\n #include \"log-tree.h\"\n+#include \"gitopt/diff-tree.h\"\n \n static struct rev_info log_tree_opt;\n \n@@ -66,23 +67,19 @@ int main(int argc, const char **argv)\n \tstatic struct rev_info *opt = &log_tree_opt;\n \tstruct object_list *list;\n \tint read_stdin = 0;\n+\tstruct exec_args *b;\n+\tstruct gitopt_extra ge = { diff_tree_ost, diff_tree_opt_handler,\n+\t\t\t\t&read_stdin };\n \n \tgit_config(git_diff_config);\n \tnr_sha1 = 0;\n \tinit_revisions(opt);\n \topt->abbrev = 0;\n \topt->diff = 1;\n-\targc = setup_revisions(argc, argv, opt, NULL);\n+\tb = setup_revisions(argc, argv, opt, NULL, &ge);\n \n-\twhile (--argc > 0) {\n-\t\tconst char *arg = *++argv;\n-\n-\t\tif (!strcmp(arg, \"--stdin\")) {\n-\t\t\tread_stdin = 1;\n-\t\t\tcontinue;\n-\t\t}\n+\tif (b->argc)\n \t\tusage(diff_tree_usage);\n-\t}\n \n \t/*\n \t * NOTE! \"setup_revisions()\" will have inserted the revisions\ndiff --git a/diff.c b/diff.c\nindex 7a7b839..7d88dc5 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -10,6 +10,48 @@ #include \"diff.h\"\n #include \"diffcore.h\"\n #include \"delta.h\"\n #include \"xdiff-interface.h\"\n+#include \"gitopt.h\"\n+\n+static int diff_scoreopt_parse(const int id, const char *opt);\n+\n+enum diff_ost_ids {\n+\topt_p = GITOPT_DIFF_BASE, opt_raw, opt_patch_with_raw,\n+\topt_stat, opt_patch_with_stat,\n+\topt_z, opt_l, opt_full_index, opt_name_only, opt_name_status,\n+\topt_R, opt_S, opt_s, opt_O, opt_diff_filter,\n+\topt_pickaxe_all, opt_pickaxe_regex,\n+\topt_B, opt_M, opt_C, opt_find_copies_harder, opt_abbrev,\n+\topt_binary\n+};\n+\n+const struct opt_spec diff_ost[] = {\n+\t{ 0,\t\t\t'p',\t0,\t0,\topt_p },\n+\t{ \"unified\",\t\t'u',\t0,\t0,\topt_p },\n+\t{ \"raw\",\t\t0,\t0,\t0,\topt_raw },\n+\t{ \"patch-with-raw\",\t0,\t0,\t0,\topt_patch_with_raw },\n+\t{ \"stat\",\t\t0,\t0,\t0,\topt_stat },\n+\t{ \"patch-with-stat\",\t0,\t0,\t0,\topt_patch_with_stat },\n+\t{ 0,\t\t\t'z',\t0,\t0,\topt_z },\n+\t{ 0,\t\t\t'l',\t0,\tARG_INT,\topt_l },\n+\t{ \"full-index\",\t\t0,\t0,\t0,\topt_full_index },\n+\t{ \"name-only\",\t\t0,\t0,\t0,\topt_name_only },\n+\t{ \"name-status\",\t0,\t0,\t0,\topt_name_status },\n+\t{ 0,\t\t\t'R',\t0,\t0,\topt_R },\n+\t{ 0,\t\t\t'S',\t0,\tARG_ONE,\topt_S },\n+\t{ 0,\t\t\t's',\t0,\t0,\topt_s },\n+\t{ 0,\t\t\t'O',\t0,\tARG_ONE, opt_O },\n+\t{ \"diff-filter\",\t0,\t0,\tARG_ONE, opt_diff_filter },\n+\t{ \"pickaxe-all\",\t0,\t0,\t0,\topt_pickaxe_all },\n+\t{ \"pickaxe-regex\",\t0,\t0,\t0,\topt_pickaxe_regex },\n+\t{ 0,\t\t\t'B',\t0,\tARG_OPT,\topt_B },\n+\t{ 0,\t\t\t'M',\t0,\tARG_OPT,\topt_M },\n+\t{ 0,\t\t\t'C',\t0,\tARG_OPT,\topt_C },\n+\t{ \"find-copies-harder\",\t0,\t0,\t0,\topt_find_copies_harder},\n+\t{ \"abbrev\",\t\t0,\t0,\tARG_OPTINT, opt_abbrev },\n+\t{ \"binary\",\t\t0,\t0,\t0,\topt_binary },\n+\t{ 0, 0 }\n+};\n+\n \n static int use_size_cache;\n \n@@ -1222,79 +1264,102 @@ int diff_setup_done(struct diff_options \n \treturn 0;\n }\n \n-int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n+int diff_opt_handler(struct gitopt_iterator *gi, const int id, void *args)\n {\n-\tconst char *arg = av[0];\n-\tif (!strcmp(arg, \"-p\") || !strcmp(arg, \"-u\"))\n+\tstruct diff_options *options = (struct diff_options *)args;\n+\n+\tswitch (id) {\n+\tcase opt_p:\n \t\toptions->output_format = DIFF_FORMAT_PATCH;\n-\telse if (!strcmp(arg, \"--patch-with-raw\")) {\n+\t\tbreak;\n+\tcase opt_raw:\n+\t\toptions->output_format = DIFF_FORMAT_RAW;\n+\t\tbreak;\n+\tcase opt_patch_with_raw:\n \t\toptions->output_format = DIFF_FORMAT_PATCH;\n \t\toptions->with_raw = 1;\n-\t}\n-\telse if (!strcmp(arg, \"--stat\"))\n+\t\tbreak;\n+\tcase opt_stat:\n \t\toptions->output_format = DIFF_FORMAT_DIFFSTAT;\n-\telse if (!strcmp(arg, \"--patch-with-stat\")) {\n+\t\tbreak;\n+\tcase opt_patch_with_stat:\n \t\toptions->output_format = DIFF_FORMAT_PATCH;\n \t\toptions->with_stat = 1;\n-\t}\n-\telse if (!strcmp(arg, \"-z\"))\n+\t\tbreak;\n+\tcase opt_z:\n \t\toptions->line_termination = 0;\n-\telse if (!strncmp(arg, \"-l\", 2))\n-\t\toptions->rename_limit = strtoul(arg+2, NULL, 10);\n-\telse if (!strcmp(arg, \"--full-index\"))\n+\t\tbreak;\n+\tcase opt_l:\n+\t\toptions->rename_limit = strtoul(gi->ea->argv[0], NULL, 10);\n+\t\tbreak;\n+\tcase opt_full_index:\n \t\toptions->full_index = 1;\n-\telse if (!strcmp(arg, \"--binary\")) {\n+\t\tbreak;\n+\tcase opt_binary:\n \t\toptions->output_format = DIFF_FORMAT_PATCH;\n \t\toptions->full_index = options->binary = 1;\n-\t}\n-\telse if (!strcmp(arg, \"--name-only\"))\n+\t\tbreak;\n+\tcase opt_name_only:\n \t\toptions->output_format = DIFF_FORMAT_NAME;\n-\telse if (!strcmp(arg, \"--name-status\"))\n+\t\tbreak;\n+\tcase opt_name_status:\n \t\toptions->output_format = DIFF_FORMAT_NAME_STATUS;\n-\telse if (!strcmp(arg, \"-R\"))\n+\t\tbreak;\n+\tcase opt_R:\n \t\toptions->reverse_diff = 1;\n-\telse if (!strncmp(arg, \"-S\", 2))\n-\t\toptions->pickaxe = arg + 2;\n-\telse if (!strcmp(arg, \"-s\"))\n+\t\tbreak;\n+\tcase opt_S:\n+\t\toptions->pickaxe = gi->ea->argv[0];\n+\t\tbreak;\n+\tcase opt_s:\n \t\toptions->output_format = DIFF_FORMAT_NO_OUTPUT;\n-\telse if (!strncmp(arg, \"-O\", 2))\n-\t\toptions->orderfile = arg + 2;\n-\telse if (!strncmp(arg, \"--diff-filter=\", 14))\n-\t\toptions->filter = arg + 14;\n-\telse if (!strcmp(arg, \"--pickaxe-all\"))\n+\t\tbreak;\n+\tcase opt_O:\n+\t\toptions->orderfile = gi->ea->argv[0];\n+\t\tbreak;\n+\tcase opt_diff_filter:\n+\t\toptions->filter = gi->ea->argv[0];\n+\t\tbreak;\n+\tcase opt_pickaxe_all:\n \t\toptions->pickaxe_opts = DIFF_PICKAXE_ALL;\n-\telse if (!strcmp(arg, \"--pickaxe-regex\"))\n+\t\tbreak;\n+\tcase opt_pickaxe_regex:\n \t\toptions->pickaxe_opts = DIFF_PICKAXE_REGEX;\n-\telse if (!strncmp(arg, \"-B\", 2)) {\n-\t\tif ((options->break_opt =\n-\t\t     diff_scoreopt_parse(arg)) == -1)\n+\t\tbreak;\n+\tcase opt_B:\n+\t\tif (gi->ea->argc && (options->break_opt =\n+\t\t     diff_scoreopt_parse(id, gi->ea->argv[1])) == -1)\n \t\t\treturn -1;\n-\t}\n-\telse if (!strncmp(arg, \"-M\", 2)) {\n-\t\tif ((options->rename_score =\n-\t\t     diff_scoreopt_parse(arg)) == -1)\n+\t\tbreak;\n+\tcase opt_M:\n+\t\tif (gi->ea->argc && (options->rename_score =\n+\t\t     diff_scoreopt_parse(id, gi->ea->argv[1])) == -1)\n \t\t\treturn -1;\n \t\toptions->detect_rename = DIFF_DETECT_RENAME;\n-\t}\n-\telse if (!strncmp(arg, \"-C\", 2)) {\n-\t\tif ((options->rename_score =\n-\t\t     diff_scoreopt_parse(arg)) == -1)\n+\t\tbreak;\n+\tcase opt_C:\n+\t\tif (gi->ea->argc && (options->rename_score =\n+\t\t     diff_scoreopt_parse(id, gi->ea->argv[1])) == -1)\n \t\t\treturn -1;\n \t\toptions->detect_rename = DIFF_DETECT_COPY;\n-\t}\n-\telse if (!strcmp(arg, \"--find-copies-harder\"))\n+\t\tbreak;\n+\tcase opt_find_copies_harder:\n \t\toptions->find_copies_harder = 1;\n-\telse if (!strcmp(arg, \"--abbrev\"))\n-\t\toptions->abbrev = DEFAULT_ABBREV;\n-\telse if (!strncmp(arg, \"--abbrev=\", 9)) {\n-\t\toptions->abbrev = strtoul(arg + 9, NULL, 10);\n-\t\tif (options->abbrev < MINIMUM_ABBREV)\n-\t\t\toptions->abbrev = MINIMUM_ABBREV;\n-\t\telse if (40 < options->abbrev)\n-\t\t\toptions->abbrev = 40;\n-\t}\n-\telse\n+\t\tbreak;\n+\tcase opt_abbrev:\n+\t\tif (gi->ea->argc == 1)\n+\t\t\toptions->abbrev = DEFAULT_ABBREV;\n+\t\telse {\n+\t\t\toptions->abbrev = strtoul(gi->ea->argv[1], NULL, 10);\n+\t\t\tif (options->abbrev < MINIMUM_ABBREV)\n+\t\t\t\toptions->abbrev = MINIMUM_ABBREV;\n+\t\t\telse if (40 < options->abbrev)\n+\t\t\t\toptions->abbrev = 40;\n+\t\t}\n+\t\tbreak;\n+\tdefault:\n \t\treturn 0;\n+\t}\n \treturn 1;\n }\n \n@@ -1304,9 +1369,13 @@ static int parse_num(const char **cp_p)\n \tint ch, dot;\n \tconst char *cp = *cp_p;\n \n+\tif (!cp)\n+\t\treturn 0;\n+\n \tnum = 0;\n \tscale = 1;\n \tdot = 0;\n+\n \tfor(;;) {\n \t\tch = *cp;\n \t\tif ( !dot && ch == '.' ) {\n@@ -1334,21 +1403,15 @@ static int parse_num(const char **cp_p)\n \treturn (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale);\n }\n \n-int diff_scoreopt_parse(const char *opt)\n+static int diff_scoreopt_parse(const int id, const char *opt)\n {\n-\tint opt1, opt2, cmd;\n-\n-\tif (*opt++ != '-')\n-\t\treturn -1;\n-\tcmd = *opt++;\n-\tif (cmd != 'M' && cmd != 'C' && cmd != 'B')\n-\t\treturn -1; /* that is not a -M, -C nor -B option */\n+\tint opt1, opt2;\n \n \topt1 = parse_num(&opt);\n-\tif (cmd != 'B')\n+\tif (id != opt_B)\n \t\topt2 = 0;\n \telse {\n-\t\tif (*opt == 0)\n+\t\tif (!opt || *opt == 0)\n \t\t\topt2 = 0;\n \t\telse if (*opt != '/')\n \t\t\treturn -1; /* we expect -B80/99 or -B80 */\n@@ -1357,7 +1420,7 @@ int diff_scoreopt_parse(const char *opt)\n \t\t\topt2 = parse_num(&opt);\n \t\t}\n \t}\n-\tif (*opt != 0)\n+\tif (opt && *opt != 0)\n \t\treturn -1;\n \treturn opt1 | (opt2 << 16);\n }\ndiff --git a/diff.h b/diff.h\nindex d052608..fa44d1b 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -5,6 +5,7 @@ #ifndef DIFF_H\n #define DIFF_H\n \n #include \"tree-walk.h\"\n+#include \"gitopt.h\"\n \n struct rev_info;\n struct diff_options;\n@@ -96,15 +97,13 @@ extern void diff_change(struct diff_opti\n extern void diff_unmerge(struct diff_options *,\n \t\t\t const char *path);\n \n-extern int diff_scoreopt_parse(const char *opt);\n-\n #define DIFF_SETUP_REVERSE      \t1\n #define DIFF_SETUP_USE_CACHE\t\t2\n #define DIFF_SETUP_USE_SIZE_CACHE\t4\n \n extern int git_diff_config(const char *var, const char *value);\n extern void diff_setup(struct diff_options *);\n-extern int diff_opt_parse(struct diff_options *, const char **, int);\n+extern int diff_opt_handler(struct gitopt_iterator *, const int, void *);\n extern int diff_setup_done(struct diff_options *);\n \n #define DIFF_DETECT_RENAME\t1\n@@ -117,6 +116,8 @@ extern void diffcore_std(struct diff_opt\n \n extern void diffcore_std_no_resolve(struct diff_options *);\n \n+extern const struct opt_spec diff_ost[];\n+\n #define COMMON_DIFF_OPTIONS_HELP \\\n \"\\ncommon diff options:\\n\" \\\n \"  -z            output diff-raw with lines terminated with NUL.\\n\" \\\ndiff --git a/gitopt/diff-files.h b/gitopt/diff-files.h\nnew file mode 100644\nindex 0000000..85c84ba\n--- /dev/null\n+++ b/gitopt/diff-files.h\n@@ -0,0 +1,45 @@\n+#ifndef GITOPT_DIFF_FILES_H\n+#define GITOPT_DIFF_FILES_H\n+\n+#include \"../gitopt/diff.h\"\n+#include \"../revision.h\"\n+\n+struct df_extra_opts {\n+\tstruct rev_info *rev;\n+\tint silent;\n+};\n+\n+static const struct opt_spec diff_files_ost[] = {\n+\t{ \"base\",\t0,\t0,\t0,\topt_base },\n+\t{ \"ours\",\t0,\t0,\t0,\topt_ours },\n+\t{ \"theirs\",\t0,\t0,\t0,\topt_theirs },\n+\t{ 0,\t\t'q',\t0,\t0,\topt_q },\n+\t{ 0, 0 }\n+};\n+\n+static int diff_files_opt_handler(struct gitopt_iterator *gi,\n+\t\t\t\tconst int id, void *args)\n+{\n+\tint rv = 1;\n+\tstruct df_extra_opts *dfeo = (struct df_extra_opts *)args;\n+\n+\tswitch (id) {\n+\tcase opt_base:\n+\t\tdfeo->rev->max_count = 1;\n+\t\tbreak;\n+\tcase opt_ours:\n+\t\tdfeo->rev->max_count = 2;\n+\t\tbreak;\n+\tcase opt_theirs:\n+\t\tdfeo->rev->max_count = 3;\n+\t\tbreak;\n+\tcase opt_q:\n+\t\tdfeo->silent = 1;\n+\t\tbreak;\n+\tdefault:\n+\t\trv = 0;\n+\t}\n+\treturn rv;\n+}\n+\n+#endif\ndiff --git a/gitopt/diff-index.h b/gitopt/diff-index.h\nnew file mode 100644\nindex 0000000..aff0944\n--- /dev/null\n+++ b/gitopt/diff-index.h\n@@ -0,0 +1,22 @@\n+#ifndef GITOPT_DIFF_INDEX_H\n+#define GITOPT_DIFF_INDEX_H\n+\n+#include \"../gitopt/diff.h\"\n+\n+static const struct opt_spec diff_index_ost[] = {\n+\t{ \"cached\",\t0,\t0,\t0,\topt_cached },\n+\t{ 0, 0 }\n+};\n+\n+static int diff_index_opt_handler(struct gitopt_iterator *gi,\n+\t\t\t\tconst int id, void *args)\n+{\n+\tif (id == opt_cached) {\n+\t\t*(int *)args = 1;\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+#endif\n+\ndiff --git a/gitopt/diff-tree.h b/gitopt/diff-tree.h\nnew file mode 100644\nindex 0000000..cfee9b5\n--- /dev/null\n+++ b/gitopt/diff-tree.h\n@@ -0,0 +1,22 @@\n+#ifndef GITOPT_DIFF_TREE_H\n+#define GITOPT_DIFF_TREE_H\n+\n+#include \"../gitopt/diff.h\"\n+\n+static const struct opt_spec diff_tree_ost[] = {\n+\t{ \"stdin\",\t0,\t0,\t0,\topt_stdin },\n+\t{ 0, 0 }\n+};\n+\n+/* inline to suppress a warning as it's not used in builtin_diff_tree */\n+static inline int diff_tree_opt_handler(struct gitopt_iterator *gi,\n+\t\t\t\tconst int id, void *args)\n+{\n+\tif (id == opt_stdin) {\n+\t\t*(int *)args = 1;\n+\t\treturn 1;\n+\t}\n+\treturn 0;\n+}\n+\n+#endif\ndiff --git a/gitopt/diff.h b/gitopt/diff.h\nnew file mode 100644\nindex 0000000..abc2c7a\n--- /dev/null\n+++ b/gitopt/diff.h\n@@ -0,0 +1,19 @@\n+#ifndef GITOPT_DIFF_H\n+#define GITOPT_DIFF_H\n+\n+#include \"../gitopt.h\"\n+\n+enum diff_extra_ids {\n+\t/* diff-files */\n+\topt_base = 1, opt_ours, opt_theirs, opt_q,\n+\n+\t/* diff-tree */\n+\topt_stdin,\n+\n+\t/* diff-index */\n+\topt_cached,\n+\n+\tLAST_DIFF_EXTRA_ID,\n+};\n+\n+#endif /* GITOPT_DIFF_H */\ndiff --git a/http-push.c b/http-push.c\nindex b4327d9..9c16f3b 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -2499,7 +2499,7 @@ int main(int argc, char **argv)\n \t\t\tcommit_argc++;\n \t\t}\n \t\tinit_revisions(&revs);\n-\t\tsetup_revisions(commit_argc, commit_argv, &revs, NULL);\n+\t\tsetup_revisions(commit_argc, commit_argv, &revs, NULL, NULL);\n \t\tfree(new_sha1_hex);\n \t\tif (old_sha1_hex) {\n \t\t\tfree(old_sha1_hex);\ndiff --git a/rev-list.c b/rev-list.c\nindex 8b0ec38..7baeb52 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -291,34 +291,49 @@ static void mark_edges_uninteresting(str\n \t}\n }\n \n+enum rev_list_ids { opt_header = 1, opt_timestamp, opt_bisect };\n+\n+static const struct opt_spec rev_list_ost[] = {\n+\t{ \"header\",\t0,\t0,\t0,\topt_header },\n+\t{ \"timestamp\",\t0,\t0,\t0,\topt_timestamp },\n+\t{ \"bisect\",\t0,\t0,\t0,\topt_bisect },\n+\t{ 0, 0 }\n+};\n+\n+static int rev_list_opt_handler(struct gitopt_iterator *gi,\n+\t\t\t\tconst int id, void *args)\n+{\n+\tint rv = 1;\n+\tswitch (id) {\n+\tcase opt_header:\n+\t\trevs.verbose_header = 1;\n+\t\tbreak;\n+\tcase opt_timestamp:\n+\t\tshow_timestamp = 1;\n+\t\tbreak;\n+\tcase opt_bisect:\n+\t\tbisect_list = 1;\n+\t\tbreak;\n+\tdefault:\n+\t\trv = 0;\n+\t}\n+\treturn rv;\n+}\n+\n int main(int argc, const char **argv)\n {\n \tstruct commit_list *list;\n-\tint i;\n+\tstruct exec_args *b;\n+\tstruct gitopt_extra ge = { rev_list_ost, rev_list_opt_handler, NULL };\n \n \tinit_revisions(&revs);\n \trevs.abbrev = 0;\n \trevs.commit_format = CMIT_FMT_UNSPECIFIED;\n-\targc = setup_revisions(argc, argv, &revs, NULL);\n-\n-\tfor (i = 1 ; i < argc; i++) {\n-\t\tconst char *arg = argv[i];\n+\tb = setup_revisions(argc, argv, &revs, NULL, &ge);\n \n-\t\tif (!strcmp(arg, \"--header\")) {\n-\t\t\trevs.verbose_header = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--timestamp\")) {\n-\t\t\tshow_timestamp = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--bisect\")) {\n-\t\t\tbisect_list = 1;\n-\t\t\tcontinue;\n-\t\t}\n+\tif (b->argc)\n \t\tusage(rev_list_usage);\n \n-\t}\n \tif (revs.commit_format != CMIT_FMT_UNSPECIFIED) {\n \t\t/* The command line has a --pretty  */\n \t\thdr_termination = '\\n';\ndiff --git a/revision.c b/revision.c\nindex 2294b16..aeaf52c 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -6,6 +6,7 @@ #include \"commit.h\"\n #include \"diff.h\"\n #include \"refs.h\"\n #include \"revision.h\"\n+#include \"gitopt.h\"\n \n static char *path_name(struct name_path *path, const char *name)\n {\n@@ -534,6 +535,136 @@ void init_revisions(struct rev_info *rev\n \tdiff_setup(&revs->diffopt);\n }\n \n+enum setup_revision_ost_ids {\n+\topt_max_count = GITOPT_SR_BASE, opt_max_age, opt_min_age,\n+\topt_since, opt_until, opt_all, opt_not, opt_default,\n+\topt_topo_order, opt_date_order, opt_parents, opt_dense,\n+\topt_sparse, opt_remove_empty, opt_no_merges, opt_boundary,\n+\topt_objects, opt_objects_edge, opt_unpacked,\n+\topt_r, opt_t, opt_m, opt_c, opt_cc, opt_v,\n+\topt_pretty, opt_root, opt_no_commit_id, opt_always,\n+\topt_abbrev, opt_no_abbrev, opt_abbrev_commit, opt_full_diff\n+};\n+\n+static const struct opt_spec setup_revisions_ost[] = {\n+\t{ \"max-count\",\t\t'n',\t0,\tARG_INT,\topt_max_count },\n+\t{ 0,\t\t\t' ',\t0,\tARG_INT,\topt_max_count },\n+\t{ \"max-age\",\t\t0,\t0,\tARG_INT,\topt_max_age },\n+\t{ \"min-age\",\t\t0,\t0,\tARG_INT,\topt_min_age },\n+\t{ \"since\",\t\t0,\t0,\tARG_ONE,\topt_since },\n+\t{ \"after\",\t\t0,\t0,\tARG_ONE,\topt_since },\n+\t{ \"before\",\t\t0,\t0,\tARG_ONE,\topt_until },\n+\t{ \"until\",\t\t0,\t0,\tARG_ONE,\topt_until },\n+\t{ \"all\",\t\t0,\t0,\t0,\t\topt_all },\n+\t{ \"not\",\t\t0,\t0,\t0,\t\topt_not },\n+\t{ \"default\",\t\t0,\t0,\tARG_ONE,\topt_default },\n+\t{ \"topo-order\",\t\t0,\t0,\t0,\topt_topo_order },\n+\t{ \"date-order\",\t\t0,\t0,\t0,\topt_date_order },\n+\t{ \"parents\",\t\t0,\t0,\t0,\topt_parents },\n+\t{ \"dense\",\t\t0,\t0,\t0,\topt_dense },\n+\t{ \"sparse\",\t\t0,\t0,\t0,\topt_sparse },\n+\t{ \"remove-empty\",\t0,\t0,\t0,\topt_remove_empty },\n+\t{ \"no-merges\",\t\t0,\t0,\t0,\topt_no_merges },\n+\t{ \"boundary\",\t\t0,\t0,\t0,\topt_boundary },\n+\t{ \"objects\",\t\t0,\t0,\t0,\topt_objects },\n+\t{ \"objects-edge\",\t0,\t0,\t0,\topt_objects_edge },\n+\t{ \"unpacked\",\t\t0,\t0,\t0,\topt_unpacked },\n+\t{ 0,\t\t\t'r',\t0,\t0,\topt_r },\n+\t{ 0,\t\t\t't',\t0,\t0,\topt_t },\n+\t{ 0,\t\t\t'm',\t0,\t0,\topt_m },\n+\t{ 0,\t\t\t'c',\t0,\t0,\topt_c },\n+\t{ \"cc\",\t\t\t0,\t0,\t0,\topt_cc },\n+\t{ 0,\t\t\t'v',\t0,\t0,\topt_v },\n+\t{ \"pretty\",\t\t0,\t0,\tARG_OPT,\topt_pretty },\n+\t{ \"root\",\t\t0,\t0,\t0,\topt_root },\n+\t{ \"no-commit-id\",\t0,\t0,\t0,\topt_no_commit_id },\n+\t{ \"always\",\t\t0,\t0,\t0,\topt_always },\n+\t{ \"no-abbrev\",\t\t0,\t0,\t0,\topt_no_abbrev },\n+\t{ \"abbrev\",\t\t0,\t0,\t0,\topt_abbrev },\n+\t{ \"abbrev-commit\",\t0,\t0,\t0,\topt_abbrev_commit },\n+\t{ \"full-diff\",\t\t0,\t0,\t0,\topt_full_diff },\n+\t{ 0, 0 }\n+};\n+\n+static int setup_revision(struct gitopt_iterator *gi, struct rev_info *revs,\n+\t\t\tint flags, int seen_dashdash)\n+{\n+\tconst char *arg = gi->argv[gi->pos];\n+\tunsigned char sha1[20];\n+\tstruct object *object;\n+\tchar *dotdot;\n+\tint local_flags;\n+\n+\tdotdot = strstr(arg, \"..\");\n+\tif (dotdot) {\n+\t\tunsigned char from_sha1[20];\n+\t\tconst char *next = dotdot + 2;\n+\t\tconst char *this = arg;\n+\t\t*dotdot = 0;\n+\t\tif (!*next)\n+\t\t\tnext = \"HEAD\";\n+\t\tif (dotdot == arg)\n+\t\t\tthis = \"HEAD\";\n+\t\tif (!get_sha1(this, from_sha1) &&\n+\t\t    !get_sha1(next, sha1)) {\n+\t\t\tstruct object *exclude;\n+\t\t\tstruct object *include;\n+\n+\t\t\texclude = get_reference(revs, this, from_sha1,\n+\t\t\t\t\t\tflags ^ UNINTERESTING);\n+\t\t\tinclude = get_reference(revs, next, sha1, flags);\n+\t\t\tif (!exclude || !include)\n+\t\t\t\tdie(\"Invalid revision range %s..%s\", arg, next);\n+\n+\t\t\tif (!seen_dashdash) {\n+\t\t\t\t*dotdot = '.';\n+\t\t\t\tverify_non_filename(revs->prefix, arg);\n+\t\t\t}\n+\t\t\tadd_pending_object(revs, exclude, this);\n+\t\t\tadd_pending_object(revs, include, next);\n+\t\t\treturn 1;\n+\t\t}\n+\t\t*dotdot = '.';\n+\t}\n+\n+\tdotdot = strstr(arg, \"^@\");\n+\tif (dotdot && !dotdot[2]) {\n+\t\t*dotdot = 0;\n+\t\tif (add_parents_only(revs, arg, flags))\n+\t\t\treturn 1;\n+\t\t*dotdot = '^';\n+\t}\n+\tlocal_flags = 0;\n+\tif (*arg == '^') {\n+\t\tlocal_flags = UNINTERESTING;\n+\t\targ++;\n+\t}\n+\tif (get_sha1(arg, sha1) < 0) {\n+\t\tint j;\n+\n+\t\tif (seen_dashdash || local_flags)\n+\t\t\tdie(\"bad revision '%s'\", arg);\n+\n+\t\t/* If we didn't have a \"--\":\n+\t\t * (1) all filenames must exist;\n+\t\t * (2) all rev-args must not be interpretable\n+\t\t *     as a valid filename.\n+\t\t * but the latter we have checked in the main loop.\n+\t\t */\n+\t\tfor (j = gi->pos; j < gi->argc; j++)\n+\t\t\tverify_filename(revs->prefix, gi->argv[j]);\n+\n+\t\trevs->prune_data = get_pathspec(revs->prefix,\n+\t\t\t\t\t\tgi->argv + gi->pos);\n+\t\treturn 1;\n+\t}\n+\tif (!seen_dashdash)\n+\t\tverify_non_filename(revs->prefix, arg);\n+\tobject = get_reference(revs, arg, sha1, flags ^ local_flags);\n+\tadd_pending_object(revs, object, arg);\n+\treturn 1;\n+}\n+\n /*\n  * Parse revision information, filling in the \"rev_info\" structure,\n  * and removing the used arguments from the argument list.\n@@ -541,11 +672,14 @@ void init_revisions(struct rev_info *rev\n  * Returns the number of arguments left that weren't recognized\n  * (which are also moved to the head of the argument list)\n  */\n-int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)\n+struct exec_args *setup_revisions(int argc, const char **argv,\n+\t\t\t\t\tstruct rev_info *revs, const char *def,\n+\t\t\t\t\tconst struct gitopt_extra *ge)\n {\n-\tint i, flags, seen_dashdash;\n-\tconst char **unrecognized = argv + 1;\n-\tint left = 1;\n+\tint i, id, flags, seen_dashdash;\n+\tstruct gitopt_iterator gi;\n+\tstruct exec_args *ea, *b;\n+\tstruct opt_spec *ost;\n \n \t/* First, search for \"--\" */\n \tseen_dashdash = 0;\n@@ -561,261 +695,158 @@ int setup_revisions(int argc, const char\n \t}\n \n \tflags = 0;\n-\tfor (i = 1; i < argc; i++) {\n-\t\tstruct object *object;\n-\t\tconst char *arg = argv[i];\n-\t\tunsigned char sha1[20];\n-\t\tchar *dotdot;\n-\t\tint local_flags;\n+\tost = combine_opt_spec(setup_revisions_ost, diff_ost);\n+\tif (ge) {\n+\t\tstruct opt_spec *tmp = ost;\n+\t\tost = combine_opt_spec(tmp, ge->ost);\n+\t\tfree(tmp);\n+\t}\n \n-\t\tif (*arg == '-') {\n-\t\t\tint opts;\n-\t\t\tif (!strncmp(arg, \"--max-count=\", 12)) {\n-\t\t\t\trevs->max_count = atoi(arg + 12);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\t/* accept -<digit>, like traditional \"head\" */\n-\t\t\tif ((*arg == '-') && isdigit(arg[1])) {\n-\t\t\t\trevs->max_count = atoi(arg + 1);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"-n\")) {\n-\t\t\t\tif (argc <= i + 1)\n-\t\t\t\t\tdie(\"-n requires an argument\");\n-\t\t\t\trevs->max_count = atoi(argv[++i]);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strncmp(arg,\"-n\",2)) {\n-\t\t\t\trevs->max_count = atoi(arg + 2);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strncmp(arg, \"--max-age=\", 10)) {\n-\t\t\t\trevs->max_age = atoi(arg + 10);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strncmp(arg, \"--since=\", 8)) {\n-\t\t\t\trevs->max_age = approxidate(arg + 8);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strncmp(arg, \"--after=\", 8)) {\n-\t\t\t\trevs->max_age = approxidate(arg + 8);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strncmp(arg, \"--min-age=\", 10)) {\n-\t\t\t\trevs->min_age = atoi(arg + 10);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strncmp(arg, \"--before=\", 9)) {\n-\t\t\t\trevs->min_age = approxidate(arg + 9);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strncmp(arg, \"--until=\", 8)) {\n-\t\t\t\trevs->min_age = approxidate(arg + 8);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--all\")) {\n-\t\t\t\thandle_all(revs, flags);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--not\")) {\n-\t\t\t\tflags ^= UNINTERESTING;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--default\")) {\n-\t\t\t\tif (++i >= argc)\n-\t\t\t\t\tdie(\"bad --default argument\");\n-\t\t\t\tdef = argv[i];\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--topo-order\")) {\n-\t\t\t\trevs->topo_order = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--date-order\")) {\n-\t\t\t\trevs->lifo = 0;\n-\t\t\t\trevs->topo_order = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--parents\")) {\n-\t\t\t\trevs->parents = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--dense\")) {\n-\t\t\t\trevs->dense = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--sparse\")) {\n-\t\t\t\trevs->dense = 0;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--remove-empty\")) {\n-\t\t\t\trevs->remove_empty_trees = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--no-merges\")) {\n-\t\t\t\trevs->no_merges = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--boundary\")) {\n-\t\t\t\trevs->boundary = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--objects\")) {\n-\t\t\t\trevs->tag_objects = 1;\n-\t\t\t\trevs->tree_objects = 1;\n-\t\t\t\trevs->blob_objects = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--objects-edge\")) {\n-\t\t\t\trevs->tag_objects = 1;\n-\t\t\t\trevs->tree_objects = 1;\n-\t\t\t\trevs->blob_objects = 1;\n-\t\t\t\trevs->edge_hint = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--unpacked\")) {\n-\t\t\t\trevs->unpacked = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"-r\")) {\n-\t\t\t\trevs->diff = 1;\n-\t\t\t\trevs->diffopt.recursive = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"-t\")) {\n-\t\t\t\trevs->diff = 1;\n-\t\t\t\trevs->diffopt.recursive = 1;\n-\t\t\t\trevs->diffopt.tree_in_recursive = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"-m\")) {\n-\t\t\t\trevs->ignore_merges = 0;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"-c\")) {\n-\t\t\t\trevs->diff = 1;\n-\t\t\t\trevs->dense_combined_merges = 0;\n-\t\t\t\trevs->combine_merges = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--cc\")) {\n-\t\t\t\trevs->diff = 1;\n-\t\t\t\trevs->dense_combined_merges = 1;\n-\t\t\t\trevs->combine_merges = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"-v\")) {\n-\t\t\t\trevs->verbose_header = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strncmp(arg, \"--pretty\", 8)) {\n-\t\t\t\trevs->verbose_header = 1;\n-\t\t\t\trevs->commit_format = get_commit_format(arg+8);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--root\")) {\n-\t\t\t\trevs->show_root_diff = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--no-commit-id\")) {\n-\t\t\t\trevs->no_commit_id = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--always\")) {\n-\t\t\t\trevs->always_show_header = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--no-abbrev\")) {\n-\t\t\t\trevs->abbrev = 0;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--abbrev\")) {\n+\tgitopt_iter_setup(&gi, argc, argv);\n+\tea = gi.ea;\n+\tb = gi.b;\n+\tfor (; (id = gitopt_iter_parse(&gi, ost)); gitopt_iter_next(&gi)) {\n+\t\tswitch (id) {\n+\t\tcase opt_max_count:\n+\t\t\trevs->max_count = atoi(ea->argv[0]);\n+\t\t\tbreak;\n+\t\tcase opt_max_age:\n+\t\t\trevs->max_age = atoi(ea->argv[0]);\n+\t\t\tbreak;\n+\t\tcase opt_min_age:\n+\t\t\trevs->min_age = atoi(ea->argv[0]);\n+\t\t\tbreak;\n+\t\tcase opt_since:\n+\t\t\trevs->max_age = approxidate(ea->argv[0]);\n+\t\t\tbreak;\n+\t\tcase opt_until:\n+\t\t\trevs->min_age = approxidate(ea->argv[0]);\n+\t\t\tbreak;\n+\t\tcase opt_all:\n+\t\t\thandle_all(revs, flags);\n+\t\t\tbreak;\n+\t\tcase opt_not:\n+\t\t\tflags ^= UNINTERESTING;\n+\t\t\tbreak;\n+\t\tcase opt_default:\n+\t\t\tif (!ea->argc)\n+\t\t\t\tdie(\"bad --default argument\");\n+\t\t\tdef = ea->argv[0];\n+\t\t\tbreak;\n+\t\tcase opt_topo_order:\n+\t\t\trevs->topo_order = 1;\n+\t\t\tbreak;\n+\t\tcase opt_date_order:\n+\t\t\trevs->lifo = 0;\n+\t\t\trevs->topo_order = 1;\n+\t\t\tbreak;\n+\t\tcase opt_parents:\n+\t\t\trevs->parents = 1;\n+\t\t\tbreak;\n+\t\tcase opt_dense:\n+\t\t\trevs->dense = 1;\n+\t\t\tbreak;\n+\t\tcase opt_sparse:\n+\t\t\trevs->dense = 0;\n+\t\t\tbreak;\n+\t\tcase opt_remove_empty:\n+\t\t\trevs->remove_empty_trees = 1;\n+\t\t\tbreak;\n+\t\tcase opt_no_merges:\n+\t\t\trevs->no_merges = 1;\n+\t\t\tbreak;\n+\t\tcase opt_boundary:\n+\t\t\trevs->boundary = 1;\n+\t\t\tbreak;\n+\t\tcase opt_objects_edge:\n+\t\t\trevs->edge_hint = 1; /* fall through */\n+\t\tcase opt_objects:\n+\t\t\trevs->tag_objects = 1;\n+\t\t\trevs->tree_objects = 1;\n+\t\t\trevs->blob_objects = 1;\n+\t\t\tbreak;\n+\t\tcase opt_unpacked:\n+\t\t\trevs->unpacked = 1;\n+\t\t\tbreak;\n+\t\tcase opt_r:\n+\t\t\trevs->diff = 1;\n+\t\t\trevs->diffopt.recursive = 1;\n+\t\t\tbreak;\n+\t\tcase opt_t:\n+\t\t\trevs->diff = 1;\n+\t\t\trevs->diffopt.recursive = 1;\n+\t\t\trevs->diffopt.tree_in_recursive = 1;\n+\t\t\tbreak;\n+\t\tcase opt_m:\n+\t\t\trevs->ignore_merges = 0;\n+\t\t\tbreak;\n+\t\tcase opt_c:\n+\t\t\trevs->diff = 1;\n+\t\t\trevs->dense_combined_merges = 0;\n+\t\t\trevs->combine_merges = 1;\n+\t\t\tbreak;\n+\t\tcase opt_cc:\n+\t\t\trevs->diff = 1;\n+\t\t\trevs->dense_combined_merges = 1;\n+\t\t\trevs->combine_merges = 1;\n+\t\t\tbreak;\n+\t\tcase opt_v:\n+\t\t\trevs->verbose_header = 1;\n+\t\t\tbreak;\n+\t\tcase opt_pretty:\n+\t\t\trevs->verbose_header = 1;\n+\t\t\trevs->commit_format = get_commit_format(ea->argv[1]);\n+\t\t\tbreak;\n+\t\tcase opt_root:\n+\t\t\trevs->show_root_diff = 1;\n+\t\t\tbreak;\n+\t\tcase opt_no_commit_id:\n+\t\t\trevs->no_commit_id = 1;\n+\t\t\tbreak;\n+\t\tcase opt_always:\n+\t\t\trevs->always_show_header = 1;\n+\t\t\tbreak;\n+\t\tcase opt_no_abbrev:\n+\t\t\trevs->abbrev = 0;\n+\t\t\tbreak;\n+\t\tcase opt_abbrev_commit:\n+\t\t\trevs->abbrev_commit = 1;\n+\t\t\tbreak;\n+\t\tcase opt_full_diff:\n+\t\t\trevs->diff = 1;\n+\t\t\trevs->full_diff = 1;\n+\t\t\tbreak;\n+\t\tcase opt_abbrev:\n+\t\t\tif (ea->argc == 1)\n \t\t\t\trevs->abbrev = DEFAULT_ABBREV;\n-\t\t\t\tcontinue;\n+\t\t\telse {\n+\t\t\t\trevs->abbrev = strtoul(ea->argv[1], NULL, 10);\n+\t\t\t\tif (revs->abbrev < MINIMUM_ABBREV)\n+\t\t\t\t\trevs->abbrev = MINIMUM_ABBREV;\n+\t\t\t\telse if (40 < revs->abbrev)\n+\t\t\t\t\trevs->abbrev = 40;\n \t\t\t}\n-\t\t\tif (!strcmp(arg, \"--abbrev-commit\")) {\n-\t\t\t\trevs->abbrev_commit = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!strcmp(arg, \"--full-diff\")) {\n-\t\t\t\trevs->diff = 1;\n-\t\t\t\trevs->full_diff = 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\topts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);\n-\t\t\tif (opts > 0) {\n-\t\t\t\trevs->diff = 1;\n-\t\t\t\ti += opts - 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\t*unrecognized++ = arg;\n-\t\t\tleft++;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tdotdot = strstr(arg, \"..\");\n-\t\tif (dotdot) {\n-\t\t\tunsigned char from_sha1[20];\n-\t\t\tconst char *next = dotdot + 2;\n-\t\t\tconst char *this = arg;\n-\t\t\t*dotdot = 0;\n-\t\t\tif (!*next)\n-\t\t\t\tnext = \"HEAD\";\n-\t\t\tif (dotdot == arg)\n-\t\t\t\tthis = \"HEAD\";\n-\t\t\tif (!get_sha1(this, from_sha1) &&\n-\t\t\t    !get_sha1(next, sha1)) {\n-\t\t\t\tstruct object *exclude;\n-\t\t\t\tstruct object *include;\n-\n-\t\t\t\texclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING);\n-\t\t\t\tinclude = get_reference(revs, next, sha1, flags);\n-\t\t\t\tif (!exclude || !include)\n-\t\t\t\t\tdie(\"Invalid revision range %s..%s\", arg, next);\n-\n-\t\t\t\tif (!seen_dashdash) {\n-\t\t\t\t\t*dotdot = '.';\n-\t\t\t\t\tverify_non_filename(revs->prefix, arg);\n-\t\t\t\t}\n-\t\t\t\tadd_pending_object(revs, exclude, this);\n-\t\t\t\tadd_pending_object(revs, include, next);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\t*dotdot = '.';\n-\t\t}\n-\t\tdotdot = strstr(arg, \"^@\");\n-\t\tif (dotdot && !dotdot[2]) {\n-\t\t\t*dotdot = 0;\n-\t\t\tif (add_parents_only(revs, arg, flags))\n-\t\t\t\tcontinue;\n-\t\t\t*dotdot = '^';\n-\t\t}\n-\t\tlocal_flags = 0;\n-\t\tif (*arg == '^') {\n-\t\t\tlocal_flags = UNINTERESTING;\n-\t\t\targ++;\n-\t\t}\n-\t\tif (get_sha1(arg, sha1)) {\n-\t\t\tint j;\n-\n-\t\t\tif (seen_dashdash || local_flags)\n-\t\t\t\tdie(\"bad revision '%s'\", arg);\n-\n-\t\t\t/* If we didn't have a \"--\":\n-\t\t\t * (1) all filenames must exist;\n-\t\t\t * (2) all rev-args must not be interpretable\n-\t\t\t *     as a valid filename.\n-\t\t\t * but the latter we have checked in the main loop.\n-\t\t\t */\n-\t\t\tfor (j = i; j < argc; j++)\n-\t\t\t\tverify_filename(revs->prefix, argv[j]);\n-\n-\t\t\trevs->prune_data = get_pathspec(revs->prefix, argv + i);\n \t\t\tbreak;\n+\t\tcase GITOPT_NON_OPTION:\n+\t\t\t/* fprintf(stderr,\"no: %s\\n\",gi.argv[gi.pos]); */\n+\t\t\ti = setup_revision(&gi, revs, flags, seen_dashdash);\n+\t\t\tgi.pos += i - 1;\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\t/* fprintf(stderr,\"do: %s\\n\",gi.argv[gi.pos]); */\n+\t\t\ti = diff_opt_handler(&gi, id, &revs->diffopt);\n+\t\t\tif (i > 0)\n+\t\t\t\trevs->diff = 1;\n+\t\t\telse if (i < 0) /* error */\n+\t\t\t\tb->argv[b->argc++] = gi.argv[gi.pos++];\n+\t\t\telse if (ge) {\n+\t\t\t\t/* i = 0: not a diff_opt, try other handler */\n+\t\t\t\ti = ge->opt_handler(&gi, id, ge->args);\n+\t\t\t\tif (i <= 0) /* error */\n+\t\t\t\t\tb->argv[b->argc++] = gi.argv[gi.pos++];\n+\t\t\t}\n+\t\t\tgi.pos += i - 1;\n \t\t}\n-\t\tif (!seen_dashdash)\n-\t\t\tverify_non_filename(revs->prefix, arg);\n-\t\tobject = get_reference(revs, arg, sha1, flags ^ local_flags);\n-\t\tadd_pending_object(revs, object, arg);\n \t}\n \tif (def && !revs->pending_objects) {\n \t\tunsigned char sha1[20];\n@@ -844,7 +875,8 @@ int setup_revisions(int argc, const char\n \trevs->diffopt.abbrev = revs->abbrev;\n \tdiff_setup_done(&revs->diffopt);\n \n-\treturn left;\n+\tfree(ost);\n+\treturn b;\n }\n \n void prepare_revision_walk(struct rev_info *revs)\ndiff --git a/revision.h b/revision.h\nindex 48d7b4c..28211b6 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -81,7 +81,9 @@ extern int rev_same_tree_as_empty(struct\n extern int rev_compare_tree(struct rev_info *, struct tree *t1, struct tree *t2);\n \n extern void init_revisions(struct rev_info *revs);\n-extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);\n+extern struct exec_args *setup_revisions(int, const char **argv,\n+\t\t\t\t\tstruct rev_info *revs, const char *def,\n+\t\t\t\t\tconst struct gitopt_extra *);\n extern void prepare_revision_walk(struct rev_info *revs);\n extern struct commit *get_revision(struct rev_info *revs);\n \n-- \n1.3.2.g102e322\n"},{"id":"19913","messageId":"1147619963765-git-send-email-normalperson@yhbt.net","threadId":"4135","inReplyTo":"11476199622462-git-send-email-normalperson@yhbt.net","subject":"[PATCH 4/5] commit: allow --pretty= args to be abbreviated","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-14T15:19:21Z","receivedAt":"2006-05-14T15:19:21Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Signed-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n commit.c |   42 +++++++++++++++++++++++++++++-------------\n 1 files changed, 29 insertions(+), 13 deletions(-)\n\nb6190a2b46acc250277fd55e33e0a7216b7fad75\ndiff --git a/commit.c b/commit.c\nindex 2343729..a786371 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -22,23 +22,39 @@ struct sort_node\n \n const char *commit_type = \"commit\";\n \n+struct cmt_fmt_map {\n+\tconst char *n;\n+\tenum cmit_fmt v;\n+} cmt_fmts[] = {\n+\t{ \"raw\",\tCMIT_FMT_RAW },\n+\t{ \"medium\",\tCMIT_FMT_MEDIUM },\n+\t{ \"short\",\tCMIT_FMT_SHORT },\n+\t{ \"full\",\tCMIT_FMT_FULL },\n+\t{ \"fuller\",\tCMIT_FMT_FULLER },\n+\t{ \"oneline\",\tCMIT_FMT_ONELINE },\n+};\n+\n enum cmit_fmt get_commit_format(const char *arg)\n {\n+\tint i, found = -1;\n \tif (!arg)\n \t\treturn CMIT_FMT_DEFAULT;\n-\tif (!strcmp(arg, \"raw\"))\n-\t\treturn CMIT_FMT_RAW;\n-\tif (!strcmp(arg, \"medium\"))\n-\t\treturn CMIT_FMT_MEDIUM;\n-\tif (!strcmp(arg, \"short\"))\n-\t\treturn CMIT_FMT_SHORT;\n-\tif (!strcmp(arg, \"full\"))\n-\t\treturn CMIT_FMT_FULL;\n-\tif (!strcmp(arg, \"fuller\"))\n-\t\treturn CMIT_FMT_FULLER;\n-\tif (!strcmp(arg, \"oneline\"))\n-\t\treturn CMIT_FMT_ONELINE;\n-\tdie(\"invalid --pretty format\");\n+\tfor (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {\n+\t\tif (!strcmp(arg, cmt_fmts[i].n))\n+\t\t\treturn cmt_fmts[i].v;\n+\t}\n+\n+\t/* look for abbreviations */\n+\tfor (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {\n+\t\tif (strstr(cmt_fmts[i].n, arg)) {\n+\t\t\tif (found >= 0)\n+\t\t\t\tdie(\"invalid --pretty format: %s\", arg);\n+\t\t\tfound = i;\n+\t\t}\n+\t}\n+\tif (found >= 0)\n+\t\treturn cmt_fmts[found].v;\n+\tdie(\"invalid --pretty format: %s\", arg);\n }\n \n static struct commit *check_commit(struct object *obj,\n-- \n1.3.2.g102e322\n"},{"id":"19912","messageId":"11476199631085-git-send-email-normalperson@yhbt.net","threadId":"4135","inReplyTo":"11476199622462-git-send-email-normalperson@yhbt.net","subject":"[PATCH 5/5] diff: parse U/u/unified options with an optional integer arg","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-14T15:19:22Z","receivedAt":"2006-05-14T15:19:22Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Looks like Linus and I both think GIT_DIFF_OPTS=\"--unified=5\" is\nsilly, but it still continues to work.\n\nThis was originally bundled into my first series gitopt patches,\nand now Linus has a more correct/complete one that affects\ncombine-diff and works with uppercase -U  This patch\ncombines the Linus one with my gitopt version.\n\n-u (lowercase) now accepts an optional arg, like -U (GNU diff\n-u also has this behavior).\n\nThis uses the built-in gitopt parsers to do optional\ninteger argument handling.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n combine-diff.c |    1 +\n diff.c         |   11 ++++++++---\n diff.h         |    1 +\n 3 files changed, 10 insertions(+), 3 deletions(-)\n\n102e3227de7c7f08a69096d5094ee31128bf9819\ndiff --git a/combine-diff.c b/combine-diff.c\nindex 8a8fe38..64b20cc 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -608,6 +608,7 @@ static int show_patch_diff(struct combin\n \tint abbrev = opt->full_index ? 40 : DEFAULT_ABBREV;\n \tmmfile_t result_file;\n \n+\tcontext = opt->context;\n \t/* Read the result of merge first */\n \tif (!working_tree_file)\n \t\tresult = grab_blob(elem->sha1, &result_size);\ndiff --git a/diff.c b/diff.c\nindex 7d88dc5..6ee612f 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -15,7 +15,7 @@ #include \"gitopt.h\"\n static int diff_scoreopt_parse(const int id, const char *opt);\n \n enum diff_ost_ids {\n-\topt_p = GITOPT_DIFF_BASE, opt_raw, opt_patch_with_raw,\n+\topt_u = GITOPT_DIFF_BASE, opt_p, opt_raw, opt_patch_with_raw,\n \topt_stat, opt_patch_with_stat,\n \topt_z, opt_l, opt_full_index, opt_name_only, opt_name_status,\n \topt_R, opt_S, opt_s, opt_O, opt_diff_filter,\n@@ -26,7 +26,8 @@ enum diff_ost_ids {\n \n const struct opt_spec diff_ost[] = {\n \t{ 0,\t\t\t'p',\t0,\t0,\topt_p },\n-\t{ \"unified\",\t\t'u',\t0,\t0,\topt_p },\n+\t{ \"unified\",\t\t'u',\t0,\tARG_OPTINT,\topt_u },\n+\t{ 0,\t\t\t'U',\t0,\tARG_OPTINT,\topt_u },\n \t{ \"raw\",\t\t0,\t0,\t0,\topt_raw },\n \t{ \"patch-with-raw\",\t0,\t0,\t0,\topt_patch_with_raw },\n \t{ \"stat\",\t\t0,\t0,\t0,\topt_stat },\n@@ -600,7 +601,7 @@ static void builtin_diff(const char *nam\n \n \t\tecbdata.label_path = lbl;\n \t\txpp.flags = XDF_NEED_MINIMAL;\n-\t\txecfg.ctxlen = 3;\n+\t\txecfg.ctxlen = o->context;\n \t\txecfg.flags = XDL_EMIT_FUNCNAMES;\n \t\tif (!diffopts)\n \t\t\t;\n@@ -1224,6 +1225,7 @@ void diff_setup(struct diff_options *opt\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n+\toptions->context = 3;\n \n \toptions->change = diff_change;\n \toptions->add_remove = diff_addremove;\n@@ -1269,6 +1271,9 @@ int diff_opt_handler(struct gitopt_itera\n \tstruct diff_options *options = (struct diff_options *)args;\n \n \tswitch (id) {\n+\tcase opt_u:\n+\t\tif (gi->ea->argc > 1)\n+\t\t\toptions->context = strtol(gi->ea->argv[1], NULL, 10);\n \tcase opt_p:\n \t\toptions->output_format = DIFF_FORMAT_PATCH;\n \t\tbreak;\ndiff --git a/diff.h b/diff.h\nindex fa44d1b..b9d7573 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -33,6 +33,7 @@ struct diff_options {\n \t\t full_index:1,\n \t\t silent_on_remove:1,\n \t\t find_copies_harder:1;\n+\tint context;\n \tint break_opt;\n \tint detect_rename;\n \tint line_termination;\n-- \n1.3.2.g102e322\n"},{"id":"19915","messageId":"Pine.LNX.4.64.0605140931410.3866@g5.osdl.org","threadId":"4135","inReplyTo":"11476199631085-git-send-email-normalperson@yhbt.net","subject":"Re: [PATCH 5/5] diff: parse U/u/unified options with an optional integer arg","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-05-14T16:33:48Z","receivedAt":"2006-05-14T16:33:48Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 14 May 2006, Eric Wong wrote:\n> \n> -u (lowercase) now accepts an optional arg, like -U (GNU diff\n> -u also has this behavior).\n\nActually, modern GNU \"diff -u5\" will say\n\n\tdiff: `-5' option is obsolete; use `-U 5'\n\tdiff: Try `diff --help' for more information.\n\nand exit.\n\nI'm not entirely sure why, but I think it's because \"u\" can be mixed (ie \nwith something like \"-urN\"), while \"U\" cannot. The GNU diff rule seems to \nbe that simple arguments can be mixed together, but arguments with \nparameters cannot. Which sounds sane.\n\n\t\tLinus\n"},{"id":"19940","messageId":"20060514233606.GA10249@hand.yhbt.net","threadId":"4135","inReplyTo":"Pine.LNX.4.64.0605140931410.3866@g5.osdl.org","subject":"Re: [PATCH 5/5] diff: parse U/u/unified options with an optional integer arg","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-14T23:36:06Z","receivedAt":"2006-05-14T23:36:06Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Linus Torvalds <torvalds@osdl.org> wrote:\n> \n> \n> On Sun, 14 May 2006, Eric Wong wrote:\n> > \n> > -u (lowercase) now accepts an optional arg, like -U (GNU diff\n> > -u also has this behavior).\n> \n> Actually, modern GNU \"diff -u5\" will say\n> \n> \tdiff: `-5' option is obsolete; use `-U 5'\n> \tdiff: Try `diff --help' for more information.\n> \n> and exit.\n> \n> I'm not entirely sure why, but I think it's because \"u\" can be mixed (ie \n> with something like \"-urN\"), while \"U\" cannot. The GNU diff rule seems to \n> be that simple arguments can be mixed together, but arguments with \n> parameters cannot. Which sounds sane.\n\nInteresting.  With _POSIX2_VERSION < 200112, diff is happy to accept\n-u5.  Setting _POSIX2_VERSION >= 200112 causes it to burp, however.\n\nIt turns out -u5 is actually short for -u -5, but I (and apparently some\nother people) have always assumed -u5 worked like -U5.  POSIX doesn't\nlike -<num> switches, however, but we support it regardless in rev-list.\n\nI prefer that it works the -u5 way, of course :)\n\nAlso, I find integer arguments being bundled with other parameters to be\npretty nice feature, but it's easy to disable if users don't like it:\n\ndiff --git a/gitopt.c b/gitopt.c\nindex 9e85247..139cd2d 100644\n--- a/gitopt.c\n+++ b/gitopt.c\n@@ -270,17 +270,9 @@ static const char * parse_bundled(struct\n        } else if (s->arg_fl) {\n                if (*cur) {\n                        /* no space between the arg and opt switch: */\n-                       if (s->arg_fl & ARG_IS_INT) {\n-                               /* we know to handle stuff like:\n-                                * -h24w80 => -h=24 -w=80 */\n-                               char *endptr = (char *)cur;\n-                               strtol(cur, &endptr, 10);\n-\n-                               while (cur < endptr)\n-                                       *c++ = *cur++;\n-                       } else if (s->arg_fl & ARG_ONE) {\n-                               /* unfortunately, other args are less\n-                                * clear-cut */\n+                       if (s->arg_fl & ARG_ONE) {\n+                               /* don't attempt further unbundling, extra\n+                                * chars are arguments */\n                                while (*cur)\n                                        *c++ = *cur++;\n                        }\n\n-- \nEric Wong\n"},{"id":"19941","messageId":"7vwtcoyxs2.fsf@assigned-by-dhcp.cox.net","threadId":"4135","inReplyTo":"1147619963765-git-send-email-normalperson@yhbt.net","subject":"Re: [PATCH 4/5] commit: allow --pretty= args to be abbreviated","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-14T23:47:09Z","receivedAt":"2006-05-14T23:47:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Wong <normalperson@yhbt.net> writes:\n\n>  commit.c |   42 +++++++++++++++++++++++++++++-------------\n>  1 files changed, 29 insertions(+), 13 deletions(-)\n\nThis is applicable without the gitopt changes, but I have a\nfeeling that when we think about abbreviations the users would\nexpect the leading substring abbreviation, not strstr().\n\nWhile \"git log --pretty=lle\" or \"git log --pretty=or\" might be\nunambiguous, I think that is trying to be too cute and\nconfusing, especially if somebody picks up that habit by\nwatching others type such a cute abbreviations.\n\nThat comment probably incidentally applies to your bigger\npatches.\n"},{"id":"19945","messageId":"20060515003405.GA5533@localdomain","threadId":"4135","inReplyTo":"7vwtcoyxs2.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] commit: allow --pretty= args to be abbreviated","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-15T00:34:05Z","receivedAt":"2006-05-15T00:34:05Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Unlike the original one, this one only does prefix matches, so\nyou can't do --pretty=er anymore :)\n\nThis one really works with and without the gitopt changes.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\nJunio C Hamano <junkio@cox.net> wrote:\n> Eric Wong <normalperson@yhbt.net> writes:\n> \n> >  commit.c |   42 +++++++++++++++++++++++++++++-------------\n> >  1 files changed, 29 insertions(+), 13 deletions(-)\n> \n> This is applicable without the gitopt changes, but I have a\n> feeling that when we think about abbreviations the users would\n> expect the leading substring abbreviation, not strstr().\n> \n> While \"git log --pretty=lle\" or \"git log --pretty=or\" might be\n> unambiguous, I think that is trying to be too cute and\n> confusing, especially if somebody picks up that habit by\n> watching others type such a cute abbreviations.\n> \n> That comment probably incidentally applies to your bigger\n> patches.\n\nOk.  The current gitopt patch only uses leading substring abbreviations\nby default.  GIT_ABBREV_HARDER needs to be set if you want all the\ncrazyness :)\n\n commit.c |   50 ++++++++++++++++++++++++++++++++++++--------------\n 1 files changed, 36 insertions(+), 14 deletions(-)\n\nd4473559924b1a5ba655cd5d5b18d39f53e25184\ndiff --git a/commit.c b/commit.c\nindex 2717dd8..2753677 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -22,23 +22,45 @@ struct sort_node\n \n const char *commit_type = \"commit\";\n \n+struct cmt_fmt_map {\n+\tconst char *n;\n+\tenum cmit_fmt v;\n+} cmt_fmts[] = {\n+\t{ \"raw\",\tCMIT_FMT_RAW },\n+\t{ \"medium\",\tCMIT_FMT_MEDIUM },\n+\t{ \"short\",\tCMIT_FMT_SHORT },\n+\t{ \"full\",\tCMIT_FMT_FULL },\n+\t{ \"fuller\",\tCMIT_FMT_FULLER },\n+\t{ \"oneline\",\tCMIT_FMT_ONELINE },\n+};\n+\n enum cmit_fmt get_commit_format(const char *arg)\n {\n-\tif (!*arg)\n+\tint i, found;\n+\tsize_t len;\n+\n+\tif (!arg || !*arg)\n \t\treturn CMIT_FMT_DEFAULT;\n-\tif (!strcmp(arg, \"=raw\"))\n-\t\treturn CMIT_FMT_RAW;\n-\tif (!strcmp(arg, \"=medium\"))\n-\t\treturn CMIT_FMT_MEDIUM;\n-\tif (!strcmp(arg, \"=short\"))\n-\t\treturn CMIT_FMT_SHORT;\n-\tif (!strcmp(arg, \"=full\"))\n-\t\treturn CMIT_FMT_FULL;\n-\tif (!strcmp(arg, \"=fuller\"))\n-\t\treturn CMIT_FMT_FULLER;\n-\tif (!strcmp(arg, \"=oneline\"))\n-\t\treturn CMIT_FMT_ONELINE;\n-\tdie(\"invalid --pretty format\");\n+\tif (*arg == '=')\n+\t\targ++;\n+\tfor (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {\n+\t\tif (!strcmp(arg, cmt_fmts[i].n))\n+\t\t\treturn cmt_fmts[i].v;\n+\t}\n+\n+\t/* look for abbreviations */\n+\tlen = strlen(arg);\n+\tfound = -1;\n+\tfor (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {\n+\t\tif (!strncmp(cmt_fmts[i].n, arg, len)) {\n+\t\t\tif (found >= 0)\n+\t\t\t\tdie(\"invalid --pretty format: %s\", arg);\n+\t\t\tfound = i;\n+\t\t}\n+\t}\n+\tif (found >= 0)\n+\t\treturn cmt_fmts[found].v;\n+\tdie(\"invalid --pretty format: %s\", arg);\n }\n \n static struct commit *check_commit(struct object *obj,\n-- \n1.3.2.g58c0\n"},{"id":"20027","messageId":"7vac9jhv66.fsf@assigned-by-dhcp.cox.net","threadId":"4135","inReplyTo":"20060515003405.GA5533@localdomain","subject":"Re: [PATCH] commit: allow --pretty= args to be abbreviated","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-05-15T20:47:45Z","receivedAt":"2006-05-15T20:47:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Wong <normalperson@yhbt.net> writes:\n\n> Unlike the original one, this one only does prefix matches, so\n> you can't do --pretty=er anymore :)\n\nSounds good.  But then you know how long the unique prefix\nare for each candidate, so wouldn't this rather be redundant, I\nwonder?\n\n> +\n> +\t/* look for abbreviations */\n> +\tlen = strlen(arg);\n> +\tfound = -1;\n> +\tfor (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {\n> +\t\tif (!strncmp(cmt_fmts[i].n, arg, len)) {\n> +\t\t\tif (found >= 0)\n> +\t\t\t\tdie(\"invalid --pretty format: %s\", arg);\n> +\t\t\tfound = i;\n> +\t\t}\n> +\t}\n> +\tif (found >= 0)\n> +\t\treturn cmt_fmts[found].v;\n> +\tdie(\"invalid --pretty format: %s\", arg);\n>  }\n\nIt would probably be better to say \"ambiguous\" not \"invalid\" in\nthe die() message.\n"},{"id":"20066","messageId":"20060516132541.GA1066@soma","threadId":"4135","inReplyTo":"7vac9jhv66.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] commit: allow --pretty= args to be abbreviated","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-05-16T13:25:41Z","receivedAt":"2006-05-16T13:25:41Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> Eric Wong <normalperson@yhbt.net> writes:\n> \n> > Unlike the original one, this one only does prefix matches, so\n> > you can't do --pretty=er anymore :)\n> \n> Sounds good.  But then you know how long the unique prefix\n> are for each candidate, so wouldn't this rather be redundant, I\n> wonder?\n\nI just copied the idea for the 2nd for loop from gitopt, since it makes\nmaintenance easier when there are lots of possibilities.  We only have 6\n(soon 7) to worry about for --pretty= here, so hard coding lengths\nprobably makes more sense.\n\n> > +\n> > +\t/* look for abbreviations */\n> > +\tlen = strlen(arg);\n> > +\tfound = -1;\n> > +\tfor (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {\n> > +\t\tif (!strncmp(cmt_fmts[i].n, arg, len)) {\n> > +\t\t\tif (found >= 0)\n> > +\t\t\t\tdie(\"invalid --pretty format: %s\", arg);\n> > +\t\t\tfound = i;\n> > +\t\t}\n> > +\t}\n> > +\tif (found >= 0)\n> > +\t\treturn cmt_fmts[found].v;\n> > +\tdie(\"invalid --pretty format: %s\", arg);\n> >  }\n> \n> It would probably be better to say \"ambiguous\" not \"invalid\" in\n> the die() message.\n\nYes, but only one die() message left now :)\n\n-- \nEric Wong\n\n>From nobody Mon Sep 17 00:00:00 2001\nFrom: Eric Wong <normalperson@yhbt.net>\nDate: Sun, 14 May 2006 17:20:46 -0700\nSubject: [PATCH] commit: allow --pretty= args to be abbreviated\n\nUnlike the original one, this one only does prefix matches, so\nyou can't do --pretty=er anymore :)\n\nThis one really works with and without the gitopt changes.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n\n---\n\n commit.c |   38 ++++++++++++++++++++++++--------------\n 1 files changed, 24 insertions(+), 14 deletions(-)\n\n044dff6523c25e173eb7fb1c5d5c8a8e6ada8fdc\ndiff --git a/commit.c b/commit.c\nindex 2717dd8..4a26070 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -22,23 +22,33 @@ struct sort_node\n \n const char *commit_type = \"commit\";\n \n+struct cmt_fmt_map {\n+\tconst char *n;\n+\tsize_t cmp_len;\n+\tenum cmit_fmt v;\n+} cmt_fmts[] = {\n+\t{ \"raw\",\t1,\tCMIT_FMT_RAW },\n+\t{ \"medium\",\t1,\tCMIT_FMT_MEDIUM },\n+\t{ \"short\",\t1,\tCMIT_FMT_SHORT },\n+\t{ \"full\",\t5,\tCMIT_FMT_FULL },\n+\t{ \"fuller\",\t5,\tCMIT_FMT_FULLER },\n+\t{ \"oneline\",\t1,\tCMIT_FMT_ONELINE },\n+};\n+\n enum cmit_fmt get_commit_format(const char *arg)\n {\n-\tif (!*arg)\n+\tint i;\n+\n+\tif (!arg || !*arg)\n \t\treturn CMIT_FMT_DEFAULT;\n-\tif (!strcmp(arg, \"=raw\"))\n-\t\treturn CMIT_FMT_RAW;\n-\tif (!strcmp(arg, \"=medium\"))\n-\t\treturn CMIT_FMT_MEDIUM;\n-\tif (!strcmp(arg, \"=short\"))\n-\t\treturn CMIT_FMT_SHORT;\n-\tif (!strcmp(arg, \"=full\"))\n-\t\treturn CMIT_FMT_FULL;\n-\tif (!strcmp(arg, \"=fuller\"))\n-\t\treturn CMIT_FMT_FULLER;\n-\tif (!strcmp(arg, \"=oneline\"))\n-\t\treturn CMIT_FMT_ONELINE;\n-\tdie(\"invalid --pretty format\");\n+\tif (*arg == '=')\n+\t\targ++;\n+\tfor (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {\n+\t\tif (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len))\n+\t\t\treturn cmt_fmts[i].v;\n+\t}\n+\n+\tdie(\"invalid --pretty format: %s\", arg);\n }\n \n static struct commit *check_commit(struct object *obj,\n-- \n1.3.2.g7d11\n"}]}