Skip to content

Commit

Permalink
Merge branch 'ph/parseopt-sh'
Browse files Browse the repository at this point in the history
* ph/parseopt-sh:
  git-quiltimport.sh fix --patches handling
  git-am: -i does not take a string parameter.
  sh-setup: don't let eval output to be shell-expanded.
  git-sh-setup: fix parseopt `eval` string underquoting
  Give git-am back the ability to add Signed-off-by lines.
  git-rev-parse --parseopt
  scripts: Add placeholders for OPTIONS_SPEC
  Migrate git-repack.sh to use git-rev-parse --parseopt
  Migrate git-quiltimport.sh to use git-rev-parse --parseopt
  Migrate git-checkout.sh to use git-rev-parse --parseopt --keep-dashdash
  Migrate git-instaweb.sh to use git-rev-parse --parseopt
  Migrate git-merge.sh to use git-rev-parse --parseopt
  Migrate git-am.sh to use git-rev-parse --parseopt
  Migrate git-clone to use git-rev-parse --parseopt
  Migrate git-clean.sh to use git-rev-parse --parseopt.
  Update git-sh-setup(1) to allow transparent use of git-rev-parse --parseopt
  Add a parseopt mode to git-rev-parse to bring parse-options to shell scripts.
  • Loading branch information
gitster committed Nov 18, 2007
2 parents 9716f21 + 9e384b4 commit 9f4c4eb
Show file tree
Hide file tree
Showing 23 changed files with 540 additions and 322 deletions.
76 changes: 74 additions & 2 deletions Documentation/git-rev-parse.txt
Expand Up @@ -23,6 +23,13 @@ distinguish between them.

OPTIONS
-------
--parseopt::
Use `git-rev-parse` in option parsing mode (see PARSEOPT section below).

--keep-dash-dash::
Only meaningful in `--parseopt` mode. Tells the option parser to echo
out the first `--` met instead of skipping it.

--revs-only::
Do not output flags and parameters not meant for
`git-rev-list` command.
Expand Down Expand Up @@ -288,10 +295,75 @@ Here are a handful examples:
C^@ I J F
F^! D G H D F

PARSEOPT
--------

In `--parseopt` mode, `git-rev-parse` helps massaging options to bring to shell
scripts the same facilities C builtins have. It works as an option normalizer
(e.g. splits single switches aggregate values), a bit like `getopt(1)` does.

It takes on the standard input the specification of the options to parse and
understand, and echoes on the standard output a line suitable for `sh(1)` `eval`
to replace the arguments with normalized ones. In case of error, it outputs
usage on the standard error stream, and exits with code 129.

Input Format
~~~~~~~~~~~~

`git-rev-parse --parseopt` input format is fully text based. It has two parts,
separated by a line that contains only `--`. The lines before the separator
(should be more than one) are used for the usage.
The lines after the separator describe the options.

Each line of options has this format:

------------
<opt_spec><arg_spec>? SP+ help LF
------------

`<opt_spec>`::
its format is the short option character, then the long option name
separated by a comma. Both parts are not required, though at least one
is necessary. `h,help`, `dry-run` and `f` are all three correct
`<opt_spec>`.

`<arg_spec>`::
an `<arg_spec>` tells the option parser if the option has an argument
(`=`), an optional one (`?` though its use is discouraged) or none
(no `<arg_spec>` in that case).

The remainder of the line, after stripping the spaces, is used
as the help associated to the option.

Blank lines are ignored, and lines that don't match this specification are used
as option group headers (start the line with a space to create such
lines on purpose).

Example
~~~~~~~

------------
OPTS_SPEC="\
some-command [options] <args>...

some-command does foo and bar!
--
h,help show the help

foo some nifty option --foo
bar= some cool option --bar with an argument

An option group Header
C? option C with an optional argument"

eval `echo "$OPTS_SPEC" | git-rev-parse --parseopt -- "$@" || echo exit $?`
------------


Author
------
Written by Linus Torvalds <torvalds@osdl.org> and
Junio C Hamano <junkio@cox.net>
Written by Linus Torvalds <torvalds@osdl.org> .
Junio C Hamano <junkio@cox.net> and Pierre Habouzit <madcoder@debian.org>

Documentation
--------------
Expand Down
128 changes: 127 additions & 1 deletion builtin-rev-parse.c
Expand Up @@ -8,6 +8,7 @@
#include "refs.h"
#include "quote.h"
#include "builtin.h"
#include "parse-options.h"

#define DO_REVS 1
#define DO_NOREV 2
Expand Down Expand Up @@ -209,13 +210,138 @@ static int try_difference(const char *arg)
return 0;
}

static int parseopt_dump(const struct option *o, const char *arg, int unset)
{
struct strbuf *parsed = o->value;
if (unset)
strbuf_addf(parsed, " --no-%s", o->long_name);
else if (o->short_name)
strbuf_addf(parsed, " -%c", o->short_name);
else
strbuf_addf(parsed, " --%s", o->long_name);
if (arg) {
strbuf_addch(parsed, ' ');
sq_quote_buf(parsed, arg);
}
return 0;
}

static const char *skipspaces(const char *s)
{
while (isspace(*s))
s++;
return s;
}

static int cmd_parseopt(int argc, const char **argv, const char *prefix)
{
static int keep_dashdash = 0;
static char const * const parseopt_usage[] = {
"git-rev-parse --parseopt [options] -- [<args>...]",
NULL
};
static struct option parseopt_opts[] = {
OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
"keep the `--` passed as an arg"),
OPT_END(),
};

struct strbuf sb, parsed;
const char **usage = NULL;
struct option *opts = NULL;
int onb = 0, osz = 0, unb = 0, usz = 0;

strbuf_init(&parsed, 0);
strbuf_addstr(&parsed, "set --");
argc = parse_options(argc, argv, parseopt_opts, parseopt_usage,
PARSE_OPT_KEEP_DASHDASH);
if (argc < 1 || strcmp(argv[0], "--"))
usage_with_options(parseopt_usage, parseopt_opts);

strbuf_init(&sb, 0);
/* get the usage up to the first line with a -- on it */
for (;;) {
if (strbuf_getline(&sb, stdin, '\n') == EOF)
die("premature end of input");
ALLOC_GROW(usage, unb + 1, usz);
if (!strcmp("--", sb.buf)) {
if (unb < 1)
die("no usage string given before the `--' separator");
usage[unb] = NULL;
break;
}
usage[unb++] = strbuf_detach(&sb, NULL);
}

/* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */
while (strbuf_getline(&sb, stdin, '\n') != EOF) {
const char *s;
struct option *o;

if (!sb.len)
continue;

ALLOC_GROW(opts, onb + 1, osz);
memset(opts + onb, 0, sizeof(opts[onb]));

o = &opts[onb++];
s = strchr(sb.buf, ' ');
if (!s || *sb.buf == ' ') {
o->type = OPTION_GROUP;
o->help = xstrdup(skipspaces(s));
continue;
}

o->type = OPTION_CALLBACK;
o->help = xstrdup(skipspaces(s));
o->value = &parsed;
o->callback = &parseopt_dump;
switch (s[-1]) {
case '=':
s--;
break;
case '?':
o->flags = PARSE_OPT_OPTARG;
s--;
break;
default:
o->flags = PARSE_OPT_NOARG;
break;
}

if (s - sb.buf == 1) /* short option only */
o->short_name = *sb.buf;
else if (sb.buf[1] != ',') /* long option only */
o->long_name = xmemdupz(sb.buf, s - sb.buf);
else {
o->short_name = *sb.buf;
o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
}
}
strbuf_release(&sb);

/* put an OPT_END() */
ALLOC_GROW(opts, onb + 1, osz);
memset(opts + onb, 0, sizeof(opts[onb]));
argc = parse_options(argc, argv, opts, usage,
keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0);

strbuf_addf(&parsed, " --");
sq_quote_argv(&parsed, argv, argc, 0);
puts(parsed.buf);
return 0;
}

int cmd_rev_parse(int argc, const char **argv, const char *prefix)
{
int i, as_is = 0, verify = 0;
unsigned char sha1[20];

git_config(git_default_config);
if (argc > 1 && !strcmp("--parseopt", argv[1]))
return cmd_parseopt(argc - 1, argv + 1, prefix);

prefix = setup_git_directory();
git_config(git_default_config);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];

Expand Down
94 changes: 49 additions & 45 deletions git-am.sh
Expand Up @@ -2,11 +2,26 @@
#
# Copyright (c) 2005, 2006 Junio C Hamano

USAGE='[--signoff] [--dotest=<dir>] [--keep] [--utf8 | --no-utf8]
[--3way] [--interactive] [--binary]
[--whitespace=<option>] [-C<n>] [-p<n>]
<mbox>|<Maildir>...
or, when resuming [--skip | --resolved]'
OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\
git-am [options] <mbox>|<Maildir>...
git-am [options] --resolved
git-am [options] --skip
--
d,dotest= use <dir> and not .dotest
i,interactive run interactively
b,binary pass --allo-binary-replacement to git-apply
3,3way allow fall back on 3way merging if needed
s,signoff add a Signed-off-by line to the commit message
u,utf8 recode into utf8 (default)
k,keep pass -k flagg to git-mailinfo
whitespace= pass it through git-apply
C= pass it through git-apply
p= pass it through git-apply
resolvemsg= override error message when patch failure occurs
r,resolved to be used after a patch failure
skip skip the current patch"

. git-sh-setup
set_reflog_action am
require_work_tree
Expand Down Expand Up @@ -110,49 +125,38 @@ git_apply_opt=
while test $# != 0
do
case "$1" in
-d=*|--d=*|--do=*|--dot=*|--dote=*|--dotes=*|--dotest=*)
dotest=`expr "z$1" : 'z-[^=]*=\(.*\)'`; shift ;;
-d|--d|--do|--dot|--dote|--dotes|--dotest)
case "$#" in 1) usage ;; esac; shift
dotest="$1"; shift;;

-i|--i|--in|--int|--inte|--inter|--intera|--interac|--interact|\
--interacti|--interactiv|--interactive)
interactive=t; shift ;;

-b|--b|--bi|--bin|--bina|--binar|--binary)
binary=t; shift ;;

-3|--3|--3w|--3wa|--3way)
threeway=t; shift ;;
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
sign=t; shift ;;
-u|--u|--ut|--utf|--utf8)
utf8=t; shift ;; # this is now default
--no-u|--no-ut|--no-utf|--no-utf8)
utf8=; shift ;;
-k|--k|--ke|--kee|--keep)
keep=t; shift ;;

-r|--r|--re|--res|--reso|--resol|--resolv|--resolve|--resolved)
resolved=t; shift ;;

--sk|--ski|--skip)
skip=t; shift ;;

--whitespace=*|-C*|-p*)
git_apply_opt="$git_apply_opt $1"; shift ;;

--resolvemsg=*)
resolvemsg=${1#--resolvemsg=}; shift ;;

-i|--interactive)
interactive=t ;;
-b|--binary)
binary=t ;;
-3|--3way)
threeway=t ;;
-s|--signoff)
sign=t ;;
-u|--utf8)
utf8=t ;; # this is now default
--no-utf8)
utf8= ;;
-k|--keep)
keep=t ;;
-r|--resolved)
resolved=t ;;
--skip)
skip=t ;;
-d|--dotest)
shift; dotest=$1;;
--resolvemsg)
shift; resolvemsg=$1 ;;
--whitespace)
git_apply_opt="$git_apply_opt $1=$2"; shift ;;
-C|-p)
git_apply_opt="$git_apply_opt $1$2"; shift ;;
--)
shift; break ;;
-*)
usage ;;
shift; break ;;
*)
break ;;
usage ;;
esac
shift
done

# If the dotest directory exists, but we have finished applying all the
Expand Down
1 change: 1 addition & 0 deletions git-bisect.sh
Expand Up @@ -22,6 +22,7 @@ git bisect log
git bisect run <cmd>...
use <cmd>... to automatically bisect.'

OPTIONS_SPEC=
. git-sh-setup
require_work_tree

Expand Down

0 comments on commit 9f4c4eb

Please sign in to comment.