Merge branch 'ph/parseopt'

* ph/parseopt: (24 commits)
  gc: use parse_options
  Fixed a command line option type for builtin-fsck.c
  Make builtin-pack-refs.c use parse_options.
  Make builtin-name-rev.c use parse_options.
  Make builtin-count-objects.c use parse_options.
  Make builtin-fsck.c use parse_options.
  Update manpages to reflect new short and long option aliases
  Make builtin-for-each-ref.c use parse-opts.
  Make builtin-symbolic-ref.c use parse_options.
  Make builtin-update-ref.c use parse_options
  Make builtin-revert.c use parse_options.
  Make builtin-describe.c use parse_options
  Make builtin-branch.c use parse_options.
  Make builtin-mv.c use parse-options
  Make builtin-rm.c use parse_options.
  Port builtin-add.c to use the new option parser.
  parse-options: allow callbacks to take no arguments at all.
  parse-options: Allow abbreviated options when unambiguous
  Add shortcuts for very often used options.
  parse-options: make some arguments optional, add callbacks.
  ...

Conflicts:

	Makefile
	builtin-add.c
This commit is contained in:
Junio C Hamano 2007-11-02 16:42:23 -07:00
commit 3d66dc9657
26 changed files with 932 additions and 613 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -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::

View File

@ -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>::

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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)

View File

@ -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);

View File

@ -11,9 +11,15 @@
#include "commit.h"
#include "builtin.h"
#include "remote.h"
#include "parse-options.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]]";
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;
argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
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")) {
delete |= force_delete;
rename |= force_rename;
if (kind_remote)
kinds = REF_REMOTE_BRANCH;
continue;
}
if (!strcmp(arg, "-a")) {
if (kind_any)
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)
if (abbrev && 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);
}
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;
}

View File

@ -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++] = '/';

View File

@ -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);
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;
}
else
usage(describe_usage);
}
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;
}

View File

@ -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)
{
int i, num_refs;
const char *format = NULL;
struct ref_sort *sort = NULL, **sort_tail = &sort;
int maxcount = 0;
int quote_style = -1; /* unspecified yet */
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));
struct ref_sort **sort_tail = opt->value;
struct ref_sort *s;
int len;
s->next = NULL;
*sort_tail = s;
if (!arg) /* should --no-sort void the list ? */
return -1;
*sort_tail = s = xcalloc(1, sizeof(*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;
}
if (quote_style < 0)
quote_style = QUOTE_NONE;
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 = "%(objectname) %(objecttype)\t%(refname)";
struct ref_sort *sort = NULL, **sort_tail = &sort;
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;
struct option opts[] = {
OPT_BOOLEAN('s', "shell", &quote_shell, "quote placeholders suitably for shells"),
OPT_BOOLEAN('p', "perl", &quote_perl, "quote placeholders suitably for perl"),
OPT_BOOLEAN( 0 , "python", &quote_python, "quote placeholders suitably for python"),
OPT_BOOLEAN( 0 , "tcl", &quote_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_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;

View File

@ -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")) {
argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0);
if (write_lost_and_found) {
check_full = 1;
include_reflogs = 0;
write_lost_and_found = 1;
continue;
}
if (*arg == '-')
usage(fsck_usage);
}
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;

View File

@ -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")) {
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);
}
continue;
}
if (!strcmp(arg, "--auto")) {
auto_gc = 1;
continue;
}
break;
}
if (i != argc)
usage(builtin_gc_usage);
if (auto_gc) {
/*

View File

@ -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)

View File

@ -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);
}

View File

@ -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;
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(),
};
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")) {
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;
continue;
}
/* perhaps other parameters later... */
break;
}
if (i != argc)
usage(builtin_pack_refs_usage);
return pack_refs(flags);
}

View File

@ -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;
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 (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];
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);
}

View File

@ -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];
argc = parse_options(argc, argv, builtin_rm_options, builtin_rm_usage, 0);
if (!argc)
usage_with_options(builtin_rm_usage, builtin_rm_options);
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);
pathspec = get_pathspec(prefix, argv + i);
pathspec = get_pathspec(prefix, argv);
seen = NULL;
for (i = 0; pathspec[i] ; i++)
/* nothing */;

View File

@ -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)
argc = parse_options(argc, argv, options, git_symbolic_ref_usage, 0);
if (msg &&!*msg)
die("Refusing to perform update with empty message");
}
else if (!strcmp("--", arg)) {
argc--;
argv++;
break;
}
else
die("unknown option %s", arg);
argc--;
argv++;
}
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;
}

View File

@ -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);
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)
argc = parse_options(argc, argv, options, git_update_ref_usage, 0);
if (msg && !*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);
}

322
parse-options.c Normal file
View File

@ -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;
}

70
parse-options.h Normal file
View File

@ -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

93
t/t0040-parse-options.sh Executable file
View File

@ -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

0
t/t6300-for-each-ref.sh Normal file → Executable file
View File

35
test-parse-options.c Normal file
View File

@ -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;
}