* [PATCH/RFC] gitopt - command-line parsing enhancements
@ 2006-05-09 5:06 Eric Wong
2006-05-09 5:06 ` [PATCH 1/6] gitopt: a new command-line option parser for git Eric Wong
2006-05-09 8:35 ` [PATCH/RFC] gitopt - command-line parsing enhancements Junio C Hamano
0 siblings, 2 replies; 22+ messages in thread
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git
Here's my take at a new command-line option parser to reduce wear on my
fingers. It handles both long and short options, permuting, automatic
abbreviations, required arguments, optional arguments, and bundling.
It doesn't use getopt or argp, so it should be easily portable to
non-GNU systems (and only uses git-isms trivially, so other programs can
use it easily, too).
I've tested everything lightly but it seems to be working well. The
unit test was very helpful throughout.
More intensive testing, bugfixes and comments would be greatly
appreciated.
It should be pretty easy to convert other git-* programs to use gitopt,
and write shell wrappers for git.c like I did with am/checkout/commit.
I'll probably do so in a few days once my fingers recover a bit, of
course feedback and patches for bug reports/enhancements are encouraged,
too.
1 - gitopt: a new command-line option parser for git
This adds the parser, tests, and some simple changes to
the git.c wrapper.
2 - update-index: convert to using gitopt
3 - ls-tree: convert to gitopt
4 - ls-files: convert to using gitopt
These three are fairly straightforward conversions.
I was somewhat disappointed that update-index didn't take
kindly to permuting arguments, but it's also a good example
how to disable permuting via gitopt: a == b
5 - gitopt: convert setup_revisions(), and diff_opt_parse()
This is a fairly intrusive change that affects several
important programs. All the tests still pass, so it must be
working :)
6 - commit: allow --pretty= args to be abbreviated
This one isn't strictly dependent on gitopt, and can be
trivially changed to work without the parent patches.
--
Eric Wong
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 5:06 [PATCH/RFC] gitopt - command-line parsing enhancements Eric Wong
@ 2006-05-09 5:06 ` Eric Wong
2006-05-09 5:06 ` [PATCH 2/6] update-index: convert to using gitopt Eric Wong
2006-05-09 9:08 ` [PATCH 1/6] gitopt: a new command-line option parser for git Timo Hirvonen
2006-05-09 8:35 ` [PATCH/RFC] gitopt - command-line parsing enhancements Junio C Hamano
1 sibling, 2 replies; 22+ messages in thread
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
It was initially conceived as an addition to the git.c wrapper,
and not affect other programs. But it turns out existing C
programs can use it pretty easily, too.
Features include:
* getopt-style permuting (can easily be disabled for
things like update-index)
* command-line compatibile with existing usage:
-S=pickaxe-arg-with-leading-equals is unchanged
* printf-style rewriting (for front-ending shell scripts)
* unbundling of short options: -uC20n20z => -u -C20 -n20 -z
* automatically understands unambiguous abbreviations
* optional argument handling (-C<num>, -M<num>)
-C <num> (with a space between them) has not changed,
however, <num> can be a sha1, or a path
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
.gitignore | 1
Makefile | 10 +
git.c | 11 +
gitopt.c | 603 ++++++++++++++++++++++++++++++++++++++++++++++++++
gitopt.h | 142 ++++++++++++
gitopt/git_wrapper.h | 45 ++++
gitopt/sh.h | 45 ++++
t/t0200-gitopt.sh | 280 +++++++++++++++++++++++
test-gitopt.c | 112 +++++++++
9 files changed, 1244 insertions(+), 5 deletions(-)
create mode 100644 gitopt.c
create mode 100644 gitopt.h
create mode 100644 gitopt/git_wrapper.h
create mode 100644 gitopt/sh.h
create mode 100755 t/t0200-gitopt.sh
create mode 100644 test-gitopt.c
083777c55feab0b68b859f8cb0d5bcf105db4ef5
diff --git a/.gitignore b/.gitignore
index b5959d6..b2d8b06 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,6 +123,7 @@ git-write-tree
git-core-*/?*
test-date
test-delta
+test-gitopt
common-cmds.h
*.tar.gz
*.dsc
diff --git a/Makefile b/Makefile
index 45484fc..46f2401 100644
--- a/Makefile
+++ b/Makefile
@@ -197,7 +197,7 @@ LIB_H = \
blob.h cache.h commit.h csum-file.h delta.h \
diff.h object.h pack.h pkt-line.h quote.h refs.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
- tree-walk.h log-tree.h
+ tree-walk.h log-tree.h gitopt.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -212,6 +212,7 @@ LIB_OBJS = \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
+ gitopt.o \
$(DIFF_OBJS)
BUILTIN_OBJS = \
@@ -470,6 +471,8 @@ all:
strip: $(PROGRAMS) git$X
$(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
+gitopt.o: gitopt.c gitopt.h gitopt/*.h
+
git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS)
$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
$(ALL_CFLAGS) -o $@ $(filter %.c,$^) \
@@ -600,7 +603,7 @@ # with that.
export NO_PYTHON
-test: all
+test: all test-gitopt$X
$(MAKE) -C t/ all
test-date$X: test-date.c date.o ctype.o
@@ -609,6 +612,9 @@ test-date$X: test-date.c date.o ctype.o
test-delta$X: test-delta.c diff-delta.o patch-delta.o
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ -lz
+test-gitopt$X: test-gitopt.c gitopt.o ctype.o usage.o
+ $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
+
check:
for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
diff --git a/git.c b/git.c
index 49ba518..785d97f 100644
--- a/git.c
+++ b/git.c
@@ -11,6 +11,7 @@ #include <stdarg.h>
#include "git-compat-util.h"
#include "exec_cmd.h"
+#include "gitopt/git_wrapper.h"
#include "builtin.h"
static void prepend_to_path(const char *dir, int len)
@@ -72,6 +73,7 @@ int main(int argc, const char **argv, ch
char *slash = strrchr(cmd, '/');
char git_command[PATH_MAX + 1];
const char *exec_path = NULL;
+ struct exec_args *ea;
/*
* Take the basename of argv[0] as the command
@@ -99,7 +101,8 @@ int main(int argc, const char **argv, ch
if (!strncmp(cmd, "git-", 4)) {
cmd += 4;
argv[0] = cmd;
- handle_internal_command(argc, argv, envp);
+ ea = gitopt_parse_git(argc, argv);
+ handle_internal_command(ea->argc, ea->argv, envp);
die("cannot handle %s internally", cmd);
}
@@ -144,6 +147,8 @@ int main(int argc, const char **argv, ch
}
argv[0] = cmd;
+ ea = gitopt_parse_git(argc, argv);
+
/*
* We search for git commands in the following order:
* - git_exec_path()
@@ -157,10 +162,10 @@ int main(int argc, const char **argv, ch
prepend_to_path(exec_path, strlen(exec_path));
/* See if it's an internal command */
- handle_internal_command(argc, argv, envp);
+ handle_internal_command(ea->argc, ea->argv, envp);
/* .. then try the external ones */
- execv_git_cmd(argv);
+ execv_git_cmd(ea->argv);
if (errno == ENOENT)
cmd_usage(0, exec_path, "'%s' is not a git-command", cmd);
diff --git a/gitopt.c b/gitopt.c
new file mode 100644
index 0000000..056e163
--- /dev/null
+++ b/gitopt.c
@@ -0,0 +1,603 @@
+#include "git-compat-util.h"
+#include "gitopt.h"
+#include "cache.h"
+
+/* whether or not to pass-through unrecognized switches or to report an error.
+ * This is only intended to be set to true for use in the git.c wrapper */
+int gitopt_pass_through = 0;
+static int gitopt_errno = 0;
+int gitopt_dd_seen = 0; /* double-dash seen flag */
+
+void (*gitopt_non_option_cb)(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos);
+
+void gitopt_default_non_option_cb(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos)
+{
+ b->argv[b->argc++] = argv[*argc_pos];
+}
+
+struct exec_args *new_exec_args(const int argc)
+{
+ struct exec_args *ea = xmalloc(sizeof(*ea));
+ ea->argc = argc;
+ ea->argv = xcalloc((argc+1), sizeof(char*));
+
+ return ea;
+}
+
+struct exec_args *nil_exec_args(struct exec_args *ea)
+{
+ int i;
+ for (i = 0; i < ea->argc; i++)
+ ea->argv[i] = NULL;
+ ea->argc = 0;
+ return ea;
+}
+
+static int combine_exec_args(struct exec_args *dest, struct exec_args *from)
+{
+ int i;
+
+ dest->argv = xrealloc(dest->argv,
+ (2 + dest->argc + from->argc)*sizeof(char*));
+
+ for (i = 0; i < from->argc; i++)
+ dest->argv[dest->argc++] = from->argv[i];
+
+ dest->argv[dest->argc] = NULL;
+
+ return i;
+}
+
+static struct exec_args *rewrite_args(const char *rewrite_fmt, const char *arg)
+{
+ struct exec_args *ea;
+ size_t len = strlen(rewrite_fmt) + (arg ? strlen(arg) : 0);
+ char *dest = xmalloc(len); /* don't free this */
+ int nr_ws = 0;
+ char *a, *b;
+
+ if (!arg) {
+ strcpy(dest, rewrite_fmt);
+ if ((a = strstr(dest,"=%s")) || (a = strstr(dest," %s")))
+ a[0] = a[1] = a[2] = '\0';
+ else if ((a = strstr(dest,"%s")))
+ a[0] = a[1] = '\0';
+ } else {
+ const char *c = rewrite_fmt;
+
+ do { if (isspace(*c++)) nr_ws++; } while (*c);
+ snprintf(dest, len, rewrite_fmt, arg);
+ }
+
+ ea = new_exec_args(1 + nr_ws);
+ a = b = dest;
+
+ for (ea->argc = 0; nr_ws && *b; b++) {
+ if (isspace(*b)) {
+ *b = '\0';
+ if (strlen(a))
+ ea->argv[ea->argc++] = a;
+ a = b + 1;
+ }
+ }
+ if (strlen(a))
+ ea->argv[ea->argc++] = a;
+ ea->argv[ea->argc] = NULL;
+
+ return ea;
+}
+
+struct exec_args *one_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos)
+{
+ const char *c = argv[*argc_pos];
+ struct exec_args *ea = NULL;
+ const char *a = NULL;
+ size_t l_len = s->l ? strlen(s->l) : 0;
+ int so = 0; /* short option passed flag */
+
+ if (*c == '-') {
+ if (s->s && c[1] != '-') {
+ if (isspace(s->s)) {
+ a = c + 1;
+ so = 1;
+ } else if (c[1] == s->s) {
+ a = c + 2;
+ so = 1;
+ }
+ } else if (s->l && c[1] == '-' && !strncmp(c+2, s->l, l_len))
+ a = c + 2 + l_len;
+ }
+
+ if (!a)
+ return NULL;
+
+ switch(a[0]) {
+ case '\0':
+ if (((*argc_pos + 1) < argc) && (a = argv[*argc_pos + 1])) {
+ /* optional arguments must be attached to the switch
+ * so that there are no abiguities */
+ if (s->arg_fl & ARG_IS_OPT)
+ break;
+ *argc_pos += 1;
+ if (s->rewrite_fmt)
+ ea = rewrite_args(s->rewrite_fmt, a);
+ else {
+ ea = new_exec_args(2);
+ ea->argv[0] = c;
+ ea->argv[1] = a;
+ }
+ }
+ break;
+ case '=': /* only long options get to use "=" to denote an arg */
+ if (!so) {
+ if (s->rewrite_fmt)
+ ea = rewrite_args(s->rewrite_fmt, a + 1);
+ else {
+ ea = new_exec_args(1);
+ ea->argv[0] = c;
+ }
+ break;
+ }
+ /* fall-through, so that -S'= assigned_val;' still works */
+ default:
+ /* -s<a> (short opts only), long opts are ambiguous
+ * w/o '=' or a space separating the opt */
+ if (!so)
+ break;
+ if (s->rewrite_fmt)
+ ea = rewrite_args(s->rewrite_fmt, a);
+ else {
+ ea = new_exec_args(1);
+ ea->argv[0] = c;
+ }
+ }
+
+ return ea;
+}
+
+static struct exec_args *optional_arg_common(struct exec_args *ea,
+ const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos)
+{
+ if (!ea) {
+ if (s->rewrite_fmt)
+ ea = rewrite_args(s->rewrite_fmt, NULL);
+ else {
+ ea = new_exec_args(1);
+ ea->argv[0] = argv[*argc_pos];
+ }
+ }
+ return ea;
+}
+
+struct exec_args *optional_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos)
+{
+ return optional_arg_common( one_arg(s, argc, argv, argc_pos),
+ s, argc, argv, argc_pos);
+}
+
+struct exec_args *int_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos)
+{
+ struct exec_args *ea = one_arg(s, argc, argv, argc_pos);
+
+ if (ea) {
+ const char *c = ea->argv[ea->argc - 1];
+ char *endptr;
+ long int tmp;
+
+ /* -C<num>: */
+ if (ea->argc == 1) {
+ while (*c && !isdigit(*c)) c++;
+ if (!c) goto err;
+ }
+
+ endptr = (char *)c;
+ tmp = strtol(c, &endptr, 10);
+ if (endptr == c) {
+err:
+ if (s->arg_fl & ARG_IS_INT)
+ (*argc_pos)--;
+ free_exec_args(ea);
+ return NULL;
+ }
+ }
+ return ea;
+}
+
+struct exec_args *optional_int_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos)
+{
+ return optional_arg_common( int_arg(s, argc, argv, argc_pos),
+ s, argc, argv, argc_pos);
+}
+
+static struct exec_args *run_proc(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos)
+{
+ if (s->fn)
+ return s->fn(s, argc, argv, argc_pos);
+ switch (s->arg_fl) {
+ case ARG_ONE:
+ return one_arg(s, argc, argv, argc_pos);
+ case ARG_INT:
+ return int_arg(s, argc, argv, argc_pos);
+ case ARG_OPT:
+ return optional_arg(s, argc, argv, argc_pos);
+ case ARG_OPTINT:
+ return optional_int_arg(s, argc, argv, argc_pos);
+ default:
+ return NULL;
+ }
+}
+
+static const char * parse_bundled(struct exec_args *dest,
+ const struct opt_spec *s, const char *cur,
+ const int argc, const char **argv, int *arg_pos)
+{
+ struct exec_args *ea = NULL;
+ const char *orig = cur;
+ char *c = xmalloc(strlen(cur) + 2); /* don't free this */
+ const char *tmp_argv[] = { c };
+ int i = 0;
+
+ *c++ = '-';
+ *c++ = *cur++;
+ if (!s || (!s->arg_fl && !s->fn)) {
+ ea = new_exec_args(1);
+ if (s && s->rewrite_fmt)
+ ea->argv[0] = s->rewrite_fmt;
+ else {
+ ea->argv[0] = tmp_argv[0];
+ *c = '\0';
+ }
+ } else if (s->arg_fl || s->fn) {
+ if (*cur) {
+ /* no space between the arg and opt switch: */
+ if (s->arg_fl & ARG_IS_INT) {
+ /* we know to handle stuff like:
+ * -h24w80 => -h=24 -w=80 */
+ char *endptr = (char *)cur;
+ strtol(cur, &endptr, 10);
+
+ while (cur < endptr)
+ *c++ = *cur++;
+ } else if (s->arg_fl & ARG_ONE) {
+ /* unfortunately, other args are less
+ * clear-cut */
+ while (*cur)
+ *c++ = *cur++;
+ }
+ *c = '\0';
+ ea = run_proc(s, 1, tmp_argv, &i);
+ } else if ((*arg_pos + 1) < argc) {
+ int j = *arg_pos;
+ int x = argc - j;
+ const char **argv2 = xmalloc(x * sizeof(char *));
+
+ *c = '\0';
+ argv2[i++] = tmp_argv[0];
+ while (i <= x) argv2[i++] = argv[++j];
+
+ i = 0;
+ ea = run_proc(s, x, argv2, &i);
+ *arg_pos += i;
+ free(argv2);
+ }
+ }
+ if (ea) {
+ combine_exec_args(dest, ea);
+ free_exec_args(ea);
+ } else
+ gitopt_errno = error("Failed to parse bundled arguments in: %s",
+ orig);
+ return cur;
+}
+
+static void unbundle(struct exec_args *dest, const char *cur,
+ const struct opt_spec *ost,
+ const struct opt_spec *cur_spec,
+ const int argc, const char **argv, int *arg_pos)
+{
+ struct exec_args *ea = new_exec_args(strlen(cur));
+ const struct opt_spec *s;
+ const char *c = cur;
+
+ ea->argc = 0;
+
+ while (*c) {
+ int i;
+ for (i = 0; ost[i].s || ost[i].l; i++) {
+ s = ost + i;
+ if (!s->s || (!isspace(s->s) && s->s != *c))
+ continue;
+ c = parse_bundled(ea, s, c, argc, argv, arg_pos);
+ break;
+ }
+ if (ost[i].l || ost[i].s) continue;
+ /* pass-through while unbundling and creating switches:
+ * this means that if we see -abc here, but we only
+ * had -a defined in ost (-a defined to not accept args),
+ * then we'd create switches
+ * for -b and -c here (since we already knew -a)
+ * and we're assuming -b and -c were just forgotten
+ * in the ost. If we had gotten -bac, that would
+ * be passed through as -bac in gitopt_parse_ost()
+ * as an unknown option if -b is undefined in the ost
+ */
+ if (gitopt_pass_through)
+ c = parse_bundled(ea, NULL, c, argc, argv, arg_pos);
+ else {
+ /* continue for now, a higher-up function will work
+ * with the error */
+ gitopt_errno = error("Unknown option '%s' in '%s'",
+ c,argv[*arg_pos]);
+ c++;
+ }
+ }
+ combine_exec_args(dest, ea);
+ free_exec_args(ea);
+}
+
+static void push_one_opt(struct exec_args *dest, const struct opt_spec *s,
+ const int argc, const char **argv, int *i)
+{
+ struct exec_args *ea;
+
+ if (!s->arg_fl && !s->fn) {
+ dest->argv[dest->argc++] = s->rewrite_fmt ? s->rewrite_fmt
+ : argv[*i];
+ return;
+ }
+
+ if ((ea = run_proc(s,argc,argv,i))) {
+ combine_exec_args(dest, ea);
+ free_exec_args(ea);
+ } else
+ gitopt_errno = error("Failed to parse arguments for: %s %s",
+ argv[*i], argv[*i+1]?argv[*i+1]:"");
+}
+
+/* look for a prefix abbreviation */
+static int opt_abbrev_match(const struct opt_spec *s, const char *p)
+{
+ const char *l = s->l;
+
+ while (*p) {
+ if (*l++ != *p++) return 0;
+ if (!*p || (s->arg_fl && *p == '=')) return 1;
+ }
+
+ return 0;
+}
+
+/* match a short option switch */
+static int opt_char_match(const struct opt_spec *s, const char *p)
+{
+ return ((s->s == p[0]) && ((!s->arg_fl && p[1] == '\0')
+ ||
+ (s->arg_fl && (p[1] == '\0' || p[1] == '='))));
+}
+
+/* tokenize on '-' and look for a prefix abbreviation match */
+static int opt_token_match(const struct opt_spec *s, const char *p0)
+{
+ const char *l = s->l;
+ const char *p;
+
+ while ((l = strchr(l,'-'))) {
+ l++;
+ p = p0;
+ while (*p) {
+ if (*l++ != *p++) break;
+ if (!*p || (s->arg_fl && *p == '=')) return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* look for unambigious abbreviated switches, if it can't be found,
+ * assume it's a non-option and pass it to b */
+static void fallback_long(const struct opt_spec *ost,
+ struct exec_args *a, struct exec_args *b,
+ const int argc, const char **argv,
+ const char *cur, int *argc_pos)
+{
+ const struct opt_spec *s;
+ int i, found = -1;
+
+ /* look for abbreviations: */
+ for (i = 0; ost[i].l || ost[i].s; i++) {
+ s = &(ost[i]);
+ /* maybe they wanted to use a short option
+ * (normally a single-dash) but typed two dashes instead.
+ * note: even if we find a short option here, we do not
+ * attempt to unbundle in this case */
+ if ((s->s && opt_char_match(s, cur)) ||
+ (s->l && opt_abbrev_match(s,cur))) {
+ if (found >= 0)
+ goto pass_through;
+ found = i;
+ }
+ }
+
+ /* ok, try harder, based on tokenization on '-' */
+ if (found < 0) {
+ for (i = 0; ost[i].l || ost[i].s; i++) {
+ s = &(ost[i]);
+ if (s->l && opt_token_match(s,cur)) {
+ if (found >= 0)
+ goto pass_through;
+ found = i;
+ }
+ }
+ }
+ /* should I add a strstr() matcher here for the desperate? */
+
+ /* rewrite the abbreviated switch in it's unabbreviated form: */
+ if (found >= 0) {
+ char *tmp;
+ size_t len = 3 + strlen(cur); /* --cur=potential_args\0 */
+ size_t l_len;
+
+ s = &(ost[found]);
+ l_len = s->l ? strlen(s->l) : 0; /* favor long options */
+ tmp = xcalloc(len + l_len, 1); /* don't free this */
+ tmp[0] = '-';
+
+ if (s->l) {
+ tmp[1] = '-';
+ memcpy(tmp + 2, s->l, l_len);
+ } else
+ tmp[1] = s->s;
+ if (s->arg_fl) {
+ const char *c;
+ if ((c = strchr(cur,'='))) {
+ if (!l_len) c++; /* skip '=' for short opts */
+ strcpy(tmp + 2 + l_len, c);
+ }
+ }
+
+ argv[*argc_pos] = tmp;
+ push_one_opt(a, s, argc, argv, argc_pos);
+ return;
+ }
+
+pass_through:
+ gitopt_non_option_cb(b, argc, argv, argc_pos);
+ if (!gitopt_pass_through)
+ gitopt_errno = error("Unknown option: '%s'",argv[*argc_pos]);
+}
+
+static int opt_complete_match(const struct opt_spec *s, const char *p)
+{
+ if (s->arg_fl) {
+ size_t len = strlen(s->l);
+
+ return (!strncmp(s->l,p,len) &&
+ (p[len] == '\0' || (p[len] == '=')));
+ }
+ return !strcmp(s->l,p);
+}
+
+/* You should really only use this in git (wrapper) and test-gitopt: */
+struct exec_args *gitopt_parse_ost(const struct opt_spec *ost,
+ const int argc, const char **argv)
+{
+ struct exec_args *a = new_exec_args(argc); /* argv[0] and options: */
+ struct exec_args *b = new_exec_args(argc); /* non-option args */
+
+ gitopt_pass_through = 1;
+
+ if (gitopt_parse_ost_split(a, b, ost, argc, argv) < 0)
+ die("gitopt argument parsing failed");
+ combine_exec_args(a, b);
+ free_exec_args(b);
+
+ return a;
+}
+
+int gitopt_verify_b_args(const struct exec_args *b)
+{
+ const char **arg;
+
+ for (arg = b->argv; *arg; arg++) {
+ /* anything goes after a double dash */
+ if (!memcmp("--",*arg,3))
+ return 1;
+ if (*arg[0] == '-')
+ return 0;
+ }
+ return 1;
+}
+
+void gitopt_parse_one_opt(struct exec_args *a, struct exec_args *b,
+ const struct opt_spec *ost,
+ const int argc, const char **argv, int *argc_pos)
+{
+ const char *c = argv[*argc_pos];
+ const struct opt_spec *s;
+ int i;
+
+ if (!gitopt_dd_seen && !memcmp("--",c,3)) {
+ gitopt_dd_seen = 1;
+ if (!gitopt_pass_through)
+ return;
+ }
+ if (gitopt_dd_seen) {
+ gitopt_non_option_cb(b, argc, argv, argc_pos);
+ return;
+ }
+ if (!memcmp("--",c,2)) { /* long options */
+ c += 2;
+ for (i = 0; ost[i].l || ost[i].s; i++) {
+ s = &(ost[i]);
+ if (!s->l || !opt_complete_match(s, c))
+ continue;
+ push_one_opt(a, s, argc, argv, argc_pos);
+ return;
+ }
+ if (ost[i].l || ost[i].s) return;
+ /* undefined --option: */
+ fallback_long(ost, a, b, argc, argv, c, argc_pos);
+ return;
+ }
+ if ((c[0] == '-') && (c[1] != '-')) { /* short option */
+ c++;
+ for (i = 0; ost[i].l || ost[i].s; i++) {
+ s = &(ost[i]);
+ if (!s->s) continue;
+ if (isspace(s->s) && (*c == '\0' || isdigit(*c))) {
+ /* special case for -<num> */
+ push_one_opt(a, s, argc, argv, argc_pos);
+ return;
+ }
+ if (s->s != *c) continue;
+ if ((c[1] != '\0') && (c[1] != '='))
+ unbundle(a, c, ost, s, argc, argv, argc_pos);
+ else
+ push_one_opt(a, s, argc, argv, argc_pos);
+ return;
+ }
+ if (ost[i].l || ost[i].s) return;
+ /* undefined: */
+ if (gitopt_pass_through)
+ gitopt_non_option_cb(b, argc, argv, argc_pos);
+ else
+ gitopt_errno = error("Unknown option: %s",
+ argv[*argc_pos]);
+ return;
+ }
+ gitopt_non_option_cb(b, argc, argv, argc_pos);
+}
+
+int gitopt_parse_ost_split(struct exec_args *a, struct exec_args *b,
+ const struct opt_spec *ost,
+ const int argc, const char **argv)
+{
+ int i;
+
+ if (!gitopt_non_option_cb)
+ gitopt_non_option_cb = gitopt_default_non_option_cb;
+
+ gitopt_dd_seen = 0;
+ b->argc = 0;
+ a->argv[0] = argv[0];
+ a->argc = 1;
+
+ for (i = 1; i < argc; i++)
+ gitopt_parse_one_opt(a, b, ost, argc, argv, &i);
+ return gitopt_errno;
+}
+
+void free_exec_args(struct exec_args *ea)
+{
+ free(ea->argv);
+ free(ea);
+}
+
diff --git a/gitopt.h b/gitopt.h
new file mode 100644
index 0000000..78c9e5d
--- /dev/null
+++ b/gitopt.h
@@ -0,0 +1,142 @@
+#ifndef GITOPT_H
+#define GITOPT_H
+
+/* gitopt_* functions will return this structure
+ * the elements in this struct can then be treated just
+ * like their counterparts from main(). */
+struct exec_args {
+ const char **argv;
+ int argc;
+};
+
+/* @l: long option string (without the leading "--")
+ *
+ * @s: single option char, ' ' has a special meaning for accepting a
+ * single '-' (dash), which can also accept an integer argument This is
+ * for things like "-5" => "--max-count=5" or "-" => "--stdin"
+ *
+ * @rewrite_fmt: rewrite the passed argument(s) (if any) into this
+ * (*printf) style string. Only a single %s can be accepted and handled
+ * by the default fn() handlers included in gitopt.
+ *
+ * Do not use this if you need to use more than one "%s", you'll need to
+ * define and use a custom fn(). rewrite_fmt is only intended for
+ * the common argument rewriting cases.
+ *
+ * If rewrite_fmt has a "%s", " %s" or "=%s" in it, it will be stripped
+ * out if no arguments are passed to it (this can be the case where
+ * fn() (see below) is defined to optional_arg).
+ *
+ * Any single space between non-space characters will be interpreted as
+ * break in the option and the options will be split out into seperate
+ * elements in argv.
+ *
+ * @fn: define this to the function you wish to use for handling any
+ * arguments, or cases where rewrite_fmt is too limited. You can define
+ * your own functions and use it here, of course. The gitopt_eat*
+ * macros provide convenient ways to write functions for this member.
+ * Set this to 0/NULL if you have no args to parse.
+ */
+struct opt_spec {
+ const char *l;
+ const char s;
+ const char *rewrite_fmt;
+ unsigned int arg_fl;
+ struct exec_args *(*fn)(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+};
+
+/* internal use: */
+#define ARG_IS_INT 0x08
+#define ARG_IS_OPT 0x10
+
+/* use these for opt_spec flags: */
+#define ARG_NIL 0x00
+#define ARG_ONE 0x01
+#define ARG_TWO 0x02 /* not really supported yet */
+#define ARG_THREE 0x04 /* not really supported yet */
+#define ARG_TRE ARG_THREE
+#define ARG_INT (ARG_ONE | ARG_IS_INT)
+#define ARG_OPT (ARG_ONE | ARG_IS_OPT)
+#define ARG_OPTINT (ARG_ONE | ARG_IS_OPT | ARG_IS_INT)
+
+extern int gitopt_pass_through;
+extern int gitopt_dd_seen; /* double-dash seen flag */
+void (*gitopt_non_option_cb)(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos);
+void gitopt_default_non_option_cb(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos);
+struct exec_args *new_exec_args(const int argc);
+void free_exec_args(struct exec_args *ea);
+struct exec_args *nil_exec_args(struct exec_args *ea);
+
+/* You should really only use this in the git wrapper or tests: */
+struct exec_args *gitopt_parse_ost(const struct opt_spec *ost,
+ const int argc, const char **argv);
+
+/* Most C programs should use this internally. Returns -1 on error */
+int gitopt_parse_ost_split(struct exec_args *a, struct exec_args *b,
+ const struct opt_spec *ost,
+ const int argc, const char **argv);
+
+# define gitopt_eat(func,code_block) \
+static struct exec_args *func(const struct opt_spec *s, \
+ const int argc, const char **argv, int *argc_pos) \
+{ \
+ code_block \
+ return new_exec_args(0); /* return non-NULL if no errors */ \
+}
+
+# define gitopt_eat_one_arg(func,code_block) \
+static struct exec_args *func(const struct opt_spec *s, \
+ const int argc, const char **argv, int *argc_pos) \
+{ \
+ struct exec_args *ea = one_arg(s, argc, argv, argc_pos); \
+ if (!ea) return NULL; \
+ code_block \
+ return nil_exec_args(ea); /* return non-NULL if no errors */ \
+}
+#define gitopt_eat_arg(func,code_block) gitopt_eat_one_arg(func,code_block)
+
+# define gitopt_eat_int(func,code_block) \
+static struct exec_args *func(const struct opt_spec *s, \
+ const int argc, const char **argv, int *argc_pos) \
+{ \
+ struct exec_args *ea = one_arg(s, argc, argv, argc_pos); \
+ if (!ea) return NULL; \
+ code_block \
+ return nil_exec_args(ea); /* return non-NULL if no errors */ \
+}
+
+# define gitopt_eat_opt_int(func,code_block) \
+static struct exec_args *func(const struct opt_spec *s, \
+ const int argc, const char **argv, int *argc_pos) \
+{ \
+ struct exec_args *ea = optional_int_arg(s, argc, argv, argc_pos); \
+ code_block \
+ return nil_exec_args(ea); /* return non-NULL if no errors */ \
+}
+
+/* used for debugging */
+static inline void dump_ea(const char *pfx, struct exec_args *ea)
+{
+ const char **arg;
+ int i = 0;
+ for (arg = ea->argv; *arg; arg++)
+ fprintf(stderr,"[%d] %s: %s\n",i++,pfx,*arg);
+}
+
+void gitopt_parse_one_opt(struct exec_args *a, struct exec_args *b,
+ const struct opt_spec *ost,
+ const int argc, const char **argv, int *argc_pos);
+void gitopt_set_pass_through(); /* should only be used by git.c wrapper */
+
+struct exec_args *one_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+struct exec_args *int_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+struct exec_args *optional_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+struct exec_args *optional_int_arg(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos);
+#endif /* GITOPT_H */
diff --git a/gitopt/git_wrapper.h b/gitopt/git_wrapper.h
new file mode 100644
index 0000000..5f27bf4
--- /dev/null
+++ b/gitopt/git_wrapper.h
@@ -0,0 +1,45 @@
+/* opt_spec table mappings for the git.c wrapper */
+
+#include "../gitopt.h"
+#include "../gitopt/sh.h"
+
+static const struct opt_spec ost_null[] = { { 0, 0 } };
+
+static const struct opt_spec_map {
+ const char *cmd;
+ const struct opt_spec *ost;
+} opt_specs[] = {
+ { "checkout", ost_checkout },
+ { "commit", ost_commit },
+ { "am", ost_am},
+};
+
+static const struct opt_spec *find_cmd_ost(const int argc, const char **argv)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(opt_specs); i++) {
+ const char *cmd = opt_specs[i].cmd;
+ if (strcmp(cmd, *argv))
+ continue;
+ return opt_specs[i].ost;
+ }
+ return NULL;
+}
+
+static struct exec_args *gitopt_parse_git(const int argc, const char **argv)
+{
+ const struct opt_spec *ost;
+
+ if (!strcmp(argv[0],"help") || !strcmp(argv[0],"version") ||
+ !(ost = find_cmd_ost(argc, argv))) {
+ struct exec_args *ea = new_exec_args(argc);
+ int i;
+
+ for (i = 0; i <= argc; i++) /* argv[argc] == NULL */
+ ea->argv[i] = argv[i];
+
+ return ea;
+ }
+ return gitopt_parse_ost(ost, argc, argv);
+}
diff --git a/gitopt/sh.h b/gitopt/sh.h
new file mode 100644
index 0000000..0ce6620
--- /dev/null
+++ b/gitopt/sh.h
@@ -0,0 +1,45 @@
+#ifndef GITOPT_SH_H
+#define GITOPT_SH_H
+
+/* opt_spec tables for some git programs written in shell that don't
+ * have too many options */
+
+static const struct opt_spec ost_am[] = {
+ { "dotest", 'd', 0, ARG_ONE, 0 },
+ { "interactive", 'i', 0, 0, 0 },
+ { "binary", 'b', 0, 0, 0 },
+ { "3way", '3', 0, 0, 0 },
+ { "signoff", 's', 0, 0, 0 },
+ { "utf8", 'u', 0, 0, 0 },
+ { "keep", 'k', 0, 0, 0 },
+ { "resolved", 'r', 0, 0, 0 },
+ { "skip", 0, 0, 0, 0 },
+ { "whitespace", 0, 0, ARG_ONE, 0 },
+ { 0, 0 }
+};
+
+static const struct opt_spec ost_checkout[] = {
+ { 0, 'f', 0, 0, 0 },
+ { 0, 'm', 0, 0, 0 },
+ { 0, 'b', "-b %s", ARG_ONE, 0 },
+ { 0, 0 }
+};
+
+static const struct opt_spec ost_commit[] = {
+ { "file", 'F', 0, ARG_ONE, 0 },
+ { "all", 'a', 0, 0, 0 },
+ { "author", 0, 0, ARG_ONE, 0 },
+ { "edit", 'e', 0, 0, 0 },
+ { "include", 'i', 0, 0, 0 },
+ { "only", 'o', 0, 0, 0 },
+ { "message", 'm', 0, ARG_ONE, 0 },
+ { "no-verify", 'n', 0, 0, 0 },
+ { "amend", 0, 0, 0, 0 },
+ { "reedit", 'c', 0, ARG_ONE, 0 },
+ { "reuse-message", 'C', 0, ARG_ONE, 0 },
+ { "signoff", 's', 0, 0, 0 },
+ { "verbose", 'v', 0, 0, 0 },
+ { 0, 0 }
+};
+
+#endif /* GITOPT_SH_H */
diff --git a/t/t0200-gitopt.sh b/t/t0200-gitopt.sh
new file mode 100755
index 0000000..6da686c
--- /dev/null
+++ b/t/t0200-gitopt.sh
@@ -0,0 +1,280 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='gitopt command-line pa{ss,rs}er'
+
+. ./test-lib.sh
+
+cat > expect <<EOF
+00 'commit'
+01 '(null)'
+EOF
+test_expect_success 'single command' \
+ 'test-gitopt commit > output && cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '(null)'
+EOF
+test_expect_success 'simple command with switches' \
+ 'test-gitopt commit -a -s > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '(null)'
+EOF
+test_expect_success 'command with bundled switches' \
+ 'test-gitopt commit -as > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '-mhello world'
+04 '(null)'
+EOF
+test_expect_success 'bundle with args for last (no space)' \
+ 'test-gitopt commit -asm"hello world" > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '-m'
+04 'hello world'
+05 '(null)'
+EOF
+test_expect_success 'unbundle with args (space)' \
+ 'test-gitopt commit -asm "hello world" > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '-m'
+04 'hello world'
+05 'file1'
+06 'file2'
+07 '(null)'
+EOF
+test_expect_success 'unbundle and reorder switches and command w/args' \
+ 'test-gitopt commit file1 file2 -asm "hello world" > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '-a'
+02 '-s'
+03 '--edit'
+04 'file2'
+05 '--'
+06 'file1'
+07 '-as'
+08 '--all'
+09 '(null)'
+EOF
+test_expect_success 'reorder up to and pass-through "--"' \
+ 'test-gitopt commit -as file2 --edit -- file1 -as --all > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '--find-copies-harder'
+02 '(null)'
+EOF
+test_expect_success 'abbreviation finder (prefix)' \
+ 'test-gitopt whatchanged --find-c > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '--patch-with-raw'
+02 '(null)'
+EOF
+test_expect_success 'abbreviation finder (substring on "-" token)' \
+ 'test-gitopt whatchanged --raw > output &&
+ cmp expect output'
+
+# we assume unknown switches that cannot be resolved to a single known
+# switch to just be an new argument we do not know about, so we pass
+# it to the underlying command
+cat > expect <<EOF
+00 'whatchanged'
+01 '--name'
+02 '(null)'
+EOF
+test_expect_success 'ambiguous abbreviation (pass-through)' \
+ 'test-gitopt whatchanged --name > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '--diff-filter=MCT'
+02 'file1'
+03 '(null)'
+EOF
+test_expect_success 'rewrite on long argument' \
+ 'test-gitopt whatchanged file1 --diff-filter MCT > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-Shello'
+02 '(null)'
+EOF
+test_expect_success 'rewrite on short argument (#1)' \
+ 'test-gitopt whatchanged -Shello > output &&
+ cmp expect output'
+test_expect_success 'rewrite on short argument (#2)' \
+ 'test-gitopt whatchanged -S hello > output &&
+ cmp expect output'
+test_expect_success 'rewrite on --short argument (#3)' \
+ 'test-gitopt whatchanged --S hello > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-Shello'
+02 '(null)'
+EOF
+test_expect_success 'rewrite on short argument (leading "=" arg) (#1)' \
+ 'test-gitopt whatchanged --S=hello > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-C20'
+02 '-M'
+03 '(null)'
+EOF
+test_expect_success 'pass optional arg (#1)' \
+ 'test-gitopt whatchanged -C20 -M > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-C'
+02 '--find-copies-harder'
+03 '(null)'
+EOF
+test_expect_success 'detect optional arg bogus (#1)' \
+ 'test-gitopt whatchanged -C --find-copies-harder > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-C20'
+02 '(null)'
+EOF
+test_expect_success 'pass optional arg (#2)' \
+ 'test-gitopt whatchanged -C20 > output &&
+ cmp expect output'
+# test_expect_success 'pass optional arg (#3)' \
+ # 'test-gitopt whatchanged -C=20 > output &&
+ # cmp expect output'
+
+cat > expect <<EOF
+00 'checkout'
+01 '-b'
+02 'newbranch'
+03 '(null)'
+EOF
+test_expect_success 'rewrite short split arg (#1)' \
+ 'test-gitopt checkout -bnewbranch > output &&
+ cmp expect output'
+# test_expect_success 'rewrite short split arg (#2)' \
+ # 'test-gitopt checkout --b=newbranch > output &&
+ # cmp expect output'
+test_expect_success 'rewrite short sanity check' \
+ 'test-gitopt checkout -b newbranch > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'log'
+01 '--default'
+02 'dunno'
+03 '--all'
+04 '(null)'
+EOF
+test_expect_success 'rewrite long split arg' \
+ 'test-gitopt log --default=dunno --all > output &&
+ cmp expect output'
+test_expect_success 'rewrite long sanity check' \
+ 'test-gitopt log --default dunno --all > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'log'
+01 '--max-count=56'
+02 '(null)'
+EOF
+test_expect_success 'rewrite -<num> => --max-count=<num>' \
+ 'test-gitopt log -56 > output && cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-u'
+02 '-l20'
+03 '-p'
+04 '-Spicktoken'
+05 '(null)'
+EOF
+test_expect_success 'bundle options with integer args mixed in' \
+ 'test-gitopt whatchanged -ul20pSpicktoken > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-u'
+02 '-C20'
+03 '-p'
+04 '(null)'
+EOF
+test_expect_success 'bundle options with optional integer args used' \
+ 'test-gitopt whatchanged -uC20p > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-u'
+02 '-C'
+03 '-p'
+04 '(null)'
+EOF
+test_expect_success 'bundle options with optional integer args not used' \
+ 'test-gitopt whatchanged -uCp > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'whatchanged'
+01 '-C'
+02 '20'
+03 '(null)'
+EOF
+test_expect_success 'optional integer arg switch must be attached' \
+ 'test-gitopt whatchanged -C 20 > output &&
+ cmp expect output'
+
+cat > expect <<EOF
+00 'commit'
+01 '--message'
+02 'hello world'
+03 '-s'
+04 'file'
+05 '(null)'
+EOF
+test_expect_success 'long option argument parsing when short option can work' \
+ 'test-gitopt commit --message "hello world" -s file > output &&
+ cmp expect output'
+
+test_done
diff --git a/test-gitopt.c b/test-gitopt.c
new file mode 100644
index 0000000..2692e2b
--- /dev/null
+++ b/test-gitopt.c
@@ -0,0 +1,112 @@
+
+#include "git-compat-util.h"
+#include "gitopt.h"
+#include "gitopt/sh.h"
+
+#define _rev \
+{ "max-count", 'n', "--max-count=%s", ARG_INT, 0 }, \
+{ 0, ' ', "--max-count=%s", ARG_INT, 0 }, \
+{ "max-age", 0, "--max-age=%s", ARG_INT, 0 }, \
+{ "min-age", 0, "--min-age=%s", ARG_INT, 0 }, \
+{ "since", 0, "--since=%s", ARG_ONE, 0 }, \
+{ "after", 0, "--after=%s", ARG_ONE, 0 }, \
+{ "before", 0, "--before=%s", ARG_ONE, 0 }, \
+{ "until", 0, "--until=%s", ARG_ONE, 0 }, \
+{ "all", 0, 0, 0, 0 }, \
+{ "not", 0, 0, 0, 0 }, \
+{ "default", 0, "--default %s", ARG_ONE, 0 }, \
+{ "topo-order", 0, 0, 0, 0 }, \
+{ "date-order", 0, 0, 0, 0 }, \
+{ "parents", 0, 0, 0, 0 }, \
+{ "dense", 0, 0, 0, 0 }, \
+{ "sparse", 0, 0, 0, 0 }, \
+{ "remove-empty", 0, 0, 0, 0 }, \
+{ "no-merges", 0, 0, 0, 0 }, \
+{ "boundary", 0, 0, 0, 0 }, \
+{ "objects", 0, 0, 0, 0 }, \
+{ "objects-edge", 0, 0, 0, 0 }, \
+{ "unpacked", 0, 0, 0, 0 }, \
+{ 0, 'r', 0, 0, 0 }, \
+{ 0, 't', 0, 0, 0 }, \
+{ 0, 'm', 0, 0, 0 }, \
+{ 0, 'c', 0, 0, 0 }, \
+{ "cc", 0, 0, 0, 0 }, \
+{ 0, 'v', 0, 0, 0 }, \
+{ "pretty", 0, "--pretty=%s", ARG_ONE, 0 }, \
+{ "root", 0, 0, 0, 0 }, \
+{ "no-commit-id", 0, 0, 0, 0 }, \
+{ "always", 0, 0, 0, 0 }, \
+{ "no-abbrev", 0, 0, 0, 0 }, \
+{ "abbrev", 0, 0, 0, 0 }, \
+{ "abbrev-commit", 0, 0, 0, 0 }, \
+{ "full-diff", 0, 0, 0, 0 }, \
+
+#define _diff \
+{ 0, 'u', 0, 0, 0 }, \
+{ 0, 'p', 0, 0, 0 }, \
+{ "patch-with-raw", 0, 0, 0, 0 }, \
+{ "stat", 0, 0, 0, 0 }, \
+{ "patch-with-stat", 0, 0, 0, 0 }, \
+{ 0, 'z', 0, 0, 0 }, \
+{ 0, 'l', "-l%s", ARG_INT, 0 }, \
+{ "full-index", 0, 0, 0, 0 }, \
+{ "name-only", 0, 0, 0, 0 }, \
+{ "name-status", 0, 0, 0, 0 }, \
+{ 0, 'R', 0, 0, 0 }, \
+{ 0, 'S', "-S%s", ARG_ONE, 0 }, \
+{ 0, 's', 0, 0, 0 }, \
+{ 0, 'O', "-O%s", ARG_ONE, 0 }, \
+{ "diff-filter", 0, "--diff-filter=%s", ARG_ONE, 0 }, \
+{ "pickaxe-all", 0, 0, 0, 0 }, \
+{ "pickaxe-regex", 0, 0, 0, 0 }, \
+{ 0, 'B', "-B%s", ARG_OPTINT, 0 }, \
+{ 0, 'M', "-M%s", ARG_OPTINT, 0 }, \
+{ 0, 'C', "-C%s", ARG_OPTINT, 0 }, \
+{ "find-copies-harder", 0, 0, 0, 0 }, \
+{ "abbrev", 0, "--abbrev=%s", ARG_OPT, 0 }, \
+
+#define end {0,0}
+
+static const struct opt_spec ost_log[] = { _rev end };
+static const struct opt_spec ost_rev_list[] = { _rev end };
+static const struct opt_spec ost_whatchanged[] = { _diff _rev end };
+static const struct opt_spec ost_show[] = { _diff _rev end };
+
+static const struct opt_spec_map {
+ const char *cmd;
+ const struct opt_spec *ost;
+} opt_specs[] = {
+ { "checkout", ost_checkout },
+ { "commit", ost_commit },
+ { "log", ost_log },
+ { "rev-list", ost_rev_list },
+ { "show", ost_show },
+ { "whatchanged", ost_whatchanged },
+};
+
+
+int main(int argc, const char **argv, char **envp)
+{
+ int i;
+ struct exec_args *ea;
+ const struct opt_spec *ost = NULL;
+
+ if (!argv[1])
+ usage("test-gitopt [<options>] <command> [<options>]");
+
+ for (i = 0; i < ARRAY_SIZE(opt_specs); i++) {
+ if (!strcmp(argv[1], opt_specs[i].cmd)) {
+ ost = opt_specs[i].ost;
+ break;
+ }
+ }
+ if (!ost)
+ usage("test-gitopt [<options>] <command> [<options>]");
+
+ ea = gitopt_parse_ost(ost, argc - 1, argv + 1);
+
+ for (i = 0; i <= ea->argc; i++)
+ printf("%02d '%s'\n", i, ea->argv[i] ? ea->argv[i] : "(null)");
+
+ return 0;
+}
--
1.3.2.g0a3ae
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 2/6] update-index: convert to using gitopt
2006-05-09 5:06 ` [PATCH 1/6] gitopt: a new command-line option parser for git Eric Wong
@ 2006-05-09 5:06 ` Eric Wong
2006-05-09 5:06 ` [PATCH 3/6] ls-tree: convert to gitopt Eric Wong
2006-05-09 9:08 ` [PATCH 1/6] gitopt: a new command-line option parser for git Timo Hirvonen
1 sibling, 1 reply; 22+ messages in thread
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
This is an example how how to override gitopt_non_option_cb, as
well as disabling permuting of options to the front of the
command list. This way, commands like:
git-update-index --add new_file --remove old_file
will continue to work.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
update-index.c | 261 ++++++++++++++++++++++++++++----------------------------
1 files changed, 130 insertions(+), 131 deletions(-)
50f33d232367e8d7a46bd014f5a5da4d0ad370b3
diff --git a/update-index.c b/update-index.c
index 00cde70..6205f85 100644
--- a/update-index.c
+++ b/update-index.c
@@ -7,6 +7,7 @@ #include "cache.h"
#include "strbuf.h"
#include "quote.h"
#include "tree-walk.h"
+#include "gitopt.h"
/*
* Default to not allowing changes to the list of files. The
@@ -25,6 +26,14 @@ static int info_only;
static int force_remove;
static int verbose;
static int mark_valid_only = 0;
+static int prefix_length = 0;
+static int line_termination = '\n';
+static const char *prefix = NULL;
+static char set_executable_bit = 0;
+static int read_from_stdin = 0;
+static int newfd = 0;
+static int has_errors = 0;
+
#define MARK_VALID 1
#define UNMARK_VALID 2
@@ -189,10 +198,9 @@ static struct cache_entry *refresh_entry
return updated;
}
-static int refresh_cache(int really)
+static void refresh_cache(int really)
{
int i;
- int has_errors = 0;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce, *new;
@@ -205,7 +213,7 @@ static int refresh_cache(int really)
if (allow_unmerged)
continue;
printf("%s: needs merge\n", ce->name);
- has_errors = 1;
+ has_errors |= 1;
continue;
}
@@ -225,7 +233,7 @@ static int refresh_cache(int really)
if (quiet)
continue;
printf("%s: needs update\n", ce->name);
- has_errors = 1;
+ has_errors |= 1;
continue;
}
active_cache_changed = 1;
@@ -234,7 +242,7 @@ static int refresh_cache(int really)
* from mmap(). */
active_cache[i] = new;
}
- return has_errors;
+ return;
}
/*
@@ -600,15 +608,123 @@ static int do_unresolve(int ac, const ch
return err;
}
+static void write_cache_if_changed ()
+{
+ if (active_cache_changed) {
+ if (write_cache(newfd, active_cache, active_nr) ||
+ commit_index_file(&cache_file))
+ die("Unable to write new cachefile");
+ }
+}
+
+gitopt_eat(opt_q, quiet = 1;)
+gitopt_eat(opt_add, allow_add = 1;)
+gitopt_eat(opt_replace, allow_replace = 1;)
+gitopt_eat(opt_remove, allow_remove = 1;)
+gitopt_eat(opt_unmerged, allow_unmerged = 1;)
+gitopt_eat(opt_refresh, refresh_cache(0);)
+gitopt_eat(opt_really_refresh, refresh_cache(1);)
+
+static struct exec_args *opt_cacheinfo(const struct opt_spec *s,
+ const int argc, const char **argv, int *argc_pos)
+{
+ struct exec_args *ea = new_exec_args(4);
+ unsigned char sha1[20];
+ unsigned int mode;
+
+ ea->argc = 1;
+ ea->argv[0] = argv[*argc_pos];
+ while (*argc_pos < (argc-1) && ea->argc < 4)
+ ea->argv[ea->argc++] = argv[++(*argc_pos)];
+ if (ea->argc != 4)
+ die("git-update-index: --cacheinfo <mode> <sha1> <path>");
+
+ if ((sscanf(ea->argv[1], "%o", &mode) != 1) ||
+ get_sha1_hex(ea->argv[2], sha1) ||
+ add_cacheinfo(mode, sha1, ea->argv[3], 0))
+ die("git-update-index: --cacheinfo cannot add %s",ea->argv[3]);
+
+ return nil_exec_args(ea);
+}
+
+gitopt_eat_one_arg(opt_chmod,
+ if (*argc_pos + 1 >= argc)
+ die("git-update-index: --chmod=%s <path>",ea->argv[0]);
+ set_executable_bit = ea->argv[0][0];)
+
+gitopt_eat(opt_assume_unchanged, mark_valid_only = MARK_VALID;)
+gitopt_eat(opt_no_assume_unchanged, mark_valid_only = UNMARK_VALID;)
+gitopt_eat(opt_info_only, info_only = 1;)
+gitopt_eat(opt_force_remove, force_remove = 1;)
+gitopt_eat(opt_z, line_termination = 0;)
+gitopt_eat(opt_stdin,
+ if (*argc_pos != argc - 1)
+ die("--stdin must be at the end");
+ read_from_stdin = 1;)
+
+gitopt_eat(opt_index_info,
+ if (*argc_pos != argc - 1)
+ die("--index-info must be at the end");
+ allow_add = allow_replace = allow_remove = 1;
+ read_index_info(line_termination);)
+
+gitopt_eat(opt_unresolve,
+ has_errors = do_unresolve(argc - *argc_pos, argv + *argc_pos,
+ prefix, prefix_length);
+ if (has_errors)
+ active_cache_changed = 0;
+ write_cache_if_changed();
+ exit(has_errors ? 1 : 0);)
+
+gitopt_eat(opt_ignore_missing, not_new = 1;)
+gitopt_eat(opt_verbose, verbose = 1;)
+gitopt_eat(opt_h, usage(update_index_usage);)
+
+static const struct opt_spec update_index_ost[] = {
+ { 0, 'q', 0, 0, opt_q },
+ { "add", 0, 0, 0, opt_add },
+ { "replace", 0, 0, 0, opt_replace },
+ { "remove", 0, 0, 0, opt_remove },
+ { "unmerged", 0, 0, 0, opt_unmerged },
+ { "refresh", 0, 0, 0, opt_refresh },
+ { "really-refresh", 0, 0, 0, opt_really_refresh },
+ { "cacheinfo", 0, 0, ARG_TRE, opt_cacheinfo},
+ { "chmod", 0, "%s", ARG_ONE, opt_chmod },
+ { "assume-unchanged", 0, 0, 0, opt_assume_unchanged },
+ { "no-assume-unchanged",0, 0, 0, opt_no_assume_unchanged},
+ { "info-only", 0, 0, 0, opt_info_only },
+ { "force-remove", 0, 0, 0, opt_force_remove },
+ { 0, 'z', 0, 0, opt_z },
+ { "stdin", 0, 0, 0, opt_stdin },
+ { "index-info", 0, 0, 0, opt_index_info },
+ { "unresolve", 0, 0, 0, opt_unresolve },
+ { "ignore-missing", 0, 0, 0, opt_ignore_missing },
+ { "verbose", 0, 0, 0, opt_verbose },
+ { "help", 'h', 0, 0, opt_h },
+ { 0, 0 }
+};
+
+/* call this for every non-option (and everything after "--") we have */
+static void update_index_non_option_cb(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos)
+{
+ const char *arg = argv[*argc_pos];
+ update_one(arg, prefix, prefix_length);
+ if (set_executable_bit)
+ chmod_path(set_executable_bit, arg);
+}
+
int main(int argc, const char **argv)
{
- int i, newfd, entries, has_errors = 0, line_termination = '\n';
- int allow_options = 1;
- int read_from_stdin = 0;
- const char *prefix = setup_git_directory();
- int prefix_length = prefix ? strlen(prefix) : 0;
- char set_executable_bit = 0;
+ int entries;
+ struct exec_args *a;
+
+ prefix = setup_git_directory();
+ if (prefix)
+ prefix_length = strlen(prefix);
+ a = new_exec_args(argc); /* argv[0] and options: */
+ gitopt_non_option_cb = update_index_non_option_cb;
git_config(git_default_config);
newfd = hold_index_file_for_update(&cache_file, get_index_file());
@@ -619,120 +735,9 @@ int main(int argc, const char **argv)
if (entries < 0)
die("cache corrupted");
- for (i = 1 ; i < argc; i++) {
- const char *path = argv[i];
+ if (gitopt_parse_ost_split(a, a, update_index_ost, argc, argv) < 0)
+ usage(update_index_usage);
- if (allow_options && *path == '-') {
- if (!strcmp(path, "--")) {
- allow_options = 0;
- continue;
- }
- if (!strcmp(path, "-q")) {
- quiet = 1;
- continue;
- }
- if (!strcmp(path, "--add")) {
- allow_add = 1;
- continue;
- }
- if (!strcmp(path, "--replace")) {
- allow_replace = 1;
- continue;
- }
- if (!strcmp(path, "--remove")) {
- allow_remove = 1;
- continue;
- }
- if (!strcmp(path, "--unmerged")) {
- allow_unmerged = 1;
- continue;
- }
- if (!strcmp(path, "--refresh")) {
- has_errors |= refresh_cache(0);
- continue;
- }
- if (!strcmp(path, "--really-refresh")) {
- has_errors |= refresh_cache(1);
- continue;
- }
- if (!strcmp(path, "--cacheinfo")) {
- unsigned char sha1[20];
- unsigned int mode;
-
- if (i+3 >= argc)
- die("git-update-index: --cacheinfo <mode> <sha1> <path>");
-
- if ((sscanf(argv[i+1], "%o", &mode) != 1) ||
- get_sha1_hex(argv[i+2], sha1) ||
- add_cacheinfo(mode, sha1, argv[i+3], 0))
- die("git-update-index: --cacheinfo"
- " cannot add %s", argv[i+3]);
- i += 3;
- continue;
- }
- if (!strcmp(path, "--chmod=-x") ||
- !strcmp(path, "--chmod=+x")) {
- if (argc <= i+1)
- die("git-update-index: %s <path>", path);
- set_executable_bit = path[8];
- continue;
- }
- if (!strcmp(path, "--assume-unchanged")) {
- mark_valid_only = MARK_VALID;
- continue;
- }
- if (!strcmp(path, "--no-assume-unchanged")) {
- mark_valid_only = UNMARK_VALID;
- continue;
- }
- if (!strcmp(path, "--info-only")) {
- info_only = 1;
- continue;
- }
- if (!strcmp(path, "--force-remove")) {
- force_remove = 1;
- continue;
- }
- if (!strcmp(path, "-z")) {
- line_termination = 0;
- continue;
- }
- if (!strcmp(path, "--stdin")) {
- if (i != argc - 1)
- die("--stdin must be at the end");
- read_from_stdin = 1;
- break;
- }
- if (!strcmp(path, "--index-info")) {
- if (i != argc - 1)
- die("--index-info must be at the end");
- allow_add = allow_replace = allow_remove = 1;
- read_index_info(line_termination);
- break;
- }
- if (!strcmp(path, "--unresolve")) {
- has_errors = do_unresolve(argc - i, argv + i,
- prefix, prefix_length);
- if (has_errors)
- active_cache_changed = 0;
- goto finish;
- }
- if (!strcmp(path, "--ignore-missing")) {
- not_new = 1;
- continue;
- }
- if (!strcmp(path, "--verbose")) {
- verbose = 1;
- continue;
- }
- if (!strcmp(path, "-h") || !strcmp(path, "--help"))
- usage(update_index_usage);
- die("unknown option %s", path);
- }
- update_one(path, prefix, prefix_length);
- if (set_executable_bit)
- chmod_path(set_executable_bit, path);
- }
if (read_from_stdin) {
struct strbuf buf;
strbuf_init(&buf);
@@ -757,12 +762,6 @@ int main(int argc, const char **argv)
}
}
- finish:
- if (active_cache_changed) {
- if (write_cache(newfd, active_cache, active_nr) ||
- commit_index_file(&cache_file))
- die("Unable to write new cachefile");
- }
-
+ write_cache_if_changed();
return has_errors ? 1 : 0;
}
--
1.3.2.g0a3ae
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 3/6] ls-tree: convert to gitopt
2006-05-09 5:06 ` [PATCH 2/6] update-index: convert to using gitopt Eric Wong
@ 2006-05-09 5:06 ` Eric Wong
2006-05-09 5:06 ` [PATCH 4/6] ls-files: convert to using gitopt Eric Wong
0 siblings, 1 reply; 22+ messages in thread
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
A pretty simple and straight forward conversion, imho.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
gitopt/abbrev.h | 24 +++++++++++++++++
ls-tree.c | 79 +++++++++++++++++++++++--------------------------------
2 files changed, 57 insertions(+), 46 deletions(-)
create mode 100644 gitopt/abbrev.h
06d5dc3649e7407f7ff8df0d42a55a906b39cb39
diff --git a/gitopt/abbrev.h b/gitopt/abbrev.h
new file mode 100644
index 0000000..c3b2353
--- /dev/null
+++ b/gitopt/abbrev.h
@@ -0,0 +1,24 @@
+#include "../cache.h"
+
+/* we specify rewrite_fmt here to make opt_abbrev() simpler: */
+#define abbrev_ost_row \
+ { "abbrev", 0, "a %s", ARG_OPTINT, opt_abbrev }
+
+#define gitopt_opt_abbrev(dest) \
+static struct exec_args *opt_abbrev(const struct opt_spec *s, \
+ const int argc, const char **argv, int *argc_pos) \
+{ \
+ struct exec_args *ea = optional_int_arg(s, argc, argv, argc_pos); \
+ if (!ea) return NULL; \
+ if (ea->argc == 1) \
+ dest = DEFAULT_ABBREV; \
+ else { \
+ dest = strtoul(ea->argv[1], NULL, 10); \
+ if (dest && dest < MINIMUM_ABBREV) \
+ dest = MINIMUM_ABBREV; \
+ else if (dest > 40) \
+ dest = 40; \
+ } \
+ return nil_exec_args(ea); \
+}
+
diff --git a/ls-tree.c b/ls-tree.c
index f2b3bc1..4fb6343 100644
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -7,6 +7,8 @@ #include "cache.h"
#include "blob.h"
#include "tree.h"
#include "quote.h"
+#include "gitopt.h"
+#include "gitopt/abbrev.h"
static int line_termination = '\n';
#define LS_RECURSIVE 1
@@ -84,68 +86,53 @@ static int show_tree(unsigned char *sha1
return retval;
}
+gitopt_eat(opt_d, ls_options |= LS_TREE_ONLY;)
+gitopt_eat(opt_t, ls_options |= LS_SHOW_TREES;)
+gitopt_eat(opt_z, line_termination = 0;)
+gitopt_eat(opt_r, ls_options |= LS_RECURSIVE;)
+gitopt_eat(opt_name_only_status, ls_options |= LS_NAME_ONLY;)
+gitopt_eat(opt_full_name, chomp_prefix = 0;)
+gitopt_opt_abbrev(abbrev)
+
+static const struct opt_spec ls_tree_ost[] = {
+ { 0, 'z', 0, 0, opt_z },
+ { 0, 'r', 0, 0, opt_r },
+ { 0, 'd', 0, 0, opt_d },
+ { 0, 't', 0, 0, opt_t },
+ { "name-only", 0, 0, 0, opt_name_only_status },
+ { "name-status", 0, 0, 0, opt_name_only_status },
+ { "full-name", 0, 0, 0, opt_full_name },
+ abbrev_ost_row,
+ { 0, 0 }
+};
+
int main(int argc, const char **argv)
{
unsigned char sha1[20];
struct tree *tree;
+ struct exec_args *a = new_exec_args(argc); /* argv[0] and options: */
+ struct exec_args *b = new_exec_args(argc); /* non-option args */
prefix = setup_git_directory();
git_config(git_default_config);
if (prefix && *prefix)
chomp_prefix = strlen(prefix);
- while (1 < argc && argv[1][0] == '-') {
- switch (argv[1][1]) {
- case 'z':
- line_termination = 0;
- break;
- case 'r':
- ls_options |= LS_RECURSIVE;
- break;
- case 'd':
- ls_options |= LS_TREE_ONLY;
- break;
- case 't':
- ls_options |= LS_SHOW_TREES;
- break;
- case '-':
- if (!strcmp(argv[1]+2, "name-only") ||
- !strcmp(argv[1]+2, "name-status")) {
- ls_options |= LS_NAME_ONLY;
- break;
- }
- if (!strcmp(argv[1]+2, "full-name")) {
- chomp_prefix = 0;
- break;
- }
- if (!strncmp(argv[1]+2, "abbrev=",7)) {
- abbrev = strtoul(argv[1]+9, NULL, 10);
- if (abbrev && abbrev < MINIMUM_ABBREV)
- abbrev = MINIMUM_ABBREV;
- else if (abbrev > 40)
- abbrev = 40;
- break;
- }
- if (!strcmp(argv[1]+2, "abbrev")) {
- abbrev = DEFAULT_ABBREV;
- break;
- }
- /* otherwise fallthru */
- default:
- usage(ls_tree_usage);
- }
- argc--; argv++;
- }
+
+ if (gitopt_parse_ost_split(a, b, ls_tree_ost, argc, argv) < 0)
+ usage(ls_tree_usage);
+ free_exec_args(a);
+
/* -d -r should imply -t, but -d by itself should not have to. */
if ( (LS_TREE_ONLY|LS_RECURSIVE) ==
((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))
ls_options |= LS_SHOW_TREES;
- if (argc < 2)
+ if (b->argc < 1)
usage(ls_tree_usage);
- if (get_sha1(argv[1], sha1))
- die("Not a valid object name %s", argv[1]);
+ if (get_sha1(b->argv[0], sha1))
+ die("Not a valid object name %s", b->argv[0]);
- pathspec = get_pathspec(prefix, argv + 2);
+ pathspec = get_pathspec(prefix, b->argv + 1);
tree = parse_tree_indirect(sha1);
if (!tree)
die("not a tree object");
--
1.3.2.g0a3ae
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 4/6] ls-files: convert to using gitopt
2006-05-09 5:06 ` [PATCH 3/6] ls-tree: convert to gitopt Eric Wong
@ 2006-05-09 5:06 ` Eric Wong
2006-05-09 5:06 ` [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse() Eric Wong
0 siblings, 1 reply; 22+ messages in thread
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
Another simple and straight forward conversion, imho.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
ls-files.c | 187 ++++++++++++++++++++++--------------------------------------
1 files changed, 69 insertions(+), 118 deletions(-)
6a65d6f1e59638185c846920d8c6f0dbd82f1bf7
diff --git a/ls-files.c b/ls-files.c
index 4a4af1c..62f9e10 100644
--- a/ls-files.c
+++ b/ls-files.c
@@ -10,6 +10,8 @@ #include <fnmatch.h>
#include "cache.h"
#include "quote.h"
+#include "gitopt.h"
+#include "gitopt/abbrev.h"
static int abbrev = 0;
static int show_deleted = 0;
@@ -648,133 +650,82 @@ static const char ls_files_usage[] =
"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
"[--] [<file>]*";
+static void tag_pfx()
+{
+ tag_cached = "H ";
+ tag_unmerged = "M ";
+ tag_removed = "R ";
+ tag_modified = "C ";
+ tag_other = "? ";
+ tag_killed = "K ";
+}
+
+static int exc_given = 0;
+
+gitopt_eat(opt_z, line_terminator = 0;)
+gitopt_eat(opt_t, tag_pfx();)
+gitopt_eat(opt_v, tag_pfx(); show_valid_bit = 1;)
+gitopt_eat(opt_c, show_cached = 1;)
+gitopt_eat(opt_d, show_deleted = 1;)
+gitopt_eat(opt_m, show_modified = 1;)
+gitopt_eat(opt_o, show_others = 1;)
+gitopt_eat(opt_i, show_ignored = 1;)
+gitopt_eat(opt_s, show_stage = 1;)
+gitopt_eat(opt_k, show_killed = 1;)
+gitopt_eat(opt_directory, show_other_directories = 1;)
+gitopt_eat(opt_no_empty_directory, hide_empty_directories = 1;)
+gitopt_eat(opt_u, show_stage = 1; show_unmerged = 1;)
+gitopt_eat_one_arg(opt_x,
+ exc_given = 1;
+ add_exclude(ea->argv[0], "", 0, &exclude_list[EXC_CMDL]);)
+gitopt_eat_one_arg(opt_X,
+ exc_given = 1;
+ add_excludes_from_file(ea->argv[0]);)
+gitopt_eat_one_arg(opt_exclude_per_dir,
+ exc_given = 1;
+ exclude_per_dir = ea->argv[0];)
+gitopt_eat(opt_full_name, prefix_offset = 0;)
+gitopt_eat(opt_error_unmatch, error_unmatch = 1;)
+gitopt_opt_abbrev(abbrev)
+
+static const struct opt_spec ls_files_ost[] = {
+ { 0, 'z', 0, 0, opt_z },
+ { 0, 'v', 0, 0, opt_v },
+ { 0, 't', 0, 0, opt_t },
+ { "cached", 'c', 0, 0, opt_c },
+ { "deleted", 'd', 0, 0, opt_d },
+ { "modified", 'm', 0, 0, opt_m },
+ { "others", 'o', 0, 0, opt_o },
+ { "ignored", 'i', 0, 0, opt_i },
+ { "stage", 's', 0, 0, opt_s },
+ { "killed", 'k', 0, 0, opt_k },
+ { "directory", 0, 0, 0, opt_directory },
+ { "no-empty-directory", 0, 0, 0, opt_no_empty_directory},
+ { "unmerged", 'u', 0, 0, opt_u },
+ { "exclude", 'x', "%s", ARG_ONE, opt_x },
+ { "exclude-from", 'X', "%s", ARG_ONE, opt_X },
+ { "exclude-per-directory",0, "%s", ARG_ONE, opt_exclude_per_dir },
+ { "full-name", 0, 0, 0, opt_full_name },
+ { "error-unmatch", 0, 0, 0, opt_error_unmatch },
+ abbrev_ost_row,
+ { 0, 0 }
+};
+
int main(int argc, const char **argv)
{
- int i;
- int exc_given = 0;
+ struct exec_args *a = new_exec_args(argc); /* argv[0] and options: */
+ struct exec_args *b = new_exec_args(argc); /* non-option args */
prefix = setup_git_directory();
if (prefix)
prefix_offset = strlen(prefix);
git_config(git_default_config);
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- if (!strcmp(arg, "--")) {
- i++;
- break;
- }
- if (!strcmp(arg, "-z")) {
- line_terminator = 0;
- continue;
- }
- if (!strcmp(arg, "-t") || !strcmp(arg, "-v")) {
- tag_cached = "H ";
- tag_unmerged = "M ";
- tag_removed = "R ";
- tag_modified = "C ";
- tag_other = "? ";
- tag_killed = "K ";
- if (arg[1] == 'v')
- show_valid_bit = 1;
- continue;
- }
- if (!strcmp(arg, "-c") || !strcmp(arg, "--cached")) {
- show_cached = 1;
- continue;
- }
- if (!strcmp(arg, "-d") || !strcmp(arg, "--deleted")) {
- show_deleted = 1;
- continue;
- }
- if (!strcmp(arg, "-m") || !strcmp(arg, "--modified")) {
- show_modified = 1;
- continue;
- }
- if (!strcmp(arg, "-o") || !strcmp(arg, "--others")) {
- show_others = 1;
- continue;
- }
- if (!strcmp(arg, "-i") || !strcmp(arg, "--ignored")) {
- show_ignored = 1;
- continue;
- }
- if (!strcmp(arg, "-s") || !strcmp(arg, "--stage")) {
- show_stage = 1;
- continue;
- }
- if (!strcmp(arg, "-k") || !strcmp(arg, "--killed")) {
- show_killed = 1;
- continue;
- }
- if (!strcmp(arg, "--directory")) {
- show_other_directories = 1;
- continue;
- }
- if (!strcmp(arg, "--no-empty-directory")) {
- hide_empty_directories = 1;
- continue;
- }
- if (!strcmp(arg, "-u") || !strcmp(arg, "--unmerged")) {
- /* There's no point in showing unmerged unless
- * you also show the stage information.
- */
- show_stage = 1;
- show_unmerged = 1;
- continue;
- }
- if (!strcmp(arg, "-x") && i+1 < argc) {
- exc_given = 1;
- add_exclude(argv[++i], "", 0, &exclude_list[EXC_CMDL]);
- continue;
- }
- if (!strncmp(arg, "--exclude=", 10)) {
- exc_given = 1;
- add_exclude(arg+10, "", 0, &exclude_list[EXC_CMDL]);
- continue;
- }
- if (!strcmp(arg, "-X") && i+1 < argc) {
- exc_given = 1;
- add_excludes_from_file(argv[++i]);
- continue;
- }
- if (!strncmp(arg, "--exclude-from=", 15)) {
- exc_given = 1;
- add_excludes_from_file(arg+15);
- continue;
- }
- if (!strncmp(arg, "--exclude-per-directory=", 24)) {
- exc_given = 1;
- exclude_per_dir = arg + 24;
- continue;
- }
- if (!strcmp(arg, "--full-name")) {
- prefix_offset = 0;
- continue;
- }
- if (!strcmp(arg, "--error-unmatch")) {
- error_unmatch = 1;
- continue;
- }
- if (!strncmp(arg, "--abbrev=", 9)) {
- abbrev = strtoul(arg+9, NULL, 10);
- if (abbrev && abbrev < MINIMUM_ABBREV)
- abbrev = MINIMUM_ABBREV;
- else if (abbrev > 40)
- abbrev = 40;
- continue;
- }
- if (!strcmp(arg, "--abbrev")) {
- abbrev = DEFAULT_ABBREV;
- continue;
- }
- if (*arg == '-')
- usage(ls_files_usage);
- break;
- }
+ if (gitopt_parse_ost_split(a, b, ls_files_ost, argc, argv) < 0)
+ usage(ls_files_usage);
+ free_exec_args(a);
- pathspec = get_pathspec(prefix, argv + i);
+ pathspec = get_pathspec(prefix,b->argv);
/* Verify that the pathspec matches the prefix */
if (pathspec)
--
1.3.2.g0a3ae
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse()
2006-05-09 5:06 ` [PATCH 4/6] ls-files: convert to using gitopt Eric Wong
@ 2006-05-09 5:06 ` Eric Wong
2006-05-09 5:06 ` [PATCH 6/6] commit: allow --pretty= args to be abbreviated Eric Wong
` (2 more replies)
0 siblings, 3 replies; 22+ messages in thread
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
I've added --raw to diff_opt_parse() for consistency's sake:
--patch-with-raw would otherwise be inconsistently abbreviated
to --raw if it wasn't added.
--with-r => --patch-with-raw works great
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
builtin-diff.c | 109 ++++---------
builtin-log.c | 7 -
commit.c | 12 +
diff-files.c | 22 +--
diff-index.c | 19 +-
diff-tree.c | 21 +-
diff.c | 182 ++++++++++++---------
diff.h | 1
gitopt/diff.h | 26 +++
http-push.c | 2
rev-list.c | 34 ++--
revision.c | 484 +++++++++++++++++++++++++-------------------------------
revision.h | 6 +
13 files changed, 437 insertions(+), 488 deletions(-)
create mode 100644 gitopt/diff.h
28cbff1d1b543e8234acc6cada0fc889d4767a59
diff --git a/builtin-diff.c b/builtin-diff.c
index d3ac581..ff4a0ec 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -12,6 +12,7 @@ #include "diffcore.h"
#include "revision.h"
#include "log-tree.h"
#include "builtin.h"
+#include "gitopt/diff.h"
/* NEEDSWORK: struct object has place for name but we _do_
* know mode when we extracted the blob out of a tree, which
@@ -29,22 +30,12 @@ static int builtin_diff_files(struct rev
int argc, const char **argv)
{
int silent = 0;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--base"))
- revs->max_count = 1;
- else if (!strcmp(arg, "--ours"))
- revs->max_count = 2;
- else if (!strcmp(arg, "--theirs"))
- revs->max_count = 3;
- else if (!strcmp(arg, "-q"))
- silent = 1;
- else if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
+ struct exec_args *a;
+
+ g_rev = revs;
+ a = new_exec_args(argc);
+ if (gitopt_parse_ost_split(a, a, diff_files_ost, argc, argv))
+ usage(builtin_diff_usage);
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* specified rev.max_count is reasonable (0 <= n <= 3), and
@@ -107,14 +98,8 @@ static int builtin_diff_b_f(struct rev_i
/* Blob vs file in the working tree*/
struct stat st;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
+ if (argc)
+ usage(builtin_diff_usage);
if (lstat(path, &st))
die("'%s': %s", path, strerror(errno));
if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
@@ -135,14 +120,9 @@ static int builtin_diff_blobs(struct rev
/* Blobs */
unsigned mode = canon_mode(S_IFREG | 0644);
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
+ if (argc)
+ usage(builtin_diff_usage);
+
stuff_change(&revs->diffopt,
mode, mode,
blob[0].sha1, blob[1].sha1,
@@ -155,17 +135,13 @@ static int builtin_diff_blobs(struct rev
static int builtin_diff_index(struct rev_info *revs,
int argc, const char **argv)
{
- int cached = 0;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--cached"))
- cached = 1;
- else if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
+ struct exec_args *a;
+
+ g_rev = revs;
+ a = new_exec_args(argc);
+ if (gitopt_parse_ost_split(a, a, diff_index_ost, argc, argv))
+ usage(builtin_diff_usage);
+
/*
* Make sure there is one revision (i.e. pending object),
* and there is no revision filtering parameters.
@@ -183,14 +159,6 @@ static int builtin_diff_tree(struct rev_
{
const unsigned char *(sha1[2]);
int swap = 1;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
/* We saw two trees, ent[0] and ent[1].
* unless ent[0] is unintesting, they are swapped
@@ -212,14 +180,6 @@ static int builtin_diff_combined(struct
const unsigned char (*parent)[20];
int i;
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--raw"))
- revs->diffopt.output_format = DIFF_FORMAT_RAW;
- else
- usage(builtin_diff_usage);
- argv++; argc--;
- }
if (!revs->dense_combined_merges && !revs->combine_merges)
revs->dense_combined_merges = revs->combine_merges = 1;
parent = xmalloc(ents * sizeof(*parent));
@@ -250,6 +210,7 @@ int cmd_diff(int argc, const char **argv
int ents = 0, blobs = 0, paths = 0;
const char *path = NULL;
struct blobinfo blob[2];
+ struct exec_args *b;
/*
* We could get N tree-ish in the rev.pending_objects list.
@@ -274,23 +235,14 @@ int cmd_diff(int argc, const char **argv
git_config(git_diff_config);
init_revisions(&rev);
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+ cached = 0;
- argc = setup_revisions(argc, argv, &rev, NULL);
+ b = setup_revisions(argc, argv, &rev, NULL, diff_index_ost);
/* Do we have --cached and not have a pending object, then
* default to HEAD by hand. Eek.
*/
- if (!rev.pending_objects) {
- int i;
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
- if (!strcmp(arg, "--"))
- break;
- else if (!strcmp(arg, "--cached")) {
- add_head(&rev);
- break;
- }
- }
- }
+ if (!rev.pending_objects && cached)
+ add_head(&rev);
for (list = rev.pending_objects; list; list = list->next) {
struct object *obj = list->item;
@@ -340,17 +292,18 @@ int cmd_diff(int argc, const char **argv
if (!ents) {
switch (blobs) {
case 0:
- return builtin_diff_files(&rev, argc, argv);
+ return builtin_diff_files(&rev, b->argc, b->argv);
break;
case 1:
if (paths != 1)
usage(builtin_diff_usage);
- return builtin_diff_b_f(&rev, argc, argv, blob, path);
+ return builtin_diff_b_f(&rev, b->argc, b->argv,
+ blob, path);
break;
case 2:
if (paths)
usage(builtin_diff_usage);
- return builtin_diff_blobs(&rev, argc, argv, blob);
+ return builtin_diff_blobs(&rev, b->argc, b->argv, blob);
break;
default:
usage(builtin_diff_usage);
@@ -359,10 +312,10 @@ int cmd_diff(int argc, const char **argv
else if (blobs)
usage(builtin_diff_usage);
else if (ents == 1)
- return builtin_diff_index(&rev, argc, argv);
+ return builtin_diff_index(&rev, b->argc, b->argv);
else if (ents == 2)
- return builtin_diff_tree(&rev, argc, argv, ent);
+ return builtin_diff_tree(&rev, b->argc, b->argv, ent);
else
- return builtin_diff_combined(&rev, argc, argv, ent, ents);
+ return builtin_diff_combined(&rev, b->argc, b->argv, ent, ents);
usage(builtin_diff_usage);
}
diff --git a/builtin-log.c b/builtin-log.c
index 69f2911..b004bc5 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -14,14 +14,15 @@ static int cmd_log_wc(int argc, const ch
struct rev_info *rev)
{
struct commit *commit;
+ struct exec_args *b;
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
rev->verbose_header = 1;
- argc = setup_revisions(argc, argv, rev, "HEAD");
+ b = setup_revisions(argc, argv, rev, "HEAD", NULL);
- if (argc > 1)
- die("unrecognized argument: %s", argv[1]);
+ if (b->argc)
+ die("unrecognized argument: %s", b->argv[0]);
prepare_revision_walk(rev);
setup_pager();
diff --git a/commit.c b/commit.c
index 2717dd8..a056b25 100644
--- a/commit.c
+++ b/commit.c
@@ -26,17 +26,17 @@ enum cmit_fmt get_commit_format(const ch
{
if (!*arg)
return CMIT_FMT_DEFAULT;
- if (!strcmp(arg, "=raw"))
+ if (!strcmp(arg, "raw"))
return CMIT_FMT_RAW;
- if (!strcmp(arg, "=medium"))
+ if (!strcmp(arg, "medium"))
return CMIT_FMT_MEDIUM;
- if (!strcmp(arg, "=short"))
+ if (!strcmp(arg, "short"))
return CMIT_FMT_SHORT;
- if (!strcmp(arg, "=full"))
+ if (!strcmp(arg, "full"))
return CMIT_FMT_FULL;
- if (!strcmp(arg, "=fuller"))
+ if (!strcmp(arg, "fuller"))
return CMIT_FMT_FULLER;
- if (!strcmp(arg, "=oneline"))
+ if (!strcmp(arg, "oneline"))
return CMIT_FMT_ONELINE;
die("invalid --pretty format");
}
diff --git a/diff-files.c b/diff-files.c
index b9d193d..e5b69f4 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -7,6 +7,7 @@ #include "cache.h"
#include "diff.h"
#include "commit.h"
#include "revision.h"
+#include "gitopt/diff.h"
static const char diff_files_usage[] =
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
@@ -15,26 +16,17 @@ COMMON_DIFF_OPTIONS_HELP;
int main(int argc, const char **argv)
{
struct rev_info rev;
- int silent = 0;
+ struct exec_args *b;
+ silent = 0;
git_config(git_diff_config);
init_revisions(&rev);
rev.abbrev = 0;
+ g_rev = &rev;
- argc = setup_revisions(argc, argv, &rev, NULL);
- while (1 < argc && argv[1][0] == '-') {
- if (!strcmp(argv[1], "--base"))
- rev.max_count = 1;
- else if (!strcmp(argv[1], "--ours"))
- rev.max_count = 2;
- else if (!strcmp(argv[1], "--theirs"))
- rev.max_count = 3;
- else if (!strcmp(argv[1], "-q"))
- silent = 1;
- else
- usage(diff_files_usage);
- argv++; argc--;
- }
+ b = setup_revisions(argc, argv, &rev, NULL, diff_files_ost);
+ if (b->argc)
+ usage(diff_files_usage);
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* rev.max_count is reasonable (0 <= n <= 3),
diff --git a/diff-index.c b/diff-index.c
index 8c9f601..92052dc 100644
--- a/diff-index.c
+++ b/diff-index.c
@@ -2,6 +2,8 @@ #include "cache.h"
#include "diff.h"
#include "commit.h"
#include "revision.h"
+#include "gitopt.h"
+#include "gitopt/diff.h"
static const char diff_cache_usage[] =
"git-diff-index [-m] [--cached] "
@@ -11,22 +13,17 @@ COMMON_DIFF_OPTIONS_HELP;
int main(int argc, const char **argv)
{
struct rev_info rev;
- int cached = 0;
- int i;
+ struct exec_args *b;
+ cached = 0;
git_config(git_diff_config);
init_revisions(&rev);
rev.abbrev = 0;
- argc = setup_revisions(argc, argv, &rev, NULL);
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- if (!strcmp(arg, "--cached"))
- cached = 1;
- else
- usage(diff_cache_usage);
- }
+ b = setup_revisions(argc, argv, &rev, NULL, diff_index_ost);
+ if (b->argc)
+ usage(diff_cache_usage);
+
/*
* Make sure there is one revision (i.e. pending object),
* and there is no revision filtering parameters.
diff --git a/diff-tree.c b/diff-tree.c
index 7207867..240242c 100644
--- a/diff-tree.c
+++ b/diff-tree.c
@@ -2,6 +2,7 @@ #include "cache.h"
#include "diff.h"
#include "commit.h"
#include "log-tree.h"
+#include "gitopt.h"
static struct rev_info log_tree_opt;
@@ -58,6 +59,13 @@ static const char diff_tree_usage[] =
" --root include the initial commit as diff against /dev/null\n"
COMMON_DIFF_OPTIONS_HELP;
+static int read_stdin = 0;
+gitopt_eat(opt_stdin, read_stdin = 1;)
+static const struct opt_spec diff_tree_ost[] = {
+ { "stdin", 0, 0, 0, opt_stdin },
+ { 0, 0 }
+};
+
int main(int argc, const char **argv)
{
int nr_sha1;
@@ -65,24 +73,17 @@ int main(int argc, const char **argv)
struct object *tree1, *tree2;
static struct rev_info *opt = &log_tree_opt;
struct object_list *list;
- int read_stdin = 0;
+ struct exec_args *b;
git_config(git_diff_config);
nr_sha1 = 0;
init_revisions(opt);
opt->abbrev = 0;
opt->diff = 1;
- argc = setup_revisions(argc, argv, opt, NULL);
-
- while (--argc > 0) {
- const char *arg = *++argv;
- if (!strcmp(arg, "--stdin")) {
- read_stdin = 1;
- continue;
- }
+ b = setup_revisions(argc, argv, opt, NULL, diff_tree_ost);
+ if (b->argc)
usage(diff_tree_usage);
- }
/*
* NOTE! "setup_revisions()" will have inserted the revisions
diff --git a/diff.c b/diff.c
index 5315270..3b56efc 100644
--- a/diff.c
+++ b/diff.c
@@ -9,6 +9,8 @@ #include "quote.h"
#include "diff.h"
#include "diffcore.h"
#include "xdiff-interface.h"
+#include "gitopt.h"
+#include "gitopt/abbrev.h"
static int use_size_cache;
@@ -407,7 +409,8 @@ static void builtin_diff(const char *nam
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
- int complete_rewrite)
+ int complete_rewrite,
+ struct diff_options *o)
{
mmfile_t mf1, mf2;
const char *lbl[2];
@@ -463,10 +466,9 @@ static void builtin_diff(const char *nam
ecbdata.label_path = lbl;
xpp.flags = XDF_NEED_MINIMAL;
- xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_FUNCNAMES;
if (!diffopts)
- ;
+ xecfg.ctxlen = o->ctxlen;
else if (!strncmp(diffopts, "--unified=", 10))
xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
else if (!strncmp(diffopts, "-u", 2))
@@ -928,7 +930,8 @@ static void run_diff_cmd(const char *pgm
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
- int complete_rewrite)
+ int complete_rewrite,
+ struct diff_options *o)
{
if (pgm) {
run_external_diff(pgm, name, other, one, two, xfrm_msg,
@@ -937,7 +940,7 @@ static void run_diff_cmd(const char *pgm
}
if (one && two)
builtin_diff(name, other ? other : name,
- one, two, xfrm_msg, complete_rewrite);
+ one, two, xfrm_msg, complete_rewrite, o);
else
printf("* Unmerged path %s\n", name);
}
@@ -971,7 +974,7 @@ static void run_diff(struct diff_filepai
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
- run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
+ run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0, o);
return;
}
@@ -1041,15 +1044,15 @@ static void run_diff(struct diff_filepai
* needs to be split into deletion and creation.
*/
struct diff_filespec *null = alloc_filespec(two->path);
- run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
+ run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0, o);
free(null);
null = alloc_filespec(one->path);
- run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
+ run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0, o);
free(null);
}
else
run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
- complete_rewrite);
+ complete_rewrite, o);
free(name_munged);
free(other_munged);
@@ -1086,6 +1089,7 @@ void diff_setup(struct diff_options *opt
options->line_termination = '\n';
options->break_opt = -1;
options->rename_limit = -1;
+ options->ctxlen = 3;
options->change = diff_change;
options->add_remove = diff_addremove;
@@ -1126,76 +1130,100 @@ int diff_setup_done(struct diff_options
return 0;
}
+static struct diff_options *g_opt = NULL; /* gitopt needs globals :x */
+static int rv_diff_opt_parse = 0;
+
+gitopt_eat_opt_int(opt_unified,
+ g_opt->output_format = DIFF_FORMAT_PATCH;
+ if (ea->argc) g_opt->ctxlen = strtol(ea->argv[0], NULL, 10);)
+gitopt_eat(opt_p, g_opt->output_format = DIFF_FORMAT_PATCH;)
+gitopt_eat(opt_patch_with_raw,
+ g_opt->output_format = DIFF_FORMAT_PATCH;
+ g_opt->with_raw = 1;)
+gitopt_eat(opt_raw, g_opt->output_format = DIFF_FORMAT_RAW;)
+gitopt_eat(opt_stat,
+ g_opt->output_format = DIFF_FORMAT_DIFFSTAT;)
+gitopt_eat(opt_patch_with_stat,
+ g_opt->output_format = DIFF_FORMAT_PATCH;
+ g_opt->with_stat = 1;)
+gitopt_eat(opt_z, g_opt->line_termination = 0;)
+gitopt_eat_one_arg(opt_l, g_opt->rename_limit = strtoul(ea->argv[0],NULL,10);)
+gitopt_eat(opt_full_index, g_opt->full_index = 1;)
+gitopt_eat(opt_name_only, g_opt->output_format = DIFF_FORMAT_NAME;)
+gitopt_eat(opt_name_status,
+ g_opt->output_format = DIFF_FORMAT_NAME_STATUS;)
+gitopt_eat(opt_R, g_opt->reverse_diff = 1;)
+gitopt_eat_one_arg(opt_S, g_opt->pickaxe = ea->argv[0];)
+gitopt_eat(opt_s, g_opt->output_format = DIFF_FORMAT_NO_OUTPUT;)
+gitopt_eat_one_arg(opt_O, g_opt->orderfile = ea->argv[0];)
+gitopt_eat_one_arg(opt_diff_filter, g_opt->filter = ea->argv[0];)
+gitopt_eat(opt_pickaxe_all, g_opt->pickaxe_opts = DIFF_PICKAXE_ALL;)
+gitopt_eat(opt_pickaxe_regex, g_opt->pickaxe_opts = DIFF_PICKAXE_REGEX;)
+gitopt_eat_opt_int(opt_B,
+ if ((g_opt->break_opt = diff_scoreopt_parse(*argv)) == -1)
+ rv_diff_opt_parse = -1;)
+gitopt_eat_opt_int(opt_M,
+ if ((g_opt->rename_score = diff_scoreopt_parse(*argv)) == -1)
+ rv_diff_opt_parse = -1;
+ g_opt->detect_rename = DIFF_DETECT_RENAME;)
+gitopt_eat_opt_int(opt_C,
+ if ((g_opt->rename_score = diff_scoreopt_parse(*argv)) == -1)
+ rv_diff_opt_parse = -1;
+ g_opt->detect_rename = DIFF_DETECT_COPY;)
+gitopt_eat(opt_find_copies_harder, g_opt->find_copies_harder = 1;)
+gitopt_opt_abbrev(g_opt->abbrev)
+
+static const struct opt_spec diff_ost[] = {
+ { 0, 'p', 0, 0, opt_p },
+ { "unified", 'u', "%s", ARG_OPTINT, opt_unified },
+ { "raw", 0, 0, 0, opt_raw },
+ { "patch-with-raw", 0, 0, 0, opt_patch_with_raw },
+ { "stat", 0, 0, 0, opt_stat },
+ { "patch-with-stat", 0, 0, 0, opt_patch_with_stat },
+ { 0, 'z', 0, 0, opt_z },
+ { 0, 'l', "%s", ARG_INT,opt_l },
+ { "full-index", 0, 0, 0, opt_full_index },
+ { "name-only", 0, 0, 0, opt_name_only },
+ { "name-status", 0, 0, 0, opt_name_status },
+ { 0, 'R', 0, 0, opt_R },
+ { 0, 'S', "%s", ARG_ONE,opt_S },
+ { 0, 's', 0, 0, opt_s },
+ { 0, 'O', "%s", ARG_ONE, opt_O },
+ { "diff-filter", 0, "%s", ARG_ONE, opt_diff_filter },
+ { "pickaxe-all", 0, 0, 0, opt_pickaxe_all },
+ { "pickaxe-regex", 0, 0, 0, opt_pickaxe_regex },
+ { 0, 'B', "%s", ARG_OPTINT,opt_B },
+ { 0, 'M', "%s", ARG_OPTINT,opt_M },
+ { 0, 'C', "%s", ARG_OPTINT,opt_C },
+ { "find-copies-harder", 0, 0, 0, opt_find_copies_harder},
+ abbrev_ost_row,
+ { 0, 0 }
+};
+
+static void diff_non_option_cb(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos)
+{
+ rv_diff_opt_parse = 0;
+}
+
int diff_opt_parse(struct diff_options *options, const char **av, int ac)
{
- const char *arg = av[0];
- if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
- options->output_format = DIFF_FORMAT_PATCH;
- else if (!strcmp(arg, "--patch-with-raw")) {
- options->output_format = DIFF_FORMAT_PATCH;
- options->with_raw = 1;
- }
- else if (!strcmp(arg, "--stat"))
- options->output_format = DIFF_FORMAT_DIFFSTAT;
- else if (!strcmp(arg, "--patch-with-stat")) {
- options->output_format = DIFF_FORMAT_PATCH;
- options->with_stat = 1;
- }
- else if (!strcmp(arg, "-z"))
- options->line_termination = 0;
- else if (!strncmp(arg, "-l", 2))
- options->rename_limit = strtoul(arg+2, NULL, 10);
- else if (!strcmp(arg, "--full-index"))
- options->full_index = 1;
- else if (!strcmp(arg, "--name-only"))
- options->output_format = DIFF_FORMAT_NAME;
- else if (!strcmp(arg, "--name-status"))
- options->output_format = DIFF_FORMAT_NAME_STATUS;
- else if (!strcmp(arg, "-R"))
- options->reverse_diff = 1;
- else if (!strncmp(arg, "-S", 2))
- options->pickaxe = arg + 2;
- else if (!strcmp(arg, "-s"))
- options->output_format = DIFF_FORMAT_NO_OUTPUT;
- else if (!strncmp(arg, "-O", 2))
- options->orderfile = arg + 2;
- else if (!strncmp(arg, "--diff-filter=", 14))
- options->filter = arg + 14;
- else if (!strcmp(arg, "--pickaxe-all"))
- options->pickaxe_opts = DIFF_PICKAXE_ALL;
- else if (!strcmp(arg, "--pickaxe-regex"))
- options->pickaxe_opts = DIFF_PICKAXE_REGEX;
- else if (!strncmp(arg, "-B", 2)) {
- if ((options->break_opt =
- diff_scoreopt_parse(arg)) == -1)
- return -1;
- }
- else if (!strncmp(arg, "-M", 2)) {
- if ((options->rename_score =
- diff_scoreopt_parse(arg)) == -1)
- return -1;
- options->detect_rename = DIFF_DETECT_RENAME;
- }
- else if (!strncmp(arg, "-C", 2)) {
- if ((options->rename_score =
- diff_scoreopt_parse(arg)) == -1)
- return -1;
- options->detect_rename = DIFF_DETECT_COPY;
- }
- else if (!strcmp(arg, "--find-copies-harder"))
- options->find_copies_harder = 1;
- else if (!strcmp(arg, "--abbrev"))
- options->abbrev = DEFAULT_ABBREV;
- else if (!strncmp(arg, "--abbrev=", 9)) {
- options->abbrev = strtoul(arg + 9, NULL, 10);
- if (options->abbrev < MINIMUM_ABBREV)
- options->abbrev = MINIMUM_ABBREV;
- else if (40 < options->abbrev)
- options->abbrev = 40;
- }
- else
- return 0;
- return 1;
+ struct exec_args *a = new_exec_args(1);
+ int i = 0;
+ void (*old_non_option_cb)(struct exec_args *b, const int argc,
+ const char **argv, int *argc_pos);
+ g_opt = options;
+ rv_diff_opt_parse = 1;
+ gitopt_pass_through = 1;
+ old_non_option_cb = gitopt_non_option_cb;
+ gitopt_non_option_cb = diff_non_option_cb;
+
+ gitopt_parse_one_opt(a, a, diff_ost, ac, av, &i);
+
+ free_exec_args(a);
+ gitopt_non_option_cb = old_non_option_cb;
+
+ return rv_diff_opt_parse;
}
static int parse_num(const char **cp_p)
diff --git a/diff.h b/diff.h
index b3b2c4d..b5f016e 100644
--- a/diff.h
+++ b/diff.h
@@ -41,6 +41,7 @@ struct diff_options {
int rename_limit;
int setup;
int abbrev;
+ int ctxlen;
int nr_paths;
const char **paths;
diff --git a/gitopt/diff.h b/gitopt/diff.h
new file mode 100644
index 0000000..6530474
--- /dev/null
+++ b/gitopt/diff.h
@@ -0,0 +1,26 @@
+#ifndef GITOPT_DIFF_H
+#define GITOPT_DIFF_H
+
+static struct rev_info *g_rev;
+static int silent;
+static int cached;
+
+gitopt_eat(opt_base, g_rev->max_count = 1;)
+gitopt_eat(opt_ours, g_rev->max_count = 2;)
+gitopt_eat(opt_theres, g_rev->max_count = 3;)
+gitopt_eat(opt_q, silent = 1;)
+static const struct opt_spec diff_files_ost[] = {
+ { "base", 0, 0, 0, opt_base },
+ { "ours", 0, 0, 0, opt_ours },
+ { "theres", 0, 0, 0, opt_theres },
+ { 0, 'q', 0, 0, opt_q },
+ { 0, 0 }
+};
+
+gitopt_eat(opt_cached, cached = 1;)
+static const struct opt_spec diff_index_ost[] = {
+ { "cached", 0, 0, 0, opt_cached },
+ { 0, 0 }
+};
+
+#endif /* GITOPT_DIFF_H */
diff --git a/http-push.c b/http-push.c
index b4327d9..9c16f3b 100644
--- a/http-push.c
+++ b/http-push.c
@@ -2499,7 +2499,7 @@ int main(int argc, char **argv)
commit_argc++;
}
init_revisions(&revs);
- setup_revisions(commit_argc, commit_argv, &revs, NULL);
+ setup_revisions(commit_argc, commit_argv, &revs, NULL, NULL);
free(new_sha1_hex);
if (old_sha1_hex) {
free(old_sha1_hex);
diff --git a/rev-list.c b/rev-list.c
index 8b0ec38..3c72c59 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -7,6 +7,7 @@ #include "blob.h"
#include "tree-walk.h"
#include "diff.h"
#include "revision.h"
+#include "gitopt.h"
/* bits #0-15 in revision.h */
@@ -291,34 +292,31 @@ static void mark_edges_uninteresting(str
}
}
+static struct rev_info *g_rev;
+gitopt_eat(opt_header, g_rev->verbose_header = 1;)
+gitopt_eat(opt_timestamp, show_timestamp = 1;)
+gitopt_eat(opt_bisect, bisect_list = 1;)
+static const struct opt_spec rev_list_ost[] = {
+ { "header", 0, 0, 0, opt_header },
+ { "timestamp", 0, 0, 0, opt_timestamp },
+ { "bisect", 0, 0, 0, opt_bisect },
+ { 0, 0 }
+};
+
int main(int argc, const char **argv)
{
struct commit_list *list;
- int i;
+ struct exec_args *b;
init_revisions(&revs);
revs.abbrev = 0;
revs.commit_format = CMIT_FMT_UNSPECIFIED;
- argc = setup_revisions(argc, argv, &revs, NULL);
-
- for (i = 1 ; i < argc; i++) {
- const char *arg = argv[i];
+ g_rev = &revs;
- if (!strcmp(arg, "--header")) {
- revs.verbose_header = 1;
- continue;
- }
- if (!strcmp(arg, "--timestamp")) {
- show_timestamp = 1;
- continue;
- }
- if (!strcmp(arg, "--bisect")) {
- bisect_list = 1;
- continue;
- }
+ b = setup_revisions(argc, argv, &revs, NULL, rev_list_ost);
+ if (b->argc)
usage(rev_list_usage);
- }
if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
/* The command line has a --pretty */
hdr_termination = '\n';
diff --git a/revision.c b/revision.c
index 2294b16..6769a48 100644
--- a/revision.c
+++ b/revision.c
@@ -6,6 +6,7 @@ #include "commit.h"
#include "diff.h"
#include "refs.h"
#include "revision.h"
+#include "gitopt/abbrev.h"
static char *path_name(struct name_path *path, const char *name)
{
@@ -534,6 +535,198 @@ void init_revisions(struct rev_info *rev
diff_setup(&revs->diffopt);
}
+static const char *g_def = NULL;
+
+/* I think I should just be able to use the all_* versions of these.. -ew */
+static struct rev_info *g_revs = NULL;
+static int g_flags = 0;
+static const struct opt_spec *g_extra_ost;
+
+gitopt_eat_int(opt_max_count, g_revs->max_count = atoi(ea->argv[0]);)
+gitopt_eat_int(opt_max_age, g_revs->max_age = atoi(ea->argv[0]);)
+gitopt_eat_int(opt_since, g_revs->max_age = approxidate(ea->argv[0]);)
+gitopt_eat_int(opt_min_age, g_revs->min_age = atoi(ea->argv[0]);)
+gitopt_eat_int(opt_until, g_revs->min_age = approxidate(ea->argv[0]);)
+gitopt_eat(opt_all, handle_all(g_revs, g_flags);)
+gitopt_eat(opt_not, g_flags ^= UNINTERESTING;)
+gitopt_eat_arg(opt_default, g_def = ea->argv[0];)
+gitopt_eat(opt_topo_order, g_revs->topo_order = 1;)
+gitopt_eat(opt_date_order, g_revs->topo_order = 1; g_revs->lifo = 0;)
+gitopt_eat(opt_parents, g_revs->parents = 1;)
+gitopt_eat(opt_dense, g_revs->dense = 1;)
+gitopt_eat(opt_sparse, g_revs->dense = 0;)
+gitopt_eat(opt_remove_empty, g_revs->remove_empty_trees = 1;)
+gitopt_eat(opt_no_merges, g_revs->no_merges = 1;)
+gitopt_eat(opt_boundary, g_revs->boundary = 1;)
+gitopt_eat(opt_objects, g_revs->tag_objects = g_revs->tree_objects =
+ g_revs->blob_objects = 1;)
+gitopt_eat(opt_objects_edge, g_revs->tag_objects = g_revs->tree_objects =
+ g_revs->blob_objects = g_revs->edge_hint = 1;)
+gitopt_eat(opt_unpacked, g_revs->unpacked = 1;)
+gitopt_eat(opt_r, g_revs->diff = g_revs->diffopt.recursive = 1;)
+gitopt_eat(opt_t, g_revs->diff = g_revs->diffopt.recursive =
+ g_revs->diffopt.tree_in_recursive = 1;)
+gitopt_eat(opt_m, g_revs->ignore_merges = 0;)
+gitopt_eat(opt_c, g_revs->diff = g_revs->combine_merges = 1;
+ g_revs->dense_combined_merges = 0;)
+gitopt_eat(opt_cc, g_revs->diff = g_revs->combine_merges =
+ g_revs->dense_combined_merges = 1;)
+gitopt_eat(opt_v, g_revs->verbose_header = 1;)
+gitopt_eat_arg(opt_pretty, g_revs->verbose_header = 1;
+ g_revs->commit_format = get_commit_format(
+ ea->argv[0]);)
+gitopt_eat(opt_root, g_revs->show_root_diff = 1;)
+gitopt_eat(opt_no_commit_id, g_revs->no_commit_id = 1;)
+gitopt_eat(opt_always, g_revs->always_show_header = 1;)
+gitopt_opt_abbrev(g_revs->abbrev)
+gitopt_eat(opt_no_abbrev, g_revs->abbrev = 0;)
+gitopt_eat(opt_abbrev_commit, g_revs->abbrev_commit = 1;)
+gitopt_eat(opt_full_diff, g_revs->full_diff = g_revs->diff = 1;)
+
+static const struct opt_spec setup_revisions_ost[] = {
+ { "max-count", 'n', "%s", ARG_INT, opt_max_count },
+ { 0, ' ', "%s", ARG_INT, opt_max_count },
+ { "max-age", 0, "%s", ARG_INT, opt_max_age },
+ { "min-age", 0, "%s", ARG_INT, opt_min_age },
+ { "since", 0, "%s", ARG_ONE, opt_since },
+ { "after", 0, "%s", ARG_ONE, opt_since },
+ { "before", 0, "%s", ARG_ONE, opt_until },
+ { "until", 0, "%s", ARG_ONE, opt_until },
+ { "all", 0, 0, 0, opt_all },
+ { "not", 0, 0, 0, opt_not },
+ { "default", 0, "%s", ARG_ONE, opt_default },
+ { "topo-order", 0, 0, 0, opt_topo_order },
+ { "date-order", 0, 0, 0, opt_date_order },
+ { "parents", 0, 0, 0, opt_parents },
+ { "dense", 0, 0, 0, opt_dense },
+ { "sparse", 0, 0, 0, opt_sparse },
+ { "remove-empty", 0, 0, 0, opt_remove_empty },
+ { "no-merges", 0, 0, 0, opt_no_merges },
+ { "boundary", 0, 0, 0, opt_boundary },
+ { "objects", 0, 0, 0, opt_objects },
+ { "objects-edge", 0, 0, 0, opt_objects_edge },
+ { "unpacked", 0, 0, 0, opt_unpacked },
+ { 0, 'r', 0, 0, opt_r },
+ { 0, 't', 0, 0, opt_t },
+ { 0, 'm', 0, 0, opt_m },
+ { 0, 'c', 0, 0, opt_c },
+ { "cc", 0, 0, 0, opt_cc },
+ { 0, 'v', 0, 0, opt_v },
+ { "pretty", 0, "%s", ARG_ONE, opt_pretty },
+ { "root", 0, 0, 0, opt_root },
+ { "no-commit-id", 0, 0, 0, opt_no_commit_id },
+ { "always", 0, 0, 0, opt_always },
+ { "no-abbrev", 0, 0, 0, opt_no_abbrev },
+ abbrev_ost_row,
+ { "abbrev-commit", 0, 0, 0, opt_abbrev_commit },
+ { "full-diff", 0, 0, 0, opt_full_diff },
+ { 0, 0 }
+};
+
+/* call this for every non-option (and everything after "--") we have */
+static void setup_revisions_non_option_cb(struct exec_args *b,
+ const int argc, const char **argv, int *argc_pos)
+{
+ int i = *argc_pos;
+ const char *arg = argv[i];
+ unsigned char sha1[20];
+ struct object *object;
+ char *dotdot;
+ int local_flags;
+
+ if (arg[0] == '-') { /* handle diff options: */
+ int opts = diff_opt_parse(&(g_revs->diffopt),
+ argv + i, argc - 1);
+ if (opts > 0) {
+ g_revs->diff = 1;
+ *argc_pos += opts - 1;
+ return;
+ }
+ if (g_extra_ost) {
+ void (*old_non_option_cb)(struct exec_args *b,
+ const int argc, const char **argv,
+ int *argc_pos);
+ int j = 0;
+ old_non_option_cb = gitopt_non_option_cb;
+ gitopt_non_option_cb = gitopt_default_non_option_cb;
+ gitopt_parse_one_opt(b, b, g_extra_ost,
+ argc - i, argv + i, &j);
+ gitopt_non_option_cb = old_non_option_cb;
+ *argc_pos += j;
+ }
+ return;
+ }
+
+ /* otherwise it's a revision */
+ dotdot = strstr(arg, "..");
+ if (dotdot) {
+ unsigned char from_sha1[20];
+ const char *next = dotdot + 2;
+ const char *this = arg;
+ *dotdot = 0;
+ if (!*next)
+ next = "HEAD";
+ if (dotdot == arg)
+ this = "HEAD";
+ if (!get_sha1(this, from_sha1) &&
+ !get_sha1(next, sha1)) {
+ struct object *exclude;
+ struct object *include;
+
+ exclude = get_reference(g_revs, this, from_sha1,
+ g_flags ^ UNINTERESTING);
+ include = get_reference(g_revs, next, sha1, g_flags);
+ if (!exclude || !include)
+ die("Invalid revision range %s..%s", arg, next);
+
+ if (!gitopt_dd_seen) {
+ *dotdot = '.';
+ verify_non_filename(g_revs->prefix, arg);
+ }
+ add_pending_object(g_revs, exclude, this);
+ add_pending_object(g_revs, include, next);
+ return;
+ }
+ *dotdot = '.';
+ }
+
+ dotdot = strstr(arg, "^@");
+ if (dotdot && !dotdot[2]) {
+ *dotdot = 0;
+ if (add_parents_only(g_revs, arg, g_flags))
+ return;
+ *dotdot = '^';
+ }
+ local_flags = 0;
+ if (*arg == '^') {
+ local_flags = UNINTERESTING;
+ arg++;
+ }
+ if (get_sha1(arg, sha1) < 0) {
+ int j;
+
+ if (gitopt_dd_seen || local_flags)
+ die("bad revision '%s'", arg);
+
+ /* If we didn't have a "--":
+ * (1) all filenames must exist;
+ * (2) all rev-args must not be interpretable
+ * as a valid filename.
+ * but the latter we have checked in the main loop.
+ */
+ for (j = *argc_pos; j < argc; j++)
+ verify_filename(g_revs->prefix, argv[j]);
+
+ g_revs->prune_data = get_pathspec(g_revs->prefix,
+ argv + *argc_pos);
+ return;
+ }
+ if (!gitopt_dd_seen)
+ verify_non_filename(g_revs->prefix, arg);
+ object = get_reference(g_revs, arg, sha1, g_flags ^ local_flags);
+ add_pending_object(g_revs, object, arg);
+}
+
/*
* Parse revision information, filling in the "rev_info" structure,
* and removing the used arguments from the argument list.
@@ -541,14 +734,19 @@ void init_revisions(struct rev_info *rev
* Returns the number of arguments left that weren't recognized
* (which are also moved to the head of the argument list)
*/
-int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
+struct exec_args *setup_revisions(int argc, const char **argv,
+ struct rev_info *revs, const char *def,
+ const struct opt_spec *extra_ost)
{
- int i, flags, seen_dashdash;
- const char **unrecognized = argv + 1;
- int left = 1;
+ int i;
+ struct exec_args *a, *b;
+
+ g_extra_ost = extra_ost;
+ g_def = def;
+ g_revs = revs;
+ gitopt_dd_seen = 0;
/* First, search for "--" */
- seen_dashdash = 0;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (strcmp(arg, "--"))
@@ -556,274 +754,24 @@ int setup_revisions(int argc, const char
argv[i] = NULL;
argc = i;
revs->prune_data = get_pathspec(revs->prefix, argv + i + 1);
- seen_dashdash = 1;
+ gitopt_dd_seen = 1;
break;
}
- flags = 0;
- for (i = 1; i < argc; i++) {
- struct object *object;
- const char *arg = argv[i];
- unsigned char sha1[20];
- char *dotdot;
- int local_flags;
-
- if (*arg == '-') {
- int opts;
- if (!strncmp(arg, "--max-count=", 12)) {
- revs->max_count = atoi(arg + 12);
- continue;
- }
- /* accept -<digit>, like traditional "head" */
- if ((*arg == '-') && isdigit(arg[1])) {
- revs->max_count = atoi(arg + 1);
- continue;
- }
- if (!strcmp(arg, "-n")) {
- if (argc <= i + 1)
- die("-n requires an argument");
- revs->max_count = atoi(argv[++i]);
- continue;
- }
- if (!strncmp(arg,"-n",2)) {
- revs->max_count = atoi(arg + 2);
- continue;
- }
- if (!strncmp(arg, "--max-age=", 10)) {
- revs->max_age = atoi(arg + 10);
- continue;
- }
- if (!strncmp(arg, "--since=", 8)) {
- revs->max_age = approxidate(arg + 8);
- continue;
- }
- if (!strncmp(arg, "--after=", 8)) {
- revs->max_age = approxidate(arg + 8);
- continue;
- }
- if (!strncmp(arg, "--min-age=", 10)) {
- revs->min_age = atoi(arg + 10);
- continue;
- }
- if (!strncmp(arg, "--before=", 9)) {
- revs->min_age = approxidate(arg + 9);
- continue;
- }
- if (!strncmp(arg, "--until=", 8)) {
- revs->min_age = approxidate(arg + 8);
- continue;
- }
- if (!strcmp(arg, "--all")) {
- handle_all(revs, flags);
- continue;
- }
- if (!strcmp(arg, "--not")) {
- flags ^= UNINTERESTING;
- continue;
- }
- if (!strcmp(arg, "--default")) {
- if (++i >= argc)
- die("bad --default argument");
- def = argv[i];
- continue;
- }
- if (!strcmp(arg, "--topo-order")) {
- revs->topo_order = 1;
- continue;
- }
- if (!strcmp(arg, "--date-order")) {
- revs->lifo = 0;
- revs->topo_order = 1;
- continue;
- }
- if (!strcmp(arg, "--parents")) {
- revs->parents = 1;
- continue;
- }
- if (!strcmp(arg, "--dense")) {
- revs->dense = 1;
- continue;
- }
- if (!strcmp(arg, "--sparse")) {
- revs->dense = 0;
- continue;
- }
- if (!strcmp(arg, "--remove-empty")) {
- revs->remove_empty_trees = 1;
- continue;
- }
- if (!strcmp(arg, "--no-merges")) {
- revs->no_merges = 1;
- continue;
- }
- if (!strcmp(arg, "--boundary")) {
- revs->boundary = 1;
- continue;
- }
- if (!strcmp(arg, "--objects")) {
- revs->tag_objects = 1;
- revs->tree_objects = 1;
- revs->blob_objects = 1;
- continue;
- }
- if (!strcmp(arg, "--objects-edge")) {
- revs->tag_objects = 1;
- revs->tree_objects = 1;
- revs->blob_objects = 1;
- revs->edge_hint = 1;
- continue;
- }
- if (!strcmp(arg, "--unpacked")) {
- revs->unpacked = 1;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- revs->diff = 1;
- revs->diffopt.recursive = 1;
- continue;
- }
- if (!strcmp(arg, "-t")) {
- revs->diff = 1;
- revs->diffopt.recursive = 1;
- revs->diffopt.tree_in_recursive = 1;
- continue;
- }
- if (!strcmp(arg, "-m")) {
- revs->ignore_merges = 0;
- continue;
- }
- if (!strcmp(arg, "-c")) {
- revs->diff = 1;
- revs->dense_combined_merges = 0;
- revs->combine_merges = 1;
- continue;
- }
- if (!strcmp(arg, "--cc")) {
- revs->diff = 1;
- revs->dense_combined_merges = 1;
- revs->combine_merges = 1;
- continue;
- }
- if (!strcmp(arg, "-v")) {
- revs->verbose_header = 1;
- continue;
- }
- if (!strncmp(arg, "--pretty", 8)) {
- revs->verbose_header = 1;
- revs->commit_format = get_commit_format(arg+8);
- continue;
- }
- if (!strcmp(arg, "--root")) {
- revs->show_root_diff = 1;
- continue;
- }
- if (!strcmp(arg, "--no-commit-id")) {
- revs->no_commit_id = 1;
- continue;
- }
- if (!strcmp(arg, "--always")) {
- revs->always_show_header = 1;
- continue;
- }
- if (!strcmp(arg, "--no-abbrev")) {
- revs->abbrev = 0;
- continue;
- }
- if (!strcmp(arg, "--abbrev")) {
- revs->abbrev = DEFAULT_ABBREV;
- continue;
- }
- if (!strcmp(arg, "--abbrev-commit")) {
- revs->abbrev_commit = 1;
- continue;
- }
- if (!strcmp(arg, "--full-diff")) {
- revs->diff = 1;
- revs->full_diff = 1;
- continue;
- }
- opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
- if (opts > 0) {
- revs->diff = 1;
- i += opts - 1;
- continue;
- }
- *unrecognized++ = arg;
- left++;
- continue;
- }
- dotdot = strstr(arg, "..");
- if (dotdot) {
- unsigned char from_sha1[20];
- const char *next = dotdot + 2;
- const char *this = arg;
- *dotdot = 0;
- if (!*next)
- next = "HEAD";
- if (dotdot == arg)
- this = "HEAD";
- if (!get_sha1(this, from_sha1) &&
- !get_sha1(next, sha1)) {
- struct object *exclude;
- struct object *include;
-
- exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
- include = get_reference(revs, next, sha1, flags);
- if (!exclude || !include)
- die("Invalid revision range %s..%s", arg, next);
-
- if (!seen_dashdash) {
- *dotdot = '.';
- verify_non_filename(revs->prefix, arg);
- }
- add_pending_object(revs, exclude, this);
- add_pending_object(revs, include, next);
- continue;
- }
- *dotdot = '.';
- }
- dotdot = strstr(arg, "^@");
- if (dotdot && !dotdot[2]) {
- *dotdot = 0;
- if (add_parents_only(revs, arg, flags))
- continue;
- *dotdot = '^';
- }
- local_flags = 0;
- if (*arg == '^') {
- local_flags = UNINTERESTING;
- arg++;
- }
- if (get_sha1(arg, sha1)) {
- int j;
-
- if (seen_dashdash || local_flags)
- die("bad revision '%s'", arg);
-
- /* If we didn't have a "--":
- * (1) all filenames must exist;
- * (2) all rev-args must not be interpretable
- * as a valid filename.
- * but the latter we have checked in the main loop.
- */
- for (j = i; j < argc; j++)
- verify_filename(revs->prefix, argv[j]);
+ a = new_exec_args(argc);
+ b = new_exec_args(argc);
+ gitopt_pass_through = 1;
+ gitopt_non_option_cb = setup_revisions_non_option_cb;
+ if (gitopt_parse_ost_split(a, b, setup_revisions_ost, argc, argv))
+ return b;
- revs->prune_data = get_pathspec(revs->prefix, argv + i);
- break;
- }
- if (!seen_dashdash)
- verify_non_filename(revs->prefix, arg);
- object = get_reference(revs, arg, sha1, flags ^ local_flags);
- add_pending_object(revs, object, arg);
- }
- if (def && !revs->pending_objects) {
+ if (g_def && !revs->pending_objects) {
unsigned char sha1[20];
struct object *object;
- if (get_sha1(def, sha1))
- die("bad default revision '%s'", def);
- object = get_reference(revs, def, sha1, 0);
- add_pending_object(revs, object, def);
+ if (get_sha1(g_def, sha1))
+ die("bad default revision '%s'", g_def);
+ object = get_reference(revs, g_def, sha1, 0);
+ add_pending_object(revs, object, g_def);
}
if (revs->topo_order || revs->unpacked)
@@ -844,7 +792,7 @@ int setup_revisions(int argc, const char
revs->diffopt.abbrev = revs->abbrev;
diff_setup_done(&revs->diffopt);
- return left;
+ return b;
}
void prepare_revision_walk(struct rev_info *revs)
diff --git a/revision.h b/revision.h
index 48d7b4c..11a5820 100644
--- a/revision.h
+++ b/revision.h
@@ -1,6 +1,8 @@
#ifndef REVISION_H
#define REVISION_H
+#include "gitopt.h"
+
#define SEEN (1u<<0)
#define UNINTERESTING (1u<<1)
#define TREECHANGE (1u<<2)
@@ -81,7 +83,9 @@ extern int rev_same_tree_as_empty(struct
extern int rev_compare_tree(struct rev_info *, struct tree *t1, struct tree *t2);
extern void init_revisions(struct rev_info *revs);
-extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
+extern struct exec_args *setup_revisions(int argc, const char **argv,
+ struct rev_info *revs, const char *def,
+ const struct opt_spec *extra_ost);
extern void prepare_revision_walk(struct rev_info *revs);
extern struct commit *get_revision(struct rev_info *revs);
--
1.3.2.g0a3ae
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 6/6] commit: allow --pretty= args to be abbreviated
2006-05-09 5:06 ` [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse() Eric Wong
@ 2006-05-09 5:06 ` Eric Wong
2006-05-09 7:16 ` [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse() Eric Wong
2006-05-11 20:19 ` Eric Wong
2 siblings, 0 replies; 22+ messages in thread
From: Eric Wong @ 2006-05-09 5:06 UTC (permalink / raw)
To: git; +Cc: Eric Wong
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
commit.c | 42 +++++++++++++++++++++++++++++-------------
1 files changed, 29 insertions(+), 13 deletions(-)
0a3aed7c25eca808b29f2318d5e4c087e03b9bfb
diff --git a/commit.c b/commit.c
index a056b25..055064a 100644
--- a/commit.c
+++ b/commit.c
@@ -22,23 +22,39 @@ struct sort_node
const char *commit_type = "commit";
+struct cmt_fmt_map {
+ const char *n;
+ enum cmit_fmt v;
+} cmt_fmts[] = {
+ { "raw", CMIT_FMT_RAW },
+ { "medium", CMIT_FMT_MEDIUM },
+ { "short", CMIT_FMT_SHORT },
+ { "full", CMIT_FMT_FULL },
+ { "fuller", CMIT_FMT_FULLER },
+ { "oneline", CMIT_FMT_ONELINE },
+};
+
enum cmit_fmt get_commit_format(const char *arg)
{
+ int i, found = -1;
if (!*arg)
return CMIT_FMT_DEFAULT;
- if (!strcmp(arg, "raw"))
- return CMIT_FMT_RAW;
- if (!strcmp(arg, "medium"))
- return CMIT_FMT_MEDIUM;
- if (!strcmp(arg, "short"))
- return CMIT_FMT_SHORT;
- if (!strcmp(arg, "full"))
- return CMIT_FMT_FULL;
- if (!strcmp(arg, "fuller"))
- return CMIT_FMT_FULLER;
- if (!strcmp(arg, "oneline"))
- return CMIT_FMT_ONELINE;
- die("invalid --pretty format");
+ for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
+ if (!strcmp(arg, cmt_fmts[i].n))
+ return cmt_fmts[i].v;
+ }
+
+ /* look for abbreviations */
+ for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
+ if (strstr(cmt_fmts[i].n, arg)) {
+ if (found >= 0)
+ die("invalid --pretty format: %s", arg);
+ found = i;
+ }
+ }
+ if (found >= 0)
+ return cmt_fmts[found].v;
+ die("invalid --pretty format: %s", arg);
}
static struct commit *check_commit(struct object *obj,
--
1.3.2.g0a3ae
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse()
2006-05-09 5:06 ` [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse() Eric Wong
2006-05-09 5:06 ` [PATCH 6/6] commit: allow --pretty= args to be abbreviated Eric Wong
@ 2006-05-09 7:16 ` Eric Wong
2006-05-11 20:19 ` Eric Wong
2 siblings, 0 replies; 22+ messages in thread
From: Eric Wong @ 2006-05-09 7:16 UTC (permalink / raw)
To: git
Eric Wong <normalperson@yhbt.net> wrote:
> I've added --raw to diff_opt_parse() for consistency's sake:
> --patch-with-raw would otherwise be inconsistently abbreviated
> to --raw if it wasn't added.
I was looking for this patch, but then I realized it slipped in here.
I've added --unified=<num>/-u<num> context here so I don't have to go
through the awkwardness of setting GIT_DIFF_OPTS in the environment.
--
Eric Wong
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH/RFC] gitopt - command-line parsing enhancements
2006-05-09 5:06 [PATCH/RFC] gitopt - command-line parsing enhancements Eric Wong
2006-05-09 5:06 ` [PATCH 1/6] gitopt: a new command-line option parser for git Eric Wong
@ 2006-05-09 8:35 ` Junio C Hamano
2006-05-09 19:48 ` Eric Wong
1 sibling, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2006-05-09 8:35 UTC (permalink / raw)
To: Eric Wong; +Cc: git
Eric Wong <normalperson@yhbt.net> writes:
> Here's my take at a new command-line option parser to reduce wear on my
> fingers. It handles both long and short options, permuting, automatic
> abbreviations, required arguments, optional arguments, and bundling.
Taken a superficial look at it.
Sounds nice, might be a tad too ambitious though. Looks
intrusive at places.
And scary, especially the "eat" macros are very scary.
I have to think about it a bit.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 5:06 ` [PATCH 1/6] gitopt: a new command-line option parser for git Eric Wong
2006-05-09 5:06 ` [PATCH 2/6] update-index: convert to using gitopt Eric Wong
@ 2006-05-09 9:08 ` Timo Hirvonen
2006-05-09 12:58 ` Junio C Hamano
2006-05-09 19:18 ` Eric Wong
1 sibling, 2 replies; 22+ messages in thread
From: Timo Hirvonen @ 2006-05-09 9:08 UTC (permalink / raw)
To: normalperson; +Cc: git
Eric Wong <normalperson@yhbt.net> wrote:
> * unbundling of short options: -uC20n20z => -u -C20 -n20 -z
Does anyone ever use this? I think this makes sense only for flags that
don't have parameters but that would create an ugly special case. Is it
too hard to type "-u -C=20 -n=20 -z"?
> * optional argument handling (-C<num>, -M<num>)
> -C <num> (with a space between them) has not changed,
> however, <num> can be a sha1, or a path
IMO optional arguments are usually bad idea.
-C 2 (is "2" argument?)
-C2 (-C=2 or -C -2?)
Better to make it obvious there's an argument
-C=2
or not support optional arguments at all and "-C 2" becomes unambiguous.
--
http://onion.dynserv.net/~timo/
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 9:08 ` [PATCH 1/6] gitopt: a new command-line option parser for git Timo Hirvonen
@ 2006-05-09 12:58 ` Junio C Hamano
2006-05-09 19:39 ` Eric Wong
2006-05-09 19:18 ` Eric Wong
1 sibling, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2006-05-09 12:58 UTC (permalink / raw)
To: Timo Hirvonen, Eric Wong; +Cc: git
Timo Hirvonen <tihirvon@gmail.com> writes:
> Eric Wong <normalperson@yhbt.net> wrote:
>
>> * unbundling of short options: -uC20n20z => -u -C20 -n20 -z
>
> Does anyone ever use this? I think this makes sense only for flags that
> don't have parameters but that would create an ugly special case. Is it
> too hard to type "-u -C=20 -n=20 -z"?
I can already hear in my head that people would start talking
about "git understands insane abbeviations of options". It
might be unambiguous, but that does not change it is a bit on
the insane side. People would probably expect -nuz can be split
into -n -u -z, and the current handcrafted mess (although it is
more obvious and easy to work with when reading and maintaining
the existing code) is not abbreviation friendly, which we would
want to do something about. But I think squashing options with
parameters together is going a bit too far.
> --with-r => --patch-with-raw works great
I personally think this also is too much.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 9:08 ` [PATCH 1/6] gitopt: a new command-line option parser for git Timo Hirvonen
2006-05-09 12:58 ` Junio C Hamano
@ 2006-05-09 19:18 ` Eric Wong
2006-05-09 20:10 ` Timo Hirvonen
1 sibling, 1 reply; 22+ messages in thread
From: Eric Wong @ 2006-05-09 19:18 UTC (permalink / raw)
To: Timo Hirvonen; +Cc: git
Timo Hirvonen <tihirvon@gmail.com> wrote:
> Eric Wong <normalperson@yhbt.net> wrote:
>
> > * unbundling of short options: -uC20n20z => -u -C20 -n20 -z
>
> Does anyone ever use this? I think this makes sense only for flags that
> don't have parameters but that would create an ugly special case. Is it
> too hard to type "-u -C=20 -n=20 -z"?
It is more for me. Many programs that I use already accept bundled
switches, and '=' and '-' are relatively far away and requires me
to stretch my hand uncomfortably (I have very small hands, and have
limited mobility in several fingers).
> > * optional argument handling (-C<num>, -M<num>)
> > -C <num> (with a space between them) has not changed,
> > however, <num> can be a sha1, or a path
>
> IMO optional arguments are usually bad idea.
>
> -C 2 (is "2" argument?)
> -C2 (-C=2 or -C -2?)
>
> Better to make it obvious there's an argument
>
> -C=2
>
> or not support optional arguments at all and "-C 2" becomes unambiguous.
git has always supported optional argument handling like this.
I'm striving for backwards compatibility with existing usage. That
means as a diff option, -C alone works, as does -C20. I've made -C 20
_not_ work because it breaks existing usage (where 20 could be a
filename, or a tree-ish). -C=20 would mean something
else, since I wanted to make pickaxe work exactly as it did before:
-S=var would search for '=var', not 'var'.
--
Eric Wong
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 12:58 ` Junio C Hamano
@ 2006-05-09 19:39 ` Eric Wong
0 siblings, 0 replies; 22+ messages in thread
From: Eric Wong @ 2006-05-09 19:39 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Timo Hirvonen, git
Junio C Hamano <junkio@cox.net> wrote:
> Timo Hirvonen <tihirvon@gmail.com> writes:
>
> > Eric Wong <normalperson@yhbt.net> wrote:
> >
> >> * unbundling of short options: -uC20n20z => -u -C20 -n20 -z
> >
> > Does anyone ever use this? I think this makes sense only for flags that
> > don't have parameters but that would create an ugly special case. Is it
> > too hard to type "-u -C=20 -n=20 -z"?
>
> I can already hear in my head that people would start talking
> about "git understands insane abbeviations of options". It
> might be unambiguous, but that does not change it is a bit on
> the insane side. People would probably expect -nuz can be split
> into -n -u -z, and the current handcrafted mess (although it is
> more obvious and easy to work with when reading and maintaining
> the existing code) is not abbreviation friendly, which we would
> want to do something about. But I think squashing options with
> parameters together is going a bit too far.
I think numeric parameters are unambiguous when bundled.
I'm used to things like `diff -ru10p` working, *shrug*
Non-numeric parameters can only be used if the option is at the end of the
bundled string:
git commit -sam'this is my commit message' would work
git commit -m'say hello' would also work
but git commit -mas'this is my commit message' would not work as intended
(where user wanted -a -s, too)
> > --with-r => --patch-with-raw works great
>
> I personally think this also is too much.
There are (currently) two types of abbreviations, one is the prefix one used
commonly in shell scripts: -e|--e|--ed|--edi|--edit. I think this should
always be supported as most of our shell scripts already do.
The other one tokenizes on '-' first and looks for a prefix match after
each '-'. I'd like to make that at least optional:
diff --git a/gitopt.c b/gitopt.c
index 056e163..9ca6025 100644
--- a/gitopt.c
+++ b/gitopt.c
gitopt.c
@@ -427,7 +427,7 @@ static void fallback_long(const struct o
}
/* ok, try harder, based on tokenization on '-' */
- if (found < 0) {
+ if (found < 0 && getenv("GIT_ABBREV_HARDER")) {
for (i = 0; ost[i].l || ost[i].s; i++) {
s = &(ost[i]);
if (s->l && opt_token_match(s,cur)) {
--
Eric Wong
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH/RFC] gitopt - command-line parsing enhancements
2006-05-09 8:35 ` [PATCH/RFC] gitopt - command-line parsing enhancements Junio C Hamano
@ 2006-05-09 19:48 ` Eric Wong
2006-05-09 20:28 ` Junio C Hamano
0 siblings, 1 reply; 22+ messages in thread
From: Eric Wong @ 2006-05-09 19:48 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <junkio@cox.net> wrote:
> Eric Wong <normalperson@yhbt.net> writes:
>
> > Here's my take at a new command-line option parser to reduce wear on my
> > fingers. It handles both long and short options, permuting, automatic
> > abbreviations, required arguments, optional arguments, and bundling.
>
> Taken a superficial look at it.
>
> Sounds nice, might be a tad too ambitious though. Looks
> intrusive at places.
I wasn't overly happy with the addition of global variables to existing
files and the way they're set (setup_revisions). But at least they're
static. Of course, I'm not yet certain that I haven't introduced new
bugs. All the tests pass, at least...
> And scary, especially the "eat" macros are very scary.
They look weird at first, but I think they help readability and
maintainability once you get used to them. They let you focus on the
important part of the function while hiding the boring parts from you.
Quite elegant, imho.
--
Eric Wong
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 19:18 ` Eric Wong
@ 2006-05-09 20:10 ` Timo Hirvonen
2006-05-09 20:35 ` Junio C Hamano
0 siblings, 1 reply; 22+ messages in thread
From: Timo Hirvonen @ 2006-05-09 20:10 UTC (permalink / raw)
To: Eric Wong; +Cc: git
Eric Wong <normalperson@yhbt.net> wrote:
> I'm striving for backwards compatibility with existing usage. That
> means as a diff option, -C alone works, as does -C20. I've made -C 20
> _not_ work because it breaks existing usage (where 20 could be a
> filename, or a tree-ish). -C=20 would mean something
> else,
I think optional arguments are still confusing. We could support both
-C (no args) and -C=20 syntax.
> since I wanted to make pickaxe work exactly as it did before:
> -S=var would search for '=var', not 'var'.
Some other options use -x=y syntax so this would be confusing. Pickaxe's
-Stext syntax is a bit strange. I think -S text or -S=text would be
more logical.
--
http://onion.dynserv.net/~timo/
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH/RFC] gitopt - command-line parsing enhancements
2006-05-09 19:48 ` Eric Wong
@ 2006-05-09 20:28 ` Junio C Hamano
2006-05-09 21:14 ` Eric Wong
0 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2006-05-09 20:28 UTC (permalink / raw)
To: Eric Wong; +Cc: git
Eric Wong <normalperson@yhbt.net> writes:
>> And scary, especially the "eat" macros are very scary.
>
> They look weird at first, but I think they help readability and
> maintainability once you get used to them. They let you focus on the
> important part of the function while hiding the boring parts from you.
> Quite elegant, imho.
Sorry, there is no elegance to it as far as I can see. A macro
invocation that creates a private function while it does not
look like a function definition is already bad, you cannot have
a comma in the stmt part, and the bare semicolons in the
parenthesised text look insane.
If your patch were like this, it would have been a bit easier
for me to understand what was going on during my first pass:
static struct exec_args *ui_optparse
(struct opt_spec *s, int ac, char **av, int *ac_p, int what)
{
struct exec_args *ea = one_arg(s, ac, av, ac_p);
if (!ea) return NULL;
switch (what) {
case IGNORE_MISSING:
not_new = 1; break;
case VERBOSE:
verbose = 1; break;
case HELP:
usage(update_index_usage); break;
}
return nil_exec_args(ea);
}
instead of
gitopt_eat(opt_ignore_missing, not_new = 1;)
gitopt_eat(opt_verbose, verbose = 1;)
gitopt_eat(opt_h, usage(update_index_usage);)
Then, you would give an extra element in the table, and your
argument parsing/splitting routine passes that one to the
handler function:
static const struct opt_spec update_index_ost[] = {
...
{ "ignore-missing", 0, 0, 0, ui_optparse, IGNORE_MISSING },
{ "verbose", 0, 0, 0, ui_optparse, VERBOSE },
{ "help", 'h', 0, 0, ui_optparse, HELP },
{ 0, 0 }
Another thing is I do not think we would want to make the
argument parsing into callback style interface like you did. It
actively encourages the option variables to be global (you could
make it file scoped static but they are global nevertheless).
If you can make it an iterator style, it would be a lot easier
to see what is going on, I suspect. Then you would not even
need the callback function pointers and small functions created
by magic eat() macros.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 20:10 ` Timo Hirvonen
@ 2006-05-09 20:35 ` Junio C Hamano
2006-05-09 21:08 ` Timo Hirvonen
0 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2006-05-09 20:35 UTC (permalink / raw)
To: Timo Hirvonen; +Cc: git, Eric Wong
Timo Hirvonen <tihirvon@gmail.com> writes:
> I think optional arguments are still confusing. We could support both
> -C (no args) and -C=20 syntax.
Actually, optional numeric arguments are the norm not exception.
Think of "diff -u" vs "diff -u20" for example. Also I think it
is conventional not to use = for single-letter single-dash
options, so -C (no args -- use the default number of the
implementation whatever it is) and -C20 (the same behaviour in
principle as -C, but use my number instead of the default) are
sane, while -C=20 _is_ odd.
Having said that, I think abbreviating -u20 -n -r to -u20nr
going too far (-nru20 would be palatable perhaps), even if the
implementation allows such, unless you are entering "obfusucated
command line parameters" contest.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 20:35 ` Junio C Hamano
@ 2006-05-09 21:08 ` Timo Hirvonen
2006-05-09 21:11 ` Junio C Hamano
0 siblings, 1 reply; 22+ messages in thread
From: Timo Hirvonen @ 2006-05-09 21:08 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, normalperson
Junio C Hamano <junkio@cox.net> wrote:
> Timo Hirvonen <tihirvon@gmail.com> writes:
>
> > I think optional arguments are still confusing. We could support both
> > -C (no args) and -C=20 syntax.
>
> Actually, optional numeric arguments are the norm not exception.
> Think of "diff -u" vs "diff -u20" for example. Also I think it
> is conventional not to use = for single-letter single-dash
> options, so -C (no args -- use the default number of the
> implementation whatever it is) and -C20 (the same behaviour in
> principle as -C, but use my number instead of the default) are
> sane, while -C=20 _is_ odd.
OK, if we don't support bundling flags at all then -x=y and -xy would do
the same thing and pickaxe's -Stext would work too. But we could not
make option flag parsing global then (-S=value -> search "=value" or
"value"?).
Maybe we should just change -Stext to -S=text or -S text.
Better to support only -x=y or -x y, not both.
> Having said that, I think abbreviating -u20 -n -r to -u20nr
> going too far
Yes
> (-nru20 would be palatable perhaps),
No :)
--
http://onion.dynserv.net/~timo/
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 21:08 ` Timo Hirvonen
@ 2006-05-09 21:11 ` Junio C Hamano
2006-05-09 21:31 ` Timo Hirvonen
0 siblings, 1 reply; 22+ messages in thread
From: Junio C Hamano @ 2006-05-09 21:11 UTC (permalink / raw)
To: Timo Hirvonen; +Cc: git
Timo Hirvonen <tihirvon@gmail.com> writes:
> Better to support only -x=y or -x y, not both.
Didn't I just say -x=y where x is a single letter _is_ odd?
It is either -xy or -x y, not -x=y.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH/RFC] gitopt - command-line parsing enhancements
2006-05-09 20:28 ` Junio C Hamano
@ 2006-05-09 21:14 ` Eric Wong
0 siblings, 0 replies; 22+ messages in thread
From: Eric Wong @ 2006-05-09 21:14 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <junkio@cox.net> wrote:
> If you can make it an iterator style, it would be a lot easier
> to see what is going on, I suspect. Then you would not even
> need the callback function pointers and small functions created
> by magic eat() macros.
That sounds like a great idea, I'll work on it tonight.
--
Eric Wong
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] gitopt: a new command-line option parser for git
2006-05-09 21:11 ` Junio C Hamano
@ 2006-05-09 21:31 ` Timo Hirvonen
0 siblings, 0 replies; 22+ messages in thread
From: Timo Hirvonen @ 2006-05-09 21:31 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git
Junio C Hamano <junkio@cox.net> wrote:
> Timo Hirvonen <tihirvon@gmail.com> writes:
>
> > Better to support only -x=y or -x y, not both.
>
> Didn't I just say -x=y where x is a single letter _is_ odd?
> It is either -xy or -x y, not -x=y.
Oh, I thought parameters would use same syntax for short and long
options. For optional args -C2 would make sense but -C 2 would be
ambiguous ("-C -- 2" or "-C2"?). Maybe I'm just too drunk to
understand.
--
http://onion.dynserv.net/~timo/
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse()
2006-05-09 5:06 ` [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse() Eric Wong
2006-05-09 5:06 ` [PATCH 6/6] commit: allow --pretty= args to be abbreviated Eric Wong
2006-05-09 7:16 ` [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse() Eric Wong
@ 2006-05-11 20:19 ` Eric Wong
2 siblings, 0 replies; 22+ messages in thread
From: Eric Wong @ 2006-05-11 20:19 UTC (permalink / raw)
To: git
In other news, this patch is broken. Bundled args won't work if some
in the bundle are handled setup_revisions() and some are handled by
diff_opt_parse().
I'll work on fixing this, as well (may take a while working mostly one-handed).
--
Eric Wong
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2006-05-11 20:19 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-05-09 5:06 [PATCH/RFC] gitopt - command-line parsing enhancements Eric Wong
2006-05-09 5:06 ` [PATCH 1/6] gitopt: a new command-line option parser for git Eric Wong
2006-05-09 5:06 ` [PATCH 2/6] update-index: convert to using gitopt Eric Wong
2006-05-09 5:06 ` [PATCH 3/6] ls-tree: convert to gitopt Eric Wong
2006-05-09 5:06 ` [PATCH 4/6] ls-files: convert to using gitopt Eric Wong
2006-05-09 5:06 ` [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse() Eric Wong
2006-05-09 5:06 ` [PATCH 6/6] commit: allow --pretty= args to be abbreviated Eric Wong
2006-05-09 7:16 ` [PATCH 5/6] gitopt: convert setup_revisions(), and diff_opt_parse() Eric Wong
2006-05-11 20:19 ` Eric Wong
2006-05-09 9:08 ` [PATCH 1/6] gitopt: a new command-line option parser for git Timo Hirvonen
2006-05-09 12:58 ` Junio C Hamano
2006-05-09 19:39 ` Eric Wong
2006-05-09 19:18 ` Eric Wong
2006-05-09 20:10 ` Timo Hirvonen
2006-05-09 20:35 ` Junio C Hamano
2006-05-09 21:08 ` Timo Hirvonen
2006-05-09 21:11 ` Junio C Hamano
2006-05-09 21:31 ` Timo Hirvonen
2006-05-09 8:35 ` [PATCH/RFC] gitopt - command-line parsing enhancements Junio C Hamano
2006-05-09 19:48 ` Eric Wong
2006-05-09 20:28 ` Junio C Hamano
2006-05-09 21:14 ` Eric Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).