diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Documentation/git-add.txt | 4 | ||||
-rw-r--r-- | Documentation/git-branch.txt | 2 | ||||
-rw-r--r-- | Documentation/git-mv.txt | 2 | ||||
-rw-r--r-- | Documentation/git-rm.txt | 4 | ||||
-rw-r--r-- | Documentation/git-symbolic-ref.txt | 2 | ||||
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | builtin-add.c | 72 | ||||
-rw-r--r-- | builtin-branch.c | 147 | ||||
-rw-r--r-- | builtin-count-objects.c | 32 | ||||
-rw-r--r-- | builtin-describe.c | 70 | ||||
-rw-r--r-- | builtin-for-each-ref.c | 138 | ||||
-rw-r--r-- | builtin-fsck.c | 80 | ||||
-rw-r--r-- | builtin-gc.c | 44 | ||||
-rw-r--r-- | builtin-mv.c | 84 | ||||
-rw-r--r-- | builtin-name-rev.c | 64 | ||||
-rw-r--r-- | builtin-pack-refs.c | 47 | ||||
-rw-r--r-- | builtin-revert.c | 67 | ||||
-rw-r--r-- | builtin-rm.c | 54 | ||||
-rw-r--r-- | builtin-symbolic-ref.c | 52 | ||||
-rw-r--r-- | builtin-update-ref.c | 65 | ||||
-rw-r--r-- | parse-options.c | 322 | ||||
-rw-r--r-- | parse-options.h | 70 | ||||
-rwxr-xr-x | t/t0040-parse-options.sh | 93 | ||||
-rwxr-xr-x[-rw-r--r--] | t/t6300-for-each-ref.sh | 0 | ||||
-rw-r--r-- | test-parse-options.c | 35 |
26 files changed, 938 insertions, 619 deletions
diff --git a/.gitignore b/.gitignore index 8670081adf..c8c13f5503 100644 --- a/.gitignore +++ b/.gitignore @@ -153,6 +153,7 @@ test-delta test-dump-cache-tree test-genrandom test-match-trees +test-parse-options test-sha1 common-cmds.h *.tar.gz diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 2fe7355555..963e1ab1e2 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -50,10 +50,10 @@ OPTIONS and `dir/file2`) can be given to add all files in the directory, recursively. --n:: +-n, \--dry-run:: Don't actually add the file(s), just show if they exist. --v:: +-v, \--verbose:: Be verbose. -f:: diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index b7285bcdbc..5e81aa4ee1 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -85,7 +85,7 @@ OPTIONS -a:: List both remote-tracking branches and local branches. --v:: +-v, --verbose:: Show sha1 and commit subject line for each head. --abbrev=<length>:: diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt index 2c9cf743c7..3b8ca76dff 100644 --- a/Documentation/git-mv.txt +++ b/Documentation/git-mv.txt @@ -34,7 +34,7 @@ OPTIONS condition. An error happens when a source is neither existing nor controlled by GIT, or when it would overwrite an existing file unless '-f' is given. --n:: +-n, \--dry-run:: Do nothing; only show what would happen diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt index be61a82164..48c1d97f93 100644 --- a/Documentation/git-rm.txt +++ b/Documentation/git-rm.txt @@ -30,7 +30,7 @@ OPTIONS -f:: Override the up-to-date check. --n:: +-n, \--dry-run:: Don't actually remove the file(s), just show if they exist in the index. @@ -51,7 +51,7 @@ OPTIONS \--ignore-unmatch:: Exit with a zero status even if no files matched. -\--quiet:: +-q, \--quiet:: git-rm normally outputs one line (in the form of an "rm" command) for each file removed. This option suppresses that output. diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt index a88f722860..694cabab24 100644 --- a/Documentation/git-symbolic-ref.txt +++ b/Documentation/git-symbolic-ref.txt @@ -26,7 +26,7 @@ a regular file whose contents is `ref: refs/heads/master`. OPTIONS ------- --q:: +-q, --quiet:: Do not issue an error message if the <name> is not a symbolic ref but a detached HEAD; instead exit with non-zero status silently. @@ -289,7 +289,7 @@ LIB_H = \ run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \ tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \ utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \ - mailmap.h remote.h transport.h diffcore.h hash.h + mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h DIFF_OBJS = \ diff.o diff-lib.o diffcore-break.o diffcore-order.o \ @@ -312,7 +312,7 @@ LIB_OBJS = \ alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \ color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \ convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \ - transport.o bundle.o walker.o + transport.o bundle.o walker.o parse-options.o BUILTIN_OBJS = \ builtin-add.o \ @@ -974,7 +974,7 @@ endif ### Testing rules -TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X +TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X all:: $(TEST_PROGRAMS) diff --git a/builtin-add.c b/builtin-add.c index dbbb05215f..45b14e8a61 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -13,9 +13,12 @@ #include "commit.h" #include "revision.h" #include "run-command.h" +#include "parse-options.h" -static const char builtin_add_usage[] = -"git-add [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] <filepattern>..."; +static const char * const builtin_add_usage[] = { + "git-add [options] [--] <filepattern>...", + NULL +}; static int take_worktree_changes; static const char *excludes_file; @@ -162,21 +165,30 @@ static struct lock_file lock_file; static const char ignore_error[] = "The following paths are ignored by one of your .gitignore files:\n"; +static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; +static int add_interactive = 0; + +static struct option builtin_add_options[] = { + OPT__DRY_RUN(&show_only), + OPT__VERBOSE(&verbose), + OPT_GROUP(""), + OPT_BOOLEAN('i', "interactive", &add_interactive, "interactive picking"), + OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"), + OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"), + OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"), + OPT_END(), +}; + int cmd_add(int argc, const char **argv, const char *prefix) { - int i, newfd; - int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0; + int i, newfd, orig_argc = argc; const char **pathspec; struct dir_struct dir; - int add_interactive = 0; - for (i = 1; i < argc; i++) { - if (!strcmp("--interactive", argv[i]) || - !strcmp("-i", argv[i])) - add_interactive++; - } + argc = parse_options(argc, argv, builtin_add_options, + builtin_add_usage, 0); if (add_interactive) { - if (argc != 2) + if (add_interactive != 1 || orig_argc != 2) die("add --interactive does not take any parameters"); exit(interactive_add()); } @@ -185,51 +197,19 @@ int cmd_add(int argc, const char **argv, const char *prefix) newfd = hold_locked_index(&lock_file, 1); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "-n")) { - show_only = 1; - continue; - } - if (!strcmp(arg, "-f")) { - ignored_too = 1; - continue; - } - if (!strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "-u")) { - take_worktree_changes = 1; - continue; - } - if (!strcmp(arg, "--refresh")) { - refresh_only = 1; - continue; - } - usage(builtin_add_usage); - } - if (take_worktree_changes) { if (read_cache() < 0) die("index file corrupt"); - add_files_to_cache(verbose, prefix, argv + i); + add_files_to_cache(verbose, prefix, argv); goto finish; } - if (argc <= i) { + if (argc == 0) { fprintf(stderr, "Nothing specified, nothing added.\n"); fprintf(stderr, "Maybe you wanted to say 'git add .'?\n"); return 0; } - pathspec = get_pathspec(prefix, argv + i); + pathspec = get_pathspec(prefix, argv); if (refresh_only) { refresh(verbose, pathspec); diff --git a/builtin-branch.c b/builtin-branch.c index 3da8b55b8f..d6d5cff6b8 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -11,9 +11,15 @@ #include "commit.h" #include "builtin.h" #include "remote.h" - -static const char builtin_branch_usage[] = - "git-branch [-r] (-d | -D) <branchname> | [--track | --no-track] [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]"; +#include "parse-options.h" + +static const char * const builtin_branch_usage[] = { + "git-branch [options] [-r | -a]", + "git-branch [options] [-l] [-f] <branchname> [<start-point>]", + "git-branch [options] [-r] (-d | -D) <branchname>", + "git-branch [options] (-m | -M) [<oldbranch>] <newbranch>", + NULL +}; #define REF_UNKNOWN_TYPE 0x00 #define REF_LOCAL_BRANCH 0x01 @@ -505,93 +511,45 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int rename = 0, force_rename = 0; int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0; int reflog = 0, track; - int kinds = REF_LOCAL_BRANCH; - int i; + int kinds = REF_LOCAL_BRANCH, kind_remote = 0, kind_any = 0; + + struct option options[] = { + OPT_GROUP("Generic options"), + OPT__VERBOSE(&verbose), + OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"), + OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"), + OPT_BOOLEAN('r', NULL, &kind_remote, "act on remote-tracking branches"), + OPT__ABBREV(&abbrev), + + OPT_GROUP("Specific git-branch actions:"), + OPT_BOOLEAN('a', NULL, &kind_any, "list both remote-tracking and local branches"), + OPT_BOOLEAN('d', NULL, &delete, "delete fully merged branch"), + OPT_BOOLEAN('D', NULL, &force_delete, "delete branch (even if not merged)"), + OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"), + OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"), + OPT_BOOLEAN('m', NULL, &rename, "move/rename a branch and its reflog"), + OPT_BOOLEAN('M', NULL, &force_rename, "move/rename a branch, even if target exists"), + OPT_END(), + }; git_config(git_branch_config); track = branch_track; - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "--track")) { - track = 1; - continue; - } - if (!strcmp(arg, "--no-track")) { - track = 0; - continue; - } - if (!strcmp(arg, "-d")) { - delete = 1; - continue; - } - if (!strcmp(arg, "-D")) { - delete = 1; - force_delete = 1; - continue; - } - if (!strcmp(arg, "-f")) { - force_create = 1; - continue; - } - if (!strcmp(arg, "-m")) { - rename = 1; - continue; - } - if (!strcmp(arg, "-M")) { - rename = 1; - force_rename = 1; - continue; - } - if (!strcmp(arg, "-r")) { - kinds = REF_REMOTE_BRANCH; - continue; - } - if (!strcmp(arg, "-a")) { - kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH; - continue; - } - if (!strcmp(arg, "-l")) { - reflog = 1; - continue; - } - if (!prefixcmp(arg, "--no-abbrev")) { - abbrev = 0; - continue; - } - if (!prefixcmp(arg, "--abbrev=")) { - abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (abbrev > 40) - abbrev = 40; - continue; - } - if (!strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--color")) { - branch_use_color = 1; - continue; - } - if (!strcmp(arg, "--no-color")) { - branch_use_color = 0; - continue; - } - usage(builtin_branch_usage); - } + argc = parse_options(argc, argv, options, builtin_branch_usage, 0); + + delete |= force_delete; + rename |= force_rename; + if (kind_remote) + kinds = REF_REMOTE_BRANCH; + if (kind_any) + kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH; + if (abbrev && abbrev < MINIMUM_ABBREV) + abbrev = MINIMUM_ABBREV; + else if (abbrev > 40) + abbrev = 40; if ((delete && rename) || (delete && force_create) || (rename && force_create)) - usage(builtin_branch_usage); + usage_with_options(builtin_branch_usage, options); head = resolve_ref("HEAD", head_sha1, 0, NULL); if (!head) @@ -599,26 +557,25 @@ int cmd_branch(int argc, const char **argv, const char *prefix) head = xstrdup(head); if (!strcmp(head, "HEAD")) { detached = 1; - } - else { + } else { if (prefixcmp(head, "refs/heads/")) die("HEAD not found below refs/heads!"); head += 11; } if (delete) - return delete_branches(argc - i, argv + i, force_delete, kinds); - else if (i == argc) + return delete_branches(argc, argv, force_delete, kinds); + else if (argc == 0) print_ref_list(kinds, detached, verbose, abbrev); - else if (rename && (i == argc - 1)) - rename_branch(head, argv[i], force_rename); - else if (rename && (i == argc - 2)) - rename_branch(argv[i], argv[i + 1], force_rename); - else if (i == argc - 1 || i == argc - 2) - create_branch(argv[i], (i == argc - 2) ? argv[i+1] : head, + else if (rename && (argc == 1)) + rename_branch(head, argv[0], force_rename); + else if (rename && (argc == 2)) + rename_branch(argv[0], argv[1], force_rename); + else if (argc <= 2) + create_branch(argv[0], (argc == 2) ? argv[1] : head, force_create, reflog, track); else - usage(builtin_branch_usage); + usage_with_options(builtin_branch_usage, options); return 0; } diff --git a/builtin-count-objects.c b/builtin-count-objects.c index 4274ec1950..f00306fb67 100644 --- a/builtin-count-objects.c +++ b/builtin-count-objects.c @@ -6,8 +6,7 @@ #include "cache.h" #include "builtin.h" - -static const char count_objects_usage[] = "git-count-objects [-v]"; +#include "parse-options.h" static void count_objects(DIR *d, char *path, int len, int verbose, unsigned long *loose, @@ -67,29 +66,28 @@ static void count_objects(DIR *d, char *path, int len, int verbose, } } -int cmd_count_objects(int ac, const char **av, const char *prefix) +static char const * const count_objects_usage[] = { + "git-count-objects [-v]", + NULL +}; + +int cmd_count_objects(int argc, const char **argv, const char *prefix) { - int i; - int verbose = 0; + int i, verbose = 0; const char *objdir = get_object_directory(); int len = strlen(objdir); char *path = xmalloc(len + 50); unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0; unsigned long loose_size = 0; + struct option opts[] = { + OPT__VERBOSE(&verbose), + OPT_END(), + }; - for (i = 1; i < ac; i++) { - const char *arg = av[i]; - if (*arg != '-') - break; - else if (!strcmp(arg, "-v")) - verbose = 1; - else - usage(count_objects_usage); - } - + argc = parse_options(argc, argv, opts, count_objects_usage, 0); /* we do not take arguments other than flags for now */ - if (i < ac) - usage(count_objects_usage); + if (argc) + usage_with_options(count_objects_usage, opts); memcpy(path, objdir, len); if (len && objdir[len-1] != '/') path[len++] = '/'; diff --git a/builtin-describe.c b/builtin-describe.c index 669110cb06..6eeb9b5045 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -4,12 +4,15 @@ #include "refs.h" #include "builtin.h" #include "exec_cmd.h" +#include "parse-options.h" #define SEEN (1u<<0) #define MAX_TAGS (FLAG_BITS - 1) -static const char describe_usage[] = -"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*"; +static const char * const describe_usage[] = { + "git-describe [options] <committish>*", + NULL +}; static int debug; /* Display lots of verbose info */ static int all; /* Default to annotated tags only */ @@ -242,57 +245,42 @@ static void describe(const char *arg, int last_one) int cmd_describe(int argc, const char **argv, const char *prefix) { - int i; int contains = 0; + struct option options[] = { + OPT_BOOLEAN(0, "contains", &contains, "find the tag that comes after the commit"), + OPT_BOOLEAN(0, "debug", &debug, "debug search strategy on stderr"), + OPT_BOOLEAN(0, "all", &all, "use any ref in .git/refs"), + OPT_BOOLEAN(0, "tags", &tags, "use any tag in .git/refs/tags"), + OPT__ABBREV(&abbrev), + OPT_INTEGER(0, "candidates", &max_candidates, + "consider <n> most recent tags (default: 10)"), + OPT_END(), + }; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (*arg != '-') - break; - else if (!strcmp(arg, "--contains")) - contains = 1; - else if (!strcmp(arg, "--debug")) - debug = 1; - else if (!strcmp(arg, "--all")) - all = 1; - else if (!strcmp(arg, "--tags")) - tags = 1; - else if (!prefixcmp(arg, "--abbrev=")) { - abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev != 0 && (abbrev < MINIMUM_ABBREV || 40 < abbrev)) - abbrev = DEFAULT_ABBREV; - } - else if (!prefixcmp(arg, "--candidates=")) { - max_candidates = strtoul(arg + 13, NULL, 10); - if (max_candidates < 1) - max_candidates = 1; - else if (max_candidates > MAX_TAGS) - max_candidates = MAX_TAGS; - } - else - usage(describe_usage); - } + argc = parse_options(argc, argv, options, describe_usage, 0); + if (max_candidates < 1) + max_candidates = 1; + else if (max_candidates > MAX_TAGS) + max_candidates = MAX_TAGS; save_commit_buffer = 0; if (contains) { - const char **args = xmalloc((4 + argc - i) * sizeof(char*)); + const char **args = xmalloc((4 + argc) * sizeof(char*)); args[0] = "name-rev"; args[1] = "--name-only"; args[2] = "--tags"; - memcpy(args + 3, argv + i, (argc - i) * sizeof(char*)); - args[3 + argc - i] = NULL; - return cmd_name_rev(3 + argc - i, args, prefix); + memcpy(args + 3, argv, argc * sizeof(char*)); + args[3 + argc] = NULL; + return cmd_name_rev(3 + argc, args, prefix); } - if (argc <= i) + if (argc == 0) { describe("HEAD", 1); - else - while (i < argc) { - describe(argv[i], (i == argc - 1)); - i++; + } else { + while (argc-- > 0) { + describe(*argv++, argc == 0); } - + } return 0; } diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c index c74ef2800c..da8c7948e6 100644 --- a/builtin-for-each-ref.c +++ b/builtin-for-each-ref.c @@ -7,6 +7,7 @@ #include "tree.h" #include "blob.h" #include "quote.h" +#include "parse-options.h" /* Quoting styles */ #define QUOTE_NONE 0 @@ -158,17 +159,18 @@ static const char *find_next(const char *cp) * Make sure the format string is well formed, and parse out * the used atoms. */ -static void verify_format(const char *format) +static int verify_format(const char *format) { const char *cp, *sp; for (cp = format; *cp && (sp = find_next(cp)); ) { const char *ep = strchr(sp, ')'); if (!ep) - die("malformatted format string %s", sp); + return error("malformatted format string %s", sp); /* sp points at "%(" and ep points at the closing ")" */ parse_atom(sp + 2, ep); cp = ep + 1; } + return 0; } /* @@ -800,94 +802,76 @@ static struct ref_sort *default_sort(void) return sort; } -int cmd_for_each_ref(int ac, const char **av, const char *prefix) +int opt_parse_sort(const struct option *opt, const char *arg, int unset) +{ + struct ref_sort **sort_tail = opt->value; + struct ref_sort *s; + int len; + + if (!arg) /* should --no-sort void the list ? */ + return -1; + + *sort_tail = s = xcalloc(1, sizeof(*s)); + sort_tail = &s->next; + + if (*arg == '-') { + s->reverse = 1; + arg++; + } + len = strlen(arg); + s->atom = parse_atom(arg, arg+len); + return 0; +} + +static char const * const for_each_ref_usage[] = { + "git-for-each-ref [options] [<pattern>]", + NULL +}; + +int cmd_for_each_ref(int argc, const char **argv, const char *prefix) { int i, num_refs; - const char *format = NULL; + const char *format = "%(objectname) %(objecttype)\t%(refname)"; struct ref_sort *sort = NULL, **sort_tail = &sort; - int maxcount = 0; - int quote_style = -1; /* unspecified yet */ + int maxcount = 0, quote_style; + int quote_shell = 0, quote_perl = 0, quote_python = 0, quote_tcl = 0; struct refinfo **refs; struct grab_ref_cbdata cbdata; - for (i = 1; i < ac; i++) { - const char *arg = av[i]; - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!prefixcmp(arg, "--format=")) { - if (format) - die("more than one --format?"); - format = arg + 9; - continue; - } - if (!strcmp(arg, "-s") || !strcmp(arg, "--shell") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_SHELL; - continue; - } - if (!strcmp(arg, "-p") || !strcmp(arg, "--perl") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_PERL; - continue; - } - if (!strcmp(arg, "--python") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_PYTHON; - continue; - } - if (!strcmp(arg, "--tcl") ) { - if (0 <= quote_style) - die("more than one quoting style?"); - quote_style = QUOTE_TCL; - continue; - } - if (!prefixcmp(arg, "--count=")) { - if (maxcount) - die("more than one --count?"); - maxcount = atoi(arg + 8); - if (maxcount <= 0) - die("The number %s did not parse", arg); - continue; - } - if (!prefixcmp(arg, "--sort=")) { - struct ref_sort *s = xcalloc(1, sizeof(*s)); - int len; - - s->next = NULL; - *sort_tail = s; - sort_tail = &s->next; - - arg += 7; - if (*arg == '-') { - s->reverse = 1; - arg++; - } - len = strlen(arg); - sort->atom = parse_atom(arg, arg+len); - continue; - } - break; + struct option opts[] = { + OPT_BOOLEAN('s', "shell", "e_shell, "quote placeholders suitably for shells"), + OPT_BOOLEAN('p', "perl", "e_perl, "quote placeholders suitably for perl"), + OPT_BOOLEAN( 0 , "python", "e_python, "quote placeholders suitably for python"), + OPT_BOOLEAN( 0 , "tcl", "e_tcl, "quote placeholders suitably for tcl"), + + OPT_GROUP(""), + OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"), + OPT_STRING( 0 , "format", &format, "format", "format to use for the output"), + OPT_CALLBACK(0 , "sort", &sort_tail, "key", + "field name to sort on", &opt_parse_sort), + OPT_END(), + }; + + parse_options(argc, argv, opts, for_each_ref_usage, 0); + if (maxcount < 0) { + error("invalid --count argument: `%d'", maxcount); + usage_with_options(for_each_ref_usage, opts); } - if (quote_style < 0) - quote_style = QUOTE_NONE; + if (quote_shell + quote_perl + quote_python + quote_tcl > 1) { + error("more than one quoting style ?"); + usage_with_options(for_each_ref_usage, opts); + } + if (verify_format(format)) + usage_with_options(for_each_ref_usage, opts); + quote_style = QUOTE_SHELL * quote_shell + QUOTE_PERL * quote_perl + + QUOTE_PYTHON * quote_python + QUOTE_TCL * quote_tcl; if (!sort) sort = default_sort(); sort_atom_limit = used_atom_cnt; - if (!format) - format = "%(objectname) %(objecttype)\t%(refname)"; - - verify_format(format); memset(&cbdata, 0, sizeof(cbdata)); - cbdata.grab_pattern = av + i; + cbdata.grab_pattern = argv; for_each_ref(grab_single_ref, &cbdata); refs = cbdata.grab_array; num_refs = cbdata.grab_cnt; diff --git a/builtin-fsck.c b/builtin-fsck.c index 8d12287f03..e4874f64a3 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -8,6 +8,7 @@ #include "pack.h" #include "cache-tree.h" #include "tree-walk.h" +#include "parse-options.h" #define REACHABLE 0x0001 #define SEEN 0x0002 @@ -666,9 +667,24 @@ static int fsck_cache_tree(struct cache_tree *it) return err; } -static const char fsck_usage[] = -"git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] " -"[--strict] [--verbose] <head-sha1>*]"; +static char const * const fsck_usage[] = { + "git-fsck [options] [<object>...]", + NULL +}; + +static struct option fsck_opts[] = { + OPT__VERBOSE(&verbose), + OPT_BOOLEAN(0, "unreachable", &show_unreachable, "show unreachable objects"), + OPT_BOOLEAN(0, "tags", &show_tags, "report tags"), + OPT_BOOLEAN(0, "root", &show_root, "report root nodes"), + OPT_BOOLEAN(0, "cache", &keep_cache_objects, "make index objects head nodes"), + OPT_BOOLEAN(0, "reflogs", &include_reflogs, "make reflogs head nodes (default)"), + OPT_BOOLEAN(0, "full", &check_full, "also consider alternate objects"), + OPT_BOOLEAN(0, "strict", &check_strict, "enable more strict checking"), + OPT_BOOLEAN(0, "lost-found", &write_lost_and_found, + "write dangling objects in .git/lost-found"), + OPT_END(), +}; int cmd_fsck(int argc, const char **argv, const char *prefix) { @@ -677,49 +693,10 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) track_object_refs = 1; errors_found = 0; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--unreachable")) { - show_unreachable = 1; - continue; - } - if (!strcmp(arg, "--tags")) { - show_tags = 1; - continue; - } - if (!strcmp(arg, "--root")) { - show_root = 1; - continue; - } - if (!strcmp(arg, "--cache")) { - keep_cache_objects = 1; - continue; - } - if (!strcmp(arg, "--no-reflogs")) { - include_reflogs = 0; - continue; - } - if (!strcmp(arg, "--full")) { - check_full = 1; - continue; - } - if (!strcmp(arg, "--strict")) { - check_strict = 1; - continue; - } - if (!strcmp(arg, "--verbose")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--lost-found")) { - check_full = 1; - include_reflogs = 0; - write_lost_and_found = 1; - continue; - } - if (*arg == '-') - usage(fsck_usage); + argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0); + if (write_lost_and_found) { + check_full = 1; + include_reflogs = 0; } fsck_head_link(); @@ -741,22 +718,18 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) verify_pack(p, 0); for (p = packed_git; p; p = p->next) { - uint32_t i, num; + uint32_t j, num; if (open_pack_index(p)) continue; num = p->num_objects; - for (i = 0; i < num; i++) - fsck_sha1(nth_packed_object_sha1(p, i)); + for (j = 0; j < num; j++) + fsck_sha1(nth_packed_object_sha1(p, j)); } } heads = 0; for (i = 1; i < argc; i++) { const char *arg = argv[i]; - - if (*arg == '-') - continue; - if (!get_sha1(arg, head_sha1)) { struct object *obj = lookup_object(head_sha1); @@ -783,7 +756,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) } if (keep_cache_objects) { - int i; read_cache(); for (i = 0; i < active_nr; i++) { unsigned int mode; diff --git a/builtin-gc.c b/builtin-gc.c index 3a2ca4f901..c5bce893a6 100644 --- a/builtin-gc.c +++ b/builtin-gc.c @@ -12,11 +12,15 @@ #include "builtin.h" #include "cache.h" +#include "parse-options.h" #include "run-command.h" #define FAILED_RUN "failed to run %s" -static const char builtin_gc_usage[] = "git-gc [--prune] [--aggressive]"; +static const char * const builtin_gc_usage[] = { + "git-gc [options]", + NULL +}; static int pack_refs = 1; static int aggressive_window = -1; @@ -165,38 +169,34 @@ static int need_to_gc(void) int cmd_gc(int argc, const char **argv, const char *prefix) { - int i; int prune = 0; + int aggressive = 0; int auto_gc = 0; char buf[80]; + struct option builtin_gc_options[] = { + OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced loose objects"), + OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"), + OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"), + OPT_END() + }; + git_config(gc_config); if (pack_refs < 0) pack_refs = !is_bare_repository(); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--prune")) { - prune = 1; - continue; - } - if (!strcmp(arg, "--aggressive")) { - append_option(argv_repack, "-f", MAX_ADD); - if (aggressive_window > 0) { - sprintf(buf, "--window=%d", aggressive_window); - append_option(argv_repack, buf, MAX_ADD); - } - continue; - } - if (!strcmp(arg, "--auto")) { - auto_gc = 1; - continue; + argc = parse_options(argc, argv, builtin_gc_options, builtin_gc_usage, 0); + if (argc > 0) + usage_with_options(builtin_gc_usage, builtin_gc_options); + + if (aggressive) { + append_option(argv_repack, "-f", MAX_ADD); + if (aggressive_window > 0) { + sprintf(buf, "--window=%d", aggressive_window); + append_option(argv_repack, buf, MAX_ADD); } - break; } - if (i != argc) - usage(builtin_gc_usage); if (auto_gc) { /* diff --git a/builtin-mv.c b/builtin-mv.c index b944651691..a3f9ad1744 100644 --- a/builtin-mv.c +++ b/builtin-mv.c @@ -8,9 +8,12 @@ #include "dir.h" #include "cache-tree.h" #include "path-list.h" +#include "parse-options.h" -static const char builtin_mv_usage[] = -"git-mv [-n] [-f] (<source> <destination> | [-k] <source>... <destination>)"; +static const char * const builtin_mv_usage[] = { + "git-mv [options] <source>... <destination>", + NULL +}; static const char **copy_pathspec(const char *prefix, const char **pathspec, int count, int base_name) @@ -61,8 +64,14 @@ static struct lock_file lock_file; int cmd_mv(int argc, const char **argv, const char *prefix) { - int i, newfd, count; + int i, newfd; int verbose = 0, show_only = 0, force = 0, ignore_errors = 0; + struct option builtin_mv_options[] = { + OPT__DRY_RUN(&show_only), + OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"), + OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"), + OPT_END(), + }; const char **source, **destination, **dest_path; enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes; struct stat st; @@ -78,52 +87,29 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; + argc = parse_options(argc, argv, builtin_mv_options, builtin_mv_usage, 0); + if (--argc < 1) + usage_with_options(builtin_mv_usage, builtin_mv_options); - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "-n")) { - show_only = 1; - continue; - } - if (!strcmp(arg, "-f")) { - force = 1; - continue; - } - if (!strcmp(arg, "-k")) { - ignore_errors = 1; - continue; - } - usage(builtin_mv_usage); - } - count = argc - i - 1; - if (count < 1) - usage(builtin_mv_usage); - - source = copy_pathspec(prefix, argv + i, count, 0); - modes = xcalloc(count, sizeof(enum update_mode)); - dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0); + source = copy_pathspec(prefix, argv, argc, 0); + modes = xcalloc(argc, sizeof(enum update_mode)); + dest_path = copy_pathspec(prefix, argv + argc, 1, 0); if (dest_path[0][0] == '\0') /* special case: "." was normalized to "" */ - destination = copy_pathspec(dest_path[0], argv + i, count, 1); + destination = copy_pathspec(dest_path[0], argv, argc, 1); else if (!lstat(dest_path[0], &st) && S_ISDIR(st.st_mode)) { dest_path[0] = add_slash(dest_path[0]); - destination = copy_pathspec(dest_path[0], argv + i, count, 1); + destination = copy_pathspec(dest_path[0], argv, argc, 1); } else { - if (count != 1) - usage(builtin_mv_usage); + if (argc != 1) + usage_with_options(builtin_mv_usage, builtin_mv_options); destination = dest_path; } /* Checking */ - for (i = 0; i < count; i++) { + for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; int length, src_is_dir; const char *bad = NULL; @@ -167,13 +153,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (last - first > 0) { source = xrealloc(source, - (count + last - first) + (argc + last - first) * sizeof(char *)); destination = xrealloc(destination, - (count + last - first) + (argc + last - first) * sizeof(char *)); modes = xrealloc(modes, - (count + last - first) + (argc + last - first) * sizeof(enum update_mode)); } @@ -183,13 +169,13 @@ int cmd_mv(int argc, const char **argv, const char *prefix) for (j = 0; j < last - first; j++) { const char *path = active_cache[first + j]->name; - source[count + j] = path; - destination[count + j] = + source[argc + j] = path; + destination[argc + j] = prefix_path(dst, dst_len, path + length); - modes[count + j] = INDEX; + modes[argc + j] = INDEX; } - count += last - first; + argc += last - first; } } else if (lstat(dst, &st) == 0) { bad = "destination exists"; @@ -216,12 +202,12 @@ int cmd_mv(int argc, const char **argv, const char *prefix) if (bad) { if (ignore_errors) { - if (--count > 0) { + if (--argc > 0) { memmove(source + i, source + i + 1, - (count - i) * sizeof(char *)); + (argc - i) * sizeof(char *)); memmove(destination + i, destination + i + 1, - (count - i) * sizeof(char *)); + (argc - i) * sizeof(char *)); } } else die ("%s, source=%s, destination=%s", @@ -229,7 +215,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) } } - for (i = 0; i < count; i++) { + for (i = 0; i < argc; i++) { const char *src = source[i], *dst = destination[i]; enum update_mode mode = modes[i]; if (show_only || verbose) @@ -253,7 +239,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix) path_list_insert(dst, &added); } - if (show_only) { + if (show_only) { show_list("Changed : ", &changed); show_list("Adding : ", &added); show_list("Deleting : ", &deleted); diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 03083e9477..a0c89a827b 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -3,12 +3,10 @@ #include "commit.h" #include "tag.h" #include "refs.h" +#include "parse-options.h" #define CUTOFF_DATE_SLOP 86400 /* one day */ -static const char name_rev_usage[] = - "git-name-rev [--tags | --refs=<pattern>] ( --all | --stdin | committish [committish...] )\n"; - typedef struct rev_name { const char *tip_name; int generation; @@ -153,51 +151,41 @@ static const char* get_rev_name(struct object *o) } } +static char const * const name_rev_usage[] = { + "git-name-rev [options] ( --all | --stdin | <commit>... )", + NULL +}; + int cmd_name_rev(int argc, const char **argv, const char *prefix) { struct object_array revs = { 0, 0, NULL }; - int as_is = 0, all = 0, transform_stdin = 0; + int all = 0, transform_stdin = 0; struct name_ref_data data = { 0, 0, NULL }; + struct option opts[] = { + OPT_BOOLEAN(0, "name-only", &data.name_only, "print only names (no SHA-1)"), + OPT_BOOLEAN(0, "tags", &data.tags_only, "only use tags to name the commits"), + OPT_STRING(0, "refs", &data.ref_filter, "pattern", + "only use refs matching <pattern>"), + OPT_GROUP(""), + OPT_BOOLEAN(0, "all", &all, "list all commits reachable from all refs"), + OPT_BOOLEAN(0, "stdin", &transform_stdin, "read from stdin"), + OPT_END(), + }; git_config(git_default_config); + argc = parse_options(argc, argv, opts, name_rev_usage, 0); + if (!!all + !!transform_stdin + !!argc > 1) { + error("Specify either a list, or --all, not both!"); + usage_with_options(name_rev_usage, opts); + } + if (all || transform_stdin) + cutoff = 0; - if (argc < 2) - usage(name_rev_usage); - - for (--argc, ++argv; argc; --argc, ++argv) { + for (; argc; argc--, argv++) { unsigned char sha1[20]; struct object *o; struct commit *commit; - if (!as_is && (*argv)[0] == '-') { - if (!strcmp(*argv, "--")) { - as_is = 1; - continue; - } else if (!strcmp(*argv, "--name-only")) { - data.name_only = 1; - continue; - } else if (!strcmp(*argv, "--tags")) { - data.tags_only = 1; - continue; - } else if (!prefixcmp(*argv, "--refs=")) { - data.ref_filter = *argv + 7; - continue; - } else if (!strcmp(*argv, "--all")) { - if (argc > 1) - die("Specify either a list, or --all, not both!"); - all = 1; - cutoff = 0; - continue; - } else if (!strcmp(*argv, "--stdin")) { - if (argc > 1) - die("Specify either a list, or --stdin, not both!"); - transform_stdin = 1; - cutoff = 0; - continue; - } - usage(name_rev_usage); - } - if (get_sha1(*argv, sha1)) { fprintf(stderr, "Could not get sha1 for %s. Skipping.\n", *argv); @@ -212,10 +200,8 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) } commit = (struct commit *)o; - if (cutoff > commit->date) cutoff = commit->date; - add_object_array((struct object *)commit, *argv, &revs); } diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 09df4e11a8..a62f06bb89 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -3,9 +3,7 @@ #include "refs.h" #include "object.h" #include "tag.h" - -static const char builtin_pack_refs_usage[] = -"git-pack-refs [--all] [--prune | --no-prune]"; +#include "parse-options.h" struct ref_to_prune { struct ref_to_prune *next; @@ -117,31 +115,26 @@ static int pack_refs(unsigned int flags) return 0; } +static char const * const pack_refs_usage[] = { + "git-pack-refs [options]", + NULL +}; + int cmd_pack_refs(int argc, const char **argv, const char *prefix) { - int i; - unsigned int flags; - - flags = PACK_REFS_PRUNE; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (!strcmp(arg, "--prune")) { - flags |= PACK_REFS_PRUNE; /* now the default */ - continue; - } - if (!strcmp(arg, "--no-prune")) { - flags &= ~PACK_REFS_PRUNE; - continue; - } - if (!strcmp(arg, "--all")) { - flags |= PACK_REFS_ALL; - continue; - } - /* perhaps other parameters later... */ - break; - } - if (i != argc) - usage(builtin_pack_refs_usage); - + int all = 0, prune = 1; + unsigned int flags = 0; + struct option opts[] = { + OPT_BOOLEAN(0, "all", &all, "pack everything"), + OPT_BOOLEAN(0, "prune", &prune, "prune loose refs (default)"), + OPT_END(), + }; + + if (parse_options(argc, argv, opts, pack_refs_usage, 0)) + usage_with_options(pack_refs_usage, opts); + if (prune) + flags |= PACK_REFS_PRUNE; + if (all) + flags |= PACK_REFS_ALL; return pack_refs(flags); } diff --git a/builtin-revert.c b/builtin-revert.c index e855b206cf..a9347cf9c5 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -7,6 +7,7 @@ #include "run-command.h" #include "exec_cmd.h" #include "utf8.h" +#include "parse-options.h" /* * This implements the builtins revert and cherry-pick. @@ -19,51 +20,42 @@ * Copyright (c) 2005 Junio C Hamano */ -static const char *revert_usage = "git-revert [--edit | --no-edit] [-n] <commit-ish>"; +static const char * const revert_usage[] = { + "git-revert [options] <commit-ish>", + NULL +}; -static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] <commit-ish>"; +static const char * const cherry_pick_usage[] = { + "git-cherry-pick [options] <commit-ish>", + NULL +}; -static int edit; -static int replay; +static int edit, no_replay, no_commit, needed_deref; static enum { REVERT, CHERRY_PICK } action; -static int no_commit; static struct commit *commit; -static int needed_deref; static const char *me; #define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION" -static void parse_options(int argc, const char **argv) +static void parse_args(int argc, const char **argv) { - const char *usage_str = action == REVERT ? - revert_usage : cherry_pick_usage; + const char * const * usage_str = + action == REVERT ? revert_usage : cherry_pick_usage; unsigned char sha1[20]; const char *arg; - int i; - - if (argc < 2) - usage(usage_str); - - for (i = 1; i < argc; i++) { - arg = argv[i]; - if (arg[0] != '-') - break; - if (!strcmp(arg, "-n") || !strcmp(arg, "--no-commit")) - no_commit = 1; - else if (!strcmp(arg, "-e") || !strcmp(arg, "--edit")) - edit = 1; - else if (!strcmp(arg, "--no-edit")) - edit = 0; - else if (!strcmp(arg, "-x") || !strcmp(arg, "--i-really-want-" - "to-expose-my-private-commit-object-name")) - replay = 0; - else if (strcmp(arg, "-r")) - usage(usage_str); - } - if (i != argc - 1) - usage(usage_str); - arg = argv[argc - 1]; + int noop; + struct option options[] = { + OPT_BOOLEAN('n', "no-commit", &no_commit, "don't automatically commit"), + OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"), + OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"), + OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"), + OPT_END(), + }; + + if (parse_options(argc, argv, options, usage_str, 0) != 1) + usage_with_options(usage_str, options); + arg = argv[0]; if (get_sha1(arg, sha1)) die ("Cannot find '%s'", arg); commit = (struct commit *)parse_object(sha1); @@ -243,10 +235,10 @@ static int revert_or_cherry_pick(int argc, const char **argv) git_config(git_default_config); me = action == REVERT ? "revert" : "cherry-pick"; setenv(GIT_REFLOG_ACTION, me, 0); - parse_options(argc, argv); + parse_args(argc, argv); /* this is copied from the shell script, but it's never triggered... */ - if (action == REVERT && replay) + if (action == REVERT && !no_replay) die("revert is incompatible with replay"); if (no_commit) { @@ -310,7 +302,7 @@ static int revert_or_cherry_pick(int argc, const char **argv) next = commit; set_author_ident_env(message); add_message_to_msg(message); - if (!replay) { + if (no_replay) { add_to_msg("(cherry picked from commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(")\n"); @@ -388,13 +380,14 @@ int cmd_revert(int argc, const char **argv, const char *prefix) { if (isatty(0)) edit = 1; + no_replay = 1; action = REVERT; return revert_or_cherry_pick(argc, argv); } int cmd_cherry_pick(int argc, const char **argv, const char *prefix) { - replay = 1; + no_replay = 0; action = CHERRY_PICK; return revert_or_cherry_pick(argc, argv); } diff --git a/builtin-rm.c b/builtin-rm.c index 3b0677e44b..bca2bd9703 100644 --- a/builtin-rm.c +++ b/builtin-rm.c @@ -8,9 +8,12 @@ #include "dir.h" #include "cache-tree.h" #include "tree-walk.h" +#include "parse-options.h" -static const char builtin_rm_usage[] = -"git-rm [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>..."; +static const char * const builtin_rm_usage[] = { + "git-rm [options] [--] <file>...", + NULL +}; static struct { int nr, alloc; @@ -121,11 +124,23 @@ static int check_local_mod(unsigned char *head, int index_only) static struct lock_file lock_file; +static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; +static int ignore_unmatch = 0; + +static struct option builtin_rm_options[] = { + OPT__DRY_RUN(&show_only), + OPT__QUIET(&quiet), + OPT_BOOLEAN( 0 , "cached", &index_only, "only remove from the index"), + OPT_BOOLEAN('f', NULL, &force, "override the up-to-date check"), + OPT_BOOLEAN('r', NULL, &recursive, "allow recursive removal"), + OPT_BOOLEAN( 0 , "ignore-unmatch", &ignore_unmatch, + "exit with a zero status even if nothing matched"), + OPT_END(), +}; + int cmd_rm(int argc, const char **argv, const char *prefix) { int i, newfd; - int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0; - int ignore_unmatch = 0; const char **pathspec; char *seen; @@ -136,34 +151,11 @@ int cmd_rm(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); - for (i = 1 ; i < argc ; i++) { - const char *arg = argv[i]; - - if (*arg != '-') - break; - else if (!strcmp(arg, "--")) { - i++; - break; - } - else if (!strcmp(arg, "-n")) - show_only = 1; - else if (!strcmp(arg, "--cached")) - index_only = 1; - else if (!strcmp(arg, "-f")) - force = 1; - else if (!strcmp(arg, "-r")) - recursive = 1; - else if (!strcmp(arg, "--quiet")) - quiet = 1; - else if (!strcmp(arg, "--ignore-unmatch")) - ignore_unmatch = 1; - else - usage(builtin_rm_usage); - } - if (argc <= i) - usage(builtin_rm_usage); + argc = parse_options(argc, argv, builtin_rm_options, builtin_rm_usage, 0); + if (!argc) + usage_with_options(builtin_rm_usage, builtin_rm_options); - pathspec = get_pathspec(prefix, argv + i); + pathspec = get_pathspec(prefix, argv); seen = NULL; for (i = 0; pathspec[i] ; i++) /* nothing */; diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 9eb95e50da..d33982b967 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -1,9 +1,12 @@ #include "builtin.h" #include "cache.h" #include "refs.h" +#include "parse-options.h" -static const char git_symbolic_ref_usage[] = -"git-symbolic-ref [-q] [-m <reason>] name [ref]"; +static const char * const git_symbolic_ref_usage[] = { + "git-symbolic-ref [options] name [ref]", + NULL +}; static void check_symref(const char *HEAD, int quiet) { @@ -26,44 +29,25 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) { int quiet = 0; const char *msg = NULL; + struct option options[] = { + OPT__QUIET(&quiet), + OPT_STRING('m', NULL, &msg, "reason", "reason of the update"), + OPT_END(), + }; git_config(git_default_config); - - while (1 < argc) { - const char *arg = argv[1]; - if (arg[0] != '-') - break; - else if (!strcmp("-q", arg)) - quiet = 1; - else if (!strcmp("-m", arg)) { - argc--; - argv++; - if (argc <= 1) - break; - msg = argv[1]; - if (!*msg) - die("Refusing to perform update with empty message"); - } - else if (!strcmp("--", arg)) { - argc--; - argv++; - break; - } - else - die("unknown option %s", arg); - argc--; - argv++; - } - + argc = parse_options(argc, argv, options, git_symbolic_ref_usage, 0); + if (msg &&!*msg) + die("Refusing to perform update with empty message"); switch (argc) { - case 2: - check_symref(argv[1], quiet); + case 1: + check_symref(argv[0], quiet); break; - case 3: - create_symref(argv[1], argv[2], msg); + case 2: + create_symref(argv[0], argv[1], msg); break; default: - usage(git_symbolic_ref_usage); + usage_with_options(git_symbolic_ref_usage, options); } return 0; } diff --git a/builtin-update-ref.c b/builtin-update-ref.c index fe1f74c9f3..e90737c350 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -1,59 +1,44 @@ #include "cache.h" #include "refs.h" #include "builtin.h" +#include "parse-options.h" -static const char git_update_ref_usage[] = -"git-update-ref [-m <reason>] (-d <refname> <value> | [--no-deref] <refname> <value> [<oldval>])"; +static const char * const git_update_ref_usage[] = { + "git-update-ref [options] -d <refname> <oldval>", + "git-update-ref [options] <refname> <newval> [<oldval>]", + NULL +}; int cmd_update_ref(int argc, const char **argv, const char *prefix) { - const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; + const char *refname, *value, *oldval, *msg=NULL; unsigned char sha1[20], oldsha1[20]; - int i, delete, ref_flags; + int delete = 0, no_deref = 0; + struct option options[] = { + OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"), + OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"), + OPT_BOOLEAN( 0 , "no-deref", &no_deref, + "update <refname> not the one it points to"), + OPT_END(), + }; - delete = 0; - ref_flags = 0; git_config(git_default_config); + argc = parse_options(argc, argv, options, git_update_ref_usage, 0); + if (msg && !*msg) + die("Refusing to perform update with empty message."); - for (i = 1; i < argc; i++) { - if (!strcmp("-m", argv[i])) { - if (i+1 >= argc) - usage(git_update_ref_usage); - msg = argv[++i]; - if (!*msg) - die("Refusing to perform update with empty message."); - continue; - } - if (!strcmp("-d", argv[i])) { - delete = 1; - continue; - } - if (!strcmp("--no-deref", argv[i])) { - ref_flags |= REF_NODEREF; - continue; - } - if (!refname) { - refname = argv[i]; - continue; - } - if (!value) { - value = argv[i]; - continue; - } - if (!oldval) { - oldval = argv[i]; - continue; - } - } - if (!refname || !value) - usage(git_update_ref_usage); + if (argc < 2 || argc > 3) + usage_with_options(git_update_ref_usage, options); + refname = argv[0]; + value = argv[1]; + oldval = argv[2]; if (get_sha1(value, sha1)) die("%s: not a valid SHA1", value); if (delete) { if (oldval) - usage(git_update_ref_usage); + usage_with_options(git_update_ref_usage, options); return delete_ref(refname, sha1); } @@ -62,5 +47,5 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) die("%s: not a valid old SHA1", oldval); return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL, - ref_flags, DIE_ON_ERR); + no_deref ? REF_NODEREF : 0, DIE_ON_ERR); } diff --git a/parse-options.c b/parse-options.c new file mode 100644 index 0000000000..cc09c98ec3 --- /dev/null +++ b/parse-options.c @@ -0,0 +1,322 @@ +#include "git-compat-util.h" +#include "parse-options.h" + +#define OPT_SHORT 1 +#define OPT_UNSET 2 + +struct optparse_t { + const char **argv; + int argc; + const char *opt; +}; + +static inline const char *get_arg(struct optparse_t *p) +{ + if (p->opt) { + const char *res = p->opt; + p->opt = NULL; + return res; + } + p->argc--; + return *++p->argv; +} + +static inline const char *skip_prefix(const char *str, const char *prefix) +{ + size_t len = strlen(prefix); + return strncmp(str, prefix, len) ? NULL : str + len; +} + +static int opterror(const struct option *opt, const char *reason, int flags) +{ + if (flags & OPT_SHORT) + return error("switch `%c' %s", opt->short_name, reason); + if (flags & OPT_UNSET) + return error("option `no-%s' %s", opt->long_name, reason); + return error("option `%s' %s", opt->long_name, reason); +} + +static int get_value(struct optparse_t *p, + const struct option *opt, int flags) +{ + const char *s, *arg; + arg = p->opt ? p->opt : (p->argc > 1 ? p->argv[1] : NULL); + + if (p->opt && (flags & OPT_UNSET)) + return opterror(opt, "takes no value", flags); + + switch (opt->type) { + case OPTION_BOOLEAN: + if (!(flags & OPT_SHORT) && p->opt) + return opterror(opt, "takes no value", flags); + if (flags & OPT_UNSET) + *(int *)opt->value = 0; + else + (*(int *)opt->value)++; + return 0; + + case OPTION_STRING: + if (flags & OPT_UNSET) { + *(const char **)opt->value = (const char *)NULL; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) { + *(const char **)opt->value = (const char *)opt->defval; + return 0; + } + if (!arg) + return opterror(opt, "requires a value", flags); + *(const char **)opt->value = get_arg(p); + return 0; + + case OPTION_CALLBACK: + if (flags & OPT_UNSET) + return (*opt->callback)(opt, NULL, 1); + if (opt->flags & PARSE_OPT_NOARG) { + if (p->opt && !(flags & OPT_SHORT)) + return opterror(opt, "takes no value", flags); + return (*opt->callback)(opt, NULL, 0); + } + if (opt->flags & PARSE_OPT_OPTARG && (!arg || *arg == '-')) + return (*opt->callback)(opt, NULL, 0); + if (!arg) + return opterror(opt, "requires a value", flags); + return (*opt->callback)(opt, get_arg(p), 0); + + case OPTION_INTEGER: + if (flags & OPT_UNSET) { + *(int *)opt->value = 0; + return 0; + } + if (opt->flags & PARSE_OPT_OPTARG && (!arg || !isdigit(*arg))) { + *(int *)opt->value = opt->defval; + return 0; + } + if (!arg) + return opterror(opt, "requires a value", flags); + *(int *)opt->value = strtol(get_arg(p), (char **)&s, 10); + if (*s) + return opterror(opt, "expects a numerical value", flags); + return 0; + + default: + die("should not happen, someone must be hit on the forehead"); + } +} + +static int parse_short_opt(struct optparse_t *p, const struct option *options) +{ + for (; options->type != OPTION_END; options++) { + if (options->short_name == *p->opt) { + p->opt = p->opt[1] ? p->opt + 1 : NULL; + return get_value(p, options, OPT_SHORT); + } + } + return error("unknown switch `%c'", *p->opt); +} + +static int parse_long_opt(struct optparse_t *p, const char *arg, + const struct option *options) +{ + const char *arg_end = strchr(arg, '='); + const struct option *abbrev_option = NULL; + int abbrev_flags = 0; + + if (!arg_end) + arg_end = arg + strlen(arg); + + for (; options->type != OPTION_END; options++) { + const char *rest; + int flags = 0; + + if (!options->long_name) + continue; + + rest = skip_prefix(arg, options->long_name); + if (!rest) { + /* abbreviated? */ + if (!strncmp(options->long_name, arg, arg_end - arg)) { +is_abbreviated: + if (abbrev_option) + return error("Ambiguous option: %s " + "(could be --%s%s or --%s%s)", + arg, + (flags & OPT_UNSET) ? + "no-" : "", + options->long_name, + (abbrev_flags & OPT_UNSET) ? + "no-" : "", + abbrev_option->long_name); + if (!(flags & OPT_UNSET) && *arg_end) + p->opt = arg_end + 1; + abbrev_option = options; + abbrev_flags = flags; + continue; + } + /* negated and abbreviated very much? */ + if (!prefixcmp("no-", arg)) { + flags |= OPT_UNSET; + goto is_abbreviated; + } + /* negated? */ + if (strncmp(arg, "no-", 3)) + continue; + flags |= OPT_UNSET; + rest = skip_prefix(arg + 3, options->long_name); + /* abbreviated and negated? */ + if (!rest && !prefixcmp(options->long_name, arg + 3)) + goto is_abbreviated; + if (!rest) + continue; + } + if (*rest) { + if (*rest != '=') + continue; + p->opt = rest + 1; + } + return get_value(p, options, flags); + } + if (abbrev_option) + return get_value(p, abbrev_option, abbrev_flags); + return error("unknown option `%s'", arg); +} + +int parse_options(int argc, const char **argv, const struct option *options, + const char * const usagestr[], int flags) +{ + struct optparse_t args = { argv + 1, argc - 1, NULL }; + int j = 0; + + for (; args.argc; args.argc--, args.argv++) { + const char *arg = args.argv[0]; + + if (*arg != '-' || !arg[1]) { + argv[j++] = args.argv[0]; + continue; + } + + if (arg[1] != '-') { + args.opt = arg + 1; + do { + if (*args.opt == 'h') + usage_with_options(usagestr, options); + if (parse_short_opt(&args, options) < 0) + usage_with_options(usagestr, options); + } while (args.opt); + continue; + } + + if (!arg[2]) { /* "--" */ + if (!(flags & PARSE_OPT_KEEP_DASHDASH)) { + args.argc--; + args.argv++; + } + break; + } + + if (!strcmp(arg + 2, "help")) + usage_with_options(usagestr, options); + if (parse_long_opt(&args, arg + 2, options)) + usage_with_options(usagestr, options); + } + + memmove(argv + j, args.argv, args.argc * sizeof(*argv)); + argv[j + args.argc] = NULL; + return j + args.argc; +} + +#define USAGE_OPTS_WIDTH 24 +#define USAGE_GAP 2 + +void usage_with_options(const char * const *usagestr, + const struct option *opts) +{ + fprintf(stderr, "usage: %s\n", *usagestr++); + while (*usagestr && **usagestr) + fprintf(stderr, " or: %s\n", *usagestr++); + while (*usagestr) + fprintf(stderr, " %s\n", *usagestr++); + + if (opts->type != OPTION_GROUP) + fputc('\n', stderr); + + for (; opts->type != OPTION_END; opts++) { + size_t pos; + int pad; + + if (opts->type == OPTION_GROUP) { + fputc('\n', stderr); + if (*opts->help) + fprintf(stderr, "%s\n", opts->help); + continue; + } + + pos = fprintf(stderr, " "); + if (opts->short_name) + pos += fprintf(stderr, "-%c", opts->short_name); + if (opts->long_name && opts->short_name) + pos += fprintf(stderr, ", "); + if (opts->long_name) + pos += fprintf(stderr, "--%s", opts->long_name); + + switch (opts->type) { + case OPTION_INTEGER: + if (opts->flags & PARSE_OPT_OPTARG) + pos += fprintf(stderr, " [<n>]"); + else + pos += fprintf(stderr, " <n>"); + break; + case OPTION_CALLBACK: + if (opts->flags & PARSE_OPT_NOARG) + break; + /* FALLTHROUGH */ + case OPTION_STRING: + if (opts->argh) { + if (opts->flags & PARSE_OPT_OPTARG) + pos += fprintf(stderr, " [<%s>]", opts->argh); + else + pos += fprintf(stderr, " <%s>", opts->argh); + } else { + if (opts->flags & PARSE_OPT_OPTARG) + pos += fprintf(stderr, " [...]"); + else + pos += fprintf(stderr, " ..."); + } + break; + default: + break; + } + + if (pos <= USAGE_OPTS_WIDTH) + pad = USAGE_OPTS_WIDTH - pos; + else { + fputc('\n', stderr); + pad = USAGE_OPTS_WIDTH; + } + fprintf(stderr, "%*s%s\n", pad + USAGE_GAP, "", opts->help); + } + fputc('\n', stderr); + + exit(129); +} + +/*----- some often used options -----*/ +#include "cache.h" +int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset) +{ + int v; + + if (!arg) { + v = unset ? 0 : DEFAULT_ABBREV; + } else { + v = strtol(arg, (char **)&arg, 10); + if (*arg) + return opterror(opt, "expects a numerical value", 0); + if (v && v < MINIMUM_ABBREV) + v = MINIMUM_ABBREV; + else if (v > 40) + v = 40; + } + *(int *)(opt->value) = v; + return 0; +} diff --git a/parse-options.h b/parse-options.h new file mode 100644 index 0000000000..3a470e5eb8 --- /dev/null +++ b/parse-options.h @@ -0,0 +1,70 @@ +#ifndef PARSE_OPTIONS_H +#define PARSE_OPTIONS_H + +enum parse_opt_type { + OPTION_END, + OPTION_GROUP, + OPTION_BOOLEAN, + OPTION_STRING, + OPTION_INTEGER, + OPTION_CALLBACK, +}; + +enum parse_opt_flags { + PARSE_OPT_KEEP_DASHDASH = 1, +}; + +enum parse_opt_option_flags { + PARSE_OPT_OPTARG = 1, + PARSE_OPT_NOARG = 2, +}; + +struct option; +typedef int parse_opt_cb(const struct option *, const char *arg, int unset); + +struct option { + enum parse_opt_type type; + int short_name; + const char *long_name; + void *value; + const char *argh; + const char *help; + + int flags; + parse_opt_cb *callback; + /* holds default value for PARSE_OPT_OPTARG, + though callbacks can use it like they want */ + intptr_t defval; +}; + +#define OPT_END() { OPTION_END } +#define OPT_GROUP(h) { OPTION_GROUP, 0, NULL, NULL, NULL, (h) } +#define OPT_BOOLEAN(s, l, v, h) { OPTION_BOOLEAN, (s), (l), (v), NULL, (h) } +#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), NULL, (h) } +#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) } +#define OPT_CALLBACK(s, l, v, a, h, f) \ + { OPTION_CALLBACK, (s), (l), (v), (a), (h), 0, (f) } + +/* parse_options() will filter out the processed options and leave the + * non-option argments in argv[]. + * Returns the number of arguments left in argv[]. + */ +extern int parse_options(int argc, const char **argv, + const struct option *options, + const char * const usagestr[], int flags); + +extern NORETURN void usage_with_options(const char * const *usagestr, + const struct option *options); + +/*----- some often used options -----*/ +extern int parse_opt_abbrev_cb(const struct option *, const char *, int); + +#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose") +#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet") +#define OPT__DRY_RUN(var) OPT_BOOLEAN('n', "dry-run", (var), "dry run") +#define OPT__ABBREV(var) \ + { OPTION_CALLBACK, 0, "abbrev", (var), "n", \ + "use <n> digits to display SHA-1s", \ + PARSE_OPT_OPTARG, &parse_opt_abbrev_cb, 0 } + +#endif diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh new file mode 100755 index 0000000000..ae49424aa0 --- /dev/null +++ b/t/t0040-parse-options.sh @@ -0,0 +1,93 @@ +#!/bin/sh +# +# Copyright (c) 2007 Johannes Schindelin +# + +test_description='our own option parser' + +. ./test-lib.sh + +cat > expect.err << EOF +usage: test-parse-options <options> + + -b, --boolean get a boolean + -i, --integer <n> get a integer + -j <n> get a integer, too + +string options + -s, --string <string> + get a string + --string2 <str> get another string + +EOF + +test_expect_success 'test help' ' + ! test-parse-options -h > output 2> output.err && + test ! -s output && + git diff expect.err output.err +' + +cat > expect << EOF +boolean: 2 +integer: 1729 +string: 123 +EOF + +test_expect_success 'short options' ' + test-parse-options -s123 -b -i 1729 -b > output 2> output.err && + git diff expect output && + test ! -s output.err +' +cat > expect << EOF +boolean: 2 +integer: 1729 +string: 321 +EOF + +test_expect_success 'long options' ' + test-parse-options --boolean --integer 1729 --boolean --string2=321 \ + > output 2> output.err && + test ! -s output.err && + git diff expect output +' + +cat > expect << EOF +boolean: 1 +integer: 13 +string: 123 +arg 00: a1 +arg 01: b1 +arg 02: --boolean +EOF + +test_expect_success 'intermingled arguments' ' + test-parse-options a1 --string 123 b1 --boolean -j 13 -- --boolean \ + > output 2> output.err && + test ! -s output.err && + git diff expect output +' + +cat > expect << EOF +boolean: 0 +integer: 2 +string: (not set) +EOF + +test_expect_success 'unambiguously abbreviated option' ' + test-parse-options --int 2 --boolean --no-bo > output 2> output.err && + test ! -s output.err && + git diff expect output +' + +test_expect_success 'unambiguously abbreviated option with "="' ' + test-parse-options --int=2 > output 2> output.err && + test ! -s output.err && + git diff expect output +' + +test_expect_failure 'ambiguously abbreviated option' ' + test-parse-options --strin 123; + test $? != 129 +' + +test_done diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh index d0809eb651..d0809eb651 100644..100755 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh diff --git a/test-parse-options.c b/test-parse-options.c new file mode 100644 index 0000000000..277cfe4d6d --- /dev/null +++ b/test-parse-options.c @@ -0,0 +1,35 @@ +#include "cache.h" +#include "parse-options.h" + +static int boolean = 0; +static int integer = 0; +static char *string = NULL; + +int main(int argc, const char **argv) +{ + const char *usage[] = { + "test-parse-options <options>", + NULL + }; + struct option options[] = { + OPT_BOOLEAN('b', "boolean", &boolean, "get a boolean"), + OPT_INTEGER('i', "integer", &integer, "get a integer"), + OPT_INTEGER('j', NULL, &integer, "get a integer, too"), + OPT_GROUP("string options"), + OPT_STRING('s', "string", &string, "string", "get a string"), + OPT_STRING(0, "string2", &string, "str", "get another string"), + OPT_END(), + }; + int i; + + argc = parse_options(argc, argv, options, usage, 0); + + printf("boolean: %d\n", boolean); + printf("integer: %d\n", integer); + printf("string: %s\n", string ? string : "(not set)"); + + for (i = 0; i < argc; i++) + printf("arg %02d: %s\n", i, argv[i]); + + return 0; +} |