Merge branch 'br/commit-tree-parseopt'

The command line parser of "git commit-tree" has been rewritten to
use the parse-options API.

* br/commit-tree-parseopt:
  commit-tree: utilize parse-options api
This commit is contained in:
Junio C Hamano 2019-03-20 15:16:08 +09:00
commit 0c45fa32ec
3 changed files with 104 additions and 74 deletions

View File

@ -23,6 +23,10 @@ Creates a new commit object based on the provided tree object and
emits the new commit object id on stdout. The log message is read emits the new commit object id on stdout. The log message is read
from the standard input, unless `-m` or `-F` options are given. from the standard input, unless `-m` or `-F` options are given.
The `-m` and `-F` options can be given any number of times, in any
order. The commit log message will be composed in the order in which
the options are given.
A commit object may have any number of parents. With exactly one A commit object may have any number of parents. With exactly one
parent, it is an ordinary commit. Having more than one parent makes parent, it is an ordinary commit. Having more than one parent makes
the commit a merge between several lines of history. Initial (root) the commit a merge between several lines of history. Initial (root)
@ -41,7 +45,7 @@ state was.
OPTIONS OPTIONS
------- -------
<tree>:: <tree>::
An existing tree object An existing tree object.
-p <parent>:: -p <parent>::
Each `-p` indicates the id of a parent commit object. Each `-p` indicates the id of a parent commit object.
@ -52,7 +56,8 @@ OPTIONS
-F <file>:: -F <file>::
Read the commit log message from the given file. Use `-` to read Read the commit log message from the given file. Use `-` to read
from the standard input. from the standard input. This can be given more than once and the
content of each file becomes its own paragraph.
-S[<keyid>]:: -S[<keyid>]::
--gpg-sign[=<keyid>]:: --gpg-sign[=<keyid>]::

View File

@ -12,8 +12,13 @@
#include "builtin.h" #include "builtin.h"
#include "utf8.h" #include "utf8.h"
#include "gpg-interface.h" #include "gpg-interface.h"
#include "parse-options.h"
static const char commit_tree_usage[] = "git commit-tree [(-p <sha1>)...] [-S[<keyid>]] [-m <message>] [-F <file>] <sha1>"; static const char * const commit_tree_usage[] = {
N_("git commit-tree [(-p <parent>)...] [-S[<keyid>]] [(-m <message>)...] "
"[(-F <file>)...] <tree>"),
NULL
};
static const char *sign_commit; static const char *sign_commit;
@ -23,7 +28,7 @@ static void new_parent(struct commit *parent, struct commit_list **parents_p)
struct commit_list *parents; struct commit_list *parents;
for (parents = *parents_p; parents; parents = parents->next) { for (parents = *parents_p; parents; parents = parents->next) {
if (parents->item == parent) { if (parents->item == parent) {
error("duplicate parent %s ignored", oid_to_hex(oid)); error(_("duplicate parent %s ignored"), oid_to_hex(oid));
return; return;
} }
parents_p = &parents->next; parents_p = &parents->next;
@ -39,91 +44,100 @@ static int commit_tree_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb); return git_default_config(var, value, cb);
} }
static int parse_parent_arg_callback(const struct option *opt,
const char *arg, int unset)
{
struct object_id oid;
struct commit_list **parents = opt->value;
BUG_ON_OPT_NEG_NOARG(unset, arg);
if (get_oid_commit(arg, &oid))
die(_("not a valid object name %s"), arg);
assert_oid_type(&oid, OBJ_COMMIT);
new_parent(lookup_commit(the_repository, &oid), parents);
return 0;
}
static int parse_message_arg_callback(const struct option *opt,
const char *arg, int unset)
{
struct strbuf *buf = opt->value;
BUG_ON_OPT_NEG_NOARG(unset, arg);
if (buf->len)
strbuf_addch(buf, '\n');
strbuf_addstr(buf, arg);
strbuf_complete_line(buf);
return 0;
}
static int parse_file_arg_callback(const struct option *opt,
const char *arg, int unset)
{
int fd;
struct strbuf *buf = opt->value;
BUG_ON_OPT_NEG_NOARG(unset, arg);
if (buf->len)
strbuf_addch(buf, '\n');
if (!strcmp(arg, "-"))
fd = 0;
else {
fd = open(arg, O_RDONLY);
if (fd < 0)
die_errno(_("git commit-tree: failed to open '%s'"), arg);
}
if (strbuf_read(buf, fd, 0) < 0)
die_errno(_("git commit-tree: failed to read '%s'"), arg);
if (fd && close(fd))
die_errno(_("git commit-tree: failed to close '%s'"), arg);
return 0;
}
int cmd_commit_tree(int argc, const char **argv, const char *prefix) int cmd_commit_tree(int argc, const char **argv, const char *prefix)
{ {
int i, got_tree = 0; static struct strbuf buffer = STRBUF_INIT;
struct commit_list *parents = NULL; struct commit_list *parents = NULL;
struct object_id tree_oid; struct object_id tree_oid;
struct object_id commit_oid; struct object_id commit_oid;
struct strbuf buffer = STRBUF_INIT;
struct option options[] = {
{ OPTION_CALLBACK, 'p', NULL, &parents, N_("parent"),
N_("id of a parent commit object"), PARSE_OPT_NONEG,
parse_parent_arg_callback },
{ OPTION_CALLBACK, 'm', NULL, &buffer, N_("message"),
N_("commit message"), PARSE_OPT_NONEG,
parse_message_arg_callback },
{ OPTION_CALLBACK, 'F', NULL, &buffer, N_("file"),
N_("read commit log message from file"), PARSE_OPT_NONEG,
parse_file_arg_callback },
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
OPT_END()
};
git_config(commit_tree_config, NULL); git_config(commit_tree_config, NULL);
if (argc < 2 || !strcmp(argv[1], "-h")) if (argc < 2 || !strcmp(argv[1], "-h"))
usage(commit_tree_usage); usage_with_options(commit_tree_usage, options);
for (i = 1; i < argc; i++) { argc = parse_options(argc, argv, prefix, options, commit_tree_usage, 0);
const char *arg = argv[i];
if (!strcmp(arg, "-p")) {
struct object_id oid;
if (argc <= ++i)
usage(commit_tree_usage);
if (get_oid_commit(argv[i], &oid))
die("Not a valid object name %s", argv[i]);
assert_oid_type(&oid, OBJ_COMMIT);
new_parent(lookup_commit(the_repository, &oid),
&parents);
continue;
}
if (!strcmp(arg, "--gpg-sign")) { if (argc != 1)
sign_commit = ""; die(_("must give exactly one tree"));
continue;
}
if (skip_prefix(arg, "-S", &sign_commit) || if (get_oid_tree(argv[0], &tree_oid))
skip_prefix(arg, "--gpg-sign=", &sign_commit)) die(_("not a valid object name %s"), argv[0]);
continue;
if (!strcmp(arg, "--no-gpg-sign")) {
sign_commit = NULL;
continue;
}
if (!strcmp(arg, "-m")) {
if (argc <= ++i)
usage(commit_tree_usage);
if (buffer.len)
strbuf_addch(&buffer, '\n');
strbuf_addstr(&buffer, argv[i]);
strbuf_complete_line(&buffer);
continue;
}
if (!strcmp(arg, "-F")) {
int fd;
if (argc <= ++i)
usage(commit_tree_usage);
if (buffer.len)
strbuf_addch(&buffer, '\n');
if (!strcmp(argv[i], "-"))
fd = 0;
else {
fd = open(argv[i], O_RDONLY);
if (fd < 0)
die_errno("git commit-tree: failed to open '%s'",
argv[i]);
}
if (strbuf_read(&buffer, fd, 0) < 0)
die_errno("git commit-tree: failed to read '%s'",
argv[i]);
if (fd && close(fd))
die_errno("git commit-tree: failed to close '%s'",
argv[i]);
continue;
}
if (get_oid_tree(arg, &tree_oid))
die("Not a valid object name %s", arg);
if (got_tree)
die("Cannot give more than one trees");
got_tree = 1;
}
if (!buffer.len) { if (!buffer.len) {
if (strbuf_read(&buffer, 0, 0) < 0) if (strbuf_read(&buffer, 0, 0) < 0)
die_errno("git commit-tree: failed to read"); die_errno(_("git commit-tree: failed to read"));
} }
if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid, if (commit_tree(buffer.buf, buffer.len, &tree_oid, parents, &commit_oid,

View File

@ -222,6 +222,17 @@ const char *optname(const struct option *opt, int flags);
BUG("option callback does not expect an argument"); \ BUG("option callback does not expect an argument"); \
} while (0) } while (0)
/*
* Similar to the assertions above, but checks that "arg" is always non-NULL.
* This assertion also implies BUG_ON_OPT_NEG(), letting you declare both
* assertions in a single line.
*/
#define BUG_ON_OPT_NEG_NOARG(unset, arg) do { \
BUG_ON_OPT_NEG(unset); \
if(!(arg)) \
BUG("option callback expects an argument"); \
} while(0)
/*----- incremental advanced APIs -----*/ /*----- incremental advanced APIs -----*/
enum parse_opt_result { enum parse_opt_result {