Merge branch 'ph/parseopt-step-blame'
* ph/parseopt-step-blame: revisions: refactor handle_revision_opt into parse_revision_opt. git-shortlog: migrate to parse-options partially. git-blame: fix lapsus git-blame: migrate to incremental parse-option [2/2] git-blame: migrate to incremental parse-option [1/2] revisions: split handle_revision_opt() from setup_revisions() parse-opt: add PARSE_OPT_KEEP_ARGV0 parser option. parse-opt: fake short strings for callers to believe in. parse-opt: do not print errors on unknown options, return -2 intead. parse-opt: create parse_options_step. parse-opt: Export a non NORETURN usage dumper. parse-opt: have parse_options_{start,end}. git-blame --reverse builtin-blame.c: allow more than 16 parents builtin-blame.c: move prepare_final() into a separate function. rev-list --children revision traversal: --children option
This commit is contained in:
commit
fa6200fc02
@ -45,6 +45,10 @@ endif::git-rev-list[]
|
||||
|
||||
Print the parents of the commit.
|
||||
|
||||
--children::
|
||||
|
||||
Print the children of the commit.
|
||||
|
||||
ifdef::git-rev-list[]
|
||||
--timestamp::
|
||||
Print the raw commit timestamp.
|
||||
|
519
builtin-blame.c
519
builtin-blame.c
@ -18,24 +18,16 @@
|
||||
#include "cache-tree.h"
|
||||
#include "path-list.h"
|
||||
#include "mailmap.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static char blame_usage[] =
|
||||
"git-blame [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
|
||||
" -c Use the same output mode as git-annotate (Default: off)\n"
|
||||
" -b Show blank SHA-1 for boundary commits (Default: off)\n"
|
||||
" -l Show long commit SHA1 (Default: off)\n"
|
||||
" --root Do not treat root commits as boundaries (Default: off)\n"
|
||||
" -t Show raw timestamp (Default: off)\n"
|
||||
" -f, --show-name Show original filename (Default: auto)\n"
|
||||
" -n, --show-number Show original linenumber (Default: off)\n"
|
||||
" -s Suppress author name and timestamp (Default: off)\n"
|
||||
" -p, --porcelain Show in a format designed for machine consumption\n"
|
||||
" -w Ignore whitespace differences\n"
|
||||
" -L n,m Process only line range n,m, counting from 1\n"
|
||||
" -M, -C Find line movements within and across files\n"
|
||||
" --incremental Show blame entries as we find them, incrementally\n"
|
||||
" --contents file Use <file>'s contents as the final image\n"
|
||||
" -S revs-file Use revisions from revs-file instead of calling git-rev-list\n";
|
||||
static char blame_usage[] = "git-blame [options] [rev-opts] [rev] [--] file";
|
||||
|
||||
static const char *blame_opt_usage[] = {
|
||||
blame_usage,
|
||||
"",
|
||||
"[rev-opts] are documented in git-rev-list(1)",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int longest_file;
|
||||
static int longest_author;
|
||||
@ -43,6 +35,7 @@ static int max_orig_digits;
|
||||
static int max_digits;
|
||||
static int max_score_digits;
|
||||
static int show_root;
|
||||
static int reverse;
|
||||
static int blank_boundary;
|
||||
static int incremental;
|
||||
static int cmd_is_annotate;
|
||||
@ -91,7 +84,7 @@ struct origin {
|
||||
* Given an origin, prepare mmfile_t structure to be used by the
|
||||
* diff machinery
|
||||
*/
|
||||
static char *fill_origin_blob(struct origin *o, mmfile_t *file)
|
||||
static void fill_origin_blob(struct origin *o, mmfile_t *file)
|
||||
{
|
||||
if (!o->file.ptr) {
|
||||
enum object_type type;
|
||||
@ -106,7 +99,6 @@ static char *fill_origin_blob(struct origin *o, mmfile_t *file)
|
||||
}
|
||||
else
|
||||
*file = o->file;
|
||||
return file->ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -178,7 +170,7 @@ struct blame_entry {
|
||||
struct scoreboard {
|
||||
/* the final commit (i.e. where we started digging from) */
|
||||
struct commit *final;
|
||||
|
||||
struct rev_info *revs;
|
||||
const char *path;
|
||||
|
||||
/*
|
||||
@ -1192,18 +1184,48 @@ static void pass_whole_blame(struct scoreboard *sb,
|
||||
}
|
||||
}
|
||||
|
||||
#define MAXPARENT 16
|
||||
/*
|
||||
* We pass blame from the current commit to its parents. We keep saying
|
||||
* "parent" (and "porigin"), but what we mean is to find scapegoat to
|
||||
* exonerate ourselves.
|
||||
*/
|
||||
static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
|
||||
{
|
||||
if (!reverse)
|
||||
return commit->parents;
|
||||
return lookup_decoration(&revs->children, &commit->object);
|
||||
}
|
||||
|
||||
static int num_scapegoats(struct rev_info *revs, struct commit *commit)
|
||||
{
|
||||
int cnt;
|
||||
struct commit_list *l = first_scapegoat(revs, commit);
|
||||
for (cnt = 0; l; l = l->next)
|
||||
cnt++;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
#define MAXSG 16
|
||||
|
||||
static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
{
|
||||
int i, pass;
|
||||
struct rev_info *revs = sb->revs;
|
||||
int i, pass, num_sg;
|
||||
struct commit *commit = origin->commit;
|
||||
struct commit_list *parent;
|
||||
struct origin *parent_origin[MAXPARENT], *porigin;
|
||||
struct commit_list *sg;
|
||||
struct origin *sg_buf[MAXSG];
|
||||
struct origin *porigin, **sg_origin = sg_buf;
|
||||
|
||||
memset(parent_origin, 0, sizeof(parent_origin));
|
||||
num_sg = num_scapegoats(revs, commit);
|
||||
if (!num_sg)
|
||||
goto finish;
|
||||
else if (num_sg < ARRAY_SIZE(sg_buf))
|
||||
memset(sg_buf, 0, sizeof(sg_buf));
|
||||
else
|
||||
sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
|
||||
|
||||
/* The first pass looks for unrenamed path to optimize for
|
||||
/*
|
||||
* The first pass looks for unrenamed path to optimize for
|
||||
* common cases, then we look for renames in the second pass.
|
||||
*/
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
@ -1211,13 +1233,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
struct commit *, struct origin *);
|
||||
find = pass ? find_rename : find_origin;
|
||||
|
||||
for (i = 0, parent = commit->parents;
|
||||
i < MAXPARENT && parent;
|
||||
parent = parent->next, i++) {
|
||||
struct commit *p = parent->item;
|
||||
for (i = 0, sg = first_scapegoat(revs, commit);
|
||||
i < num_sg && sg;
|
||||
sg = sg->next, i++) {
|
||||
struct commit *p = sg->item;
|
||||
int j, same;
|
||||
|
||||
if (parent_origin[i])
|
||||
if (sg_origin[i])
|
||||
continue;
|
||||
if (parse_commit(p))
|
||||
continue;
|
||||
@ -1230,24 +1252,24 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
goto finish;
|
||||
}
|
||||
for (j = same = 0; j < i; j++)
|
||||
if (parent_origin[j] &&
|
||||
!hashcmp(parent_origin[j]->blob_sha1,
|
||||
if (sg_origin[j] &&
|
||||
!hashcmp(sg_origin[j]->blob_sha1,
|
||||
porigin->blob_sha1)) {
|
||||
same = 1;
|
||||
break;
|
||||
}
|
||||
if (!same)
|
||||
parent_origin[i] = porigin;
|
||||
sg_origin[i] = porigin;
|
||||
else
|
||||
origin_decref(porigin);
|
||||
}
|
||||
}
|
||||
|
||||
num_commits++;
|
||||
for (i = 0, parent = commit->parents;
|
||||
i < MAXPARENT && parent;
|
||||
parent = parent->next, i++) {
|
||||
struct origin *porigin = parent_origin[i];
|
||||
for (i = 0, sg = first_scapegoat(revs, commit);
|
||||
i < num_sg && sg;
|
||||
sg = sg->next, i++) {
|
||||
struct origin *porigin = sg_origin[i];
|
||||
if (!porigin)
|
||||
continue;
|
||||
if (pass_blame_to_parent(sb, origin, porigin))
|
||||
@ -1258,10 +1280,10 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
* Optionally find moves in parents' files.
|
||||
*/
|
||||
if (opt & PICKAXE_BLAME_MOVE)
|
||||
for (i = 0, parent = commit->parents;
|
||||
i < MAXPARENT && parent;
|
||||
parent = parent->next, i++) {
|
||||
struct origin *porigin = parent_origin[i];
|
||||
for (i = 0, sg = first_scapegoat(revs, commit);
|
||||
i < num_sg && sg;
|
||||
sg = sg->next, i++) {
|
||||
struct origin *porigin = sg_origin[i];
|
||||
if (!porigin)
|
||||
continue;
|
||||
if (find_move_in_parent(sb, origin, porigin))
|
||||
@ -1272,23 +1294,25 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
|
||||
* Optionally find copies from parents' files.
|
||||
*/
|
||||
if (opt & PICKAXE_BLAME_COPY)
|
||||
for (i = 0, parent = commit->parents;
|
||||
i < MAXPARENT && parent;
|
||||
parent = parent->next, i++) {
|
||||
struct origin *porigin = parent_origin[i];
|
||||
if (find_copy_in_parent(sb, origin, parent->item,
|
||||
for (i = 0, sg = first_scapegoat(revs, commit);
|
||||
i < num_sg && sg;
|
||||
sg = sg->next, i++) {
|
||||
struct origin *porigin = sg_origin[i];
|
||||
if (find_copy_in_parent(sb, origin, sg->item,
|
||||
porigin, opt))
|
||||
goto finish;
|
||||
}
|
||||
|
||||
finish:
|
||||
for (i = 0; i < MAXPARENT; i++) {
|
||||
if (parent_origin[i]) {
|
||||
drop_origin_blob(parent_origin[i]);
|
||||
origin_decref(parent_origin[i]);
|
||||
for (i = 0; i < num_sg; i++) {
|
||||
if (sg_origin[i]) {
|
||||
drop_origin_blob(sg_origin[i]);
|
||||
origin_decref(sg_origin[i]);
|
||||
}
|
||||
}
|
||||
drop_origin_blob(origin);
|
||||
if (sg_buf != sg_origin)
|
||||
free(sg_origin);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1487,8 +1511,10 @@ static void found_guilty_entry(struct blame_entry *ent)
|
||||
* is still unknown, pick one blame_entry, and allow its current
|
||||
* suspect to pass blames to its parents.
|
||||
*/
|
||||
static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
|
||||
static void assign_blame(struct scoreboard *sb, int opt)
|
||||
{
|
||||
struct rev_info *revs = sb->revs;
|
||||
|
||||
while (1) {
|
||||
struct blame_entry *ent;
|
||||
struct commit *commit;
|
||||
@ -1509,8 +1535,9 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
|
||||
commit = suspect->commit;
|
||||
if (!commit->object.parsed)
|
||||
parse_commit(commit);
|
||||
if (!(commit->object.flags & UNINTERESTING) &&
|
||||
!(revs->max_age != -1 && commit->date < revs->max_age))
|
||||
if (reverse ||
|
||||
(!(commit->object.flags & UNINTERESTING) &&
|
||||
!(revs->max_age != -1 && commit->date < revs->max_age)))
|
||||
pass_blame(sb, suspect, opt);
|
||||
else {
|
||||
commit->object.flags |= UNINTERESTING;
|
||||
@ -2006,6 +2033,10 @@ static int git_blame_config(const char *var, const char *value, void *cb)
|
||||
return git_default_config(var, value, cb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare a dummy commit that represents the work tree (or staged) item.
|
||||
* Note that annotating work tree item never works in the reverse.
|
||||
*/
|
||||
static struct commit *fake_working_tree_commit(const char *path, const char *contents_from)
|
||||
{
|
||||
struct commit *commit;
|
||||
@ -2122,6 +2153,108 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
|
||||
return commit;
|
||||
}
|
||||
|
||||
static const char *prepare_final(struct scoreboard *sb)
|
||||
{
|
||||
int i;
|
||||
const char *final_commit_name = NULL;
|
||||
struct rev_info *revs = sb->revs;
|
||||
|
||||
/*
|
||||
* There must be one and only one positive commit in the
|
||||
* revs->pending array.
|
||||
*/
|
||||
for (i = 0; i < revs->pending.nr; i++) {
|
||||
struct object *obj = revs->pending.objects[i].item;
|
||||
if (obj->flags & UNINTERESTING)
|
||||
continue;
|
||||
while (obj->type == OBJ_TAG)
|
||||
obj = deref_tag(obj, NULL, 0);
|
||||
if (obj->type != OBJ_COMMIT)
|
||||
die("Non commit %s?", revs->pending.objects[i].name);
|
||||
if (sb->final)
|
||||
die("More than one commit to dig from %s and %s?",
|
||||
revs->pending.objects[i].name,
|
||||
final_commit_name);
|
||||
sb->final = (struct commit *) obj;
|
||||
final_commit_name = revs->pending.objects[i].name;
|
||||
}
|
||||
return final_commit_name;
|
||||
}
|
||||
|
||||
static const char *prepare_initial(struct scoreboard *sb)
|
||||
{
|
||||
int i;
|
||||
const char *final_commit_name = NULL;
|
||||
struct rev_info *revs = sb->revs;
|
||||
|
||||
/*
|
||||
* There must be one and only one negative commit, and it must be
|
||||
* the boundary.
|
||||
*/
|
||||
for (i = 0; i < revs->pending.nr; i++) {
|
||||
struct object *obj = revs->pending.objects[i].item;
|
||||
if (!(obj->flags & UNINTERESTING))
|
||||
continue;
|
||||
while (obj->type == OBJ_TAG)
|
||||
obj = deref_tag(obj, NULL, 0);
|
||||
if (obj->type != OBJ_COMMIT)
|
||||
die("Non commit %s?", revs->pending.objects[i].name);
|
||||
if (sb->final)
|
||||
die("More than one commit to dig down to %s and %s?",
|
||||
revs->pending.objects[i].name,
|
||||
final_commit_name);
|
||||
sb->final = (struct commit *) obj;
|
||||
final_commit_name = revs->pending.objects[i].name;
|
||||
}
|
||||
if (!final_commit_name)
|
||||
die("No commit to dig down to?");
|
||||
return final_commit_name;
|
||||
}
|
||||
|
||||
static int blame_copy_callback(const struct option *option, const char *arg, int unset)
|
||||
{
|
||||
int *opt = option->value;
|
||||
|
||||
/*
|
||||
* -C enables copy from removed files;
|
||||
* -C -C enables copy from existing files, but only
|
||||
* when blaming a new file;
|
||||
* -C -C -C enables copy from existing files for
|
||||
* everybody
|
||||
*/
|
||||
if (*opt & PICKAXE_BLAME_COPY_HARDER)
|
||||
*opt |= PICKAXE_BLAME_COPY_HARDEST;
|
||||
if (*opt & PICKAXE_BLAME_COPY)
|
||||
*opt |= PICKAXE_BLAME_COPY_HARDER;
|
||||
*opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
|
||||
|
||||
if (arg)
|
||||
blame_copy_score = parse_score(arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blame_move_callback(const struct option *option, const char *arg, int unset)
|
||||
{
|
||||
int *opt = option->value;
|
||||
|
||||
*opt |= PICKAXE_BLAME_MOVE;
|
||||
|
||||
if (arg)
|
||||
blame_move_score = parse_score(arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blame_bottomtop_callback(const struct option *option, const char *arg, int unset)
|
||||
{
|
||||
const char **bottomtop = option->value;
|
||||
if (!arg)
|
||||
return -1;
|
||||
if (*bottomtop)
|
||||
die("More than one '-L n,m' option given");
|
||||
*bottomtop = arg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct rev_info revs;
|
||||
@ -2129,102 +2262,66 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||
struct scoreboard sb;
|
||||
struct origin *o;
|
||||
struct blame_entry *ent;
|
||||
int i, seen_dashdash, unk, opt;
|
||||
long bottom, top, lno;
|
||||
int output_option = 0;
|
||||
int show_stats = 0;
|
||||
const char *revs_file = NULL;
|
||||
long dashdash_pos, bottom, top, lno;
|
||||
const char *final_commit_name = NULL;
|
||||
enum object_type type;
|
||||
const char *bottomtop = NULL;
|
||||
const char *contents_from = NULL;
|
||||
|
||||
static const char *bottomtop = NULL;
|
||||
static int output_option = 0, opt = 0;
|
||||
static int show_stats = 0;
|
||||
static const char *revs_file = NULL;
|
||||
static const char *contents_from = NULL;
|
||||
static const struct option options[] = {
|
||||
OPT_BOOLEAN(0, "incremental", &incremental, "Show blame entries as we find them, incrementally"),
|
||||
OPT_BOOLEAN('b', NULL, &blank_boundary, "Show blank SHA-1 for boundary commits (Default: off)"),
|
||||
OPT_BOOLEAN(0, "root", &show_root, "Do not treat root commits as boundaries (Default: off)"),
|
||||
OPT_BOOLEAN(0, "show-stats", &show_stats, "Show work cost statistics"),
|
||||
OPT_BIT(0, "score-debug", &output_option, "Show output score for blame entries", OUTPUT_SHOW_SCORE),
|
||||
OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
|
||||
OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
|
||||
OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
|
||||
OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
|
||||
OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
|
||||
OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
|
||||
OPT_BIT('s', NULL, &output_option, "Suppress author name and timestamp (Default: off)", OUTPUT_NO_AUTHOR),
|
||||
OPT_BIT('w', NULL, &xdl_opts, "Ignore whitespace differences", XDF_IGNORE_WHITESPACE),
|
||||
OPT_STRING('S', NULL, &revs_file, "file", "Use revisions from <file> instead of calling git-rev-list"),
|
||||
OPT_STRING(0, "contents", &contents_from, "file", "Use <file>'s contents as the final image"),
|
||||
{ OPTION_CALLBACK, 'C', NULL, &opt, "score", "Find line copies within and across files", PARSE_OPT_OPTARG, blame_copy_callback },
|
||||
{ OPTION_CALLBACK, 'M', NULL, &opt, "score", "Find line movements within and across files", PARSE_OPT_OPTARG, blame_move_callback },
|
||||
OPT_CALLBACK('L', NULL, &bottomtop, "n,m", "Process only line range n,m, counting from 1", blame_bottomtop_callback),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
struct parse_opt_ctx_t ctx;
|
||||
|
||||
cmd_is_annotate = !strcmp(argv[0], "annotate");
|
||||
|
||||
git_config(git_blame_config, NULL);
|
||||
init_revisions(&revs, NULL);
|
||||
save_commit_buffer = 0;
|
||||
dashdash_pos = 0;
|
||||
|
||||
opt = 0;
|
||||
seen_dashdash = 0;
|
||||
for (unk = i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (*arg != '-')
|
||||
break;
|
||||
else if (!strcmp("-b", arg))
|
||||
blank_boundary = 1;
|
||||
else if (!strcmp("--root", arg))
|
||||
show_root = 1;
|
||||
else if (!strcmp(arg, "--show-stats"))
|
||||
show_stats = 1;
|
||||
else if (!strcmp("-c", arg))
|
||||
output_option |= OUTPUT_ANNOTATE_COMPAT;
|
||||
else if (!strcmp("-t", arg))
|
||||
output_option |= OUTPUT_RAW_TIMESTAMP;
|
||||
else if (!strcmp("-l", arg))
|
||||
output_option |= OUTPUT_LONG_OBJECT_NAME;
|
||||
else if (!strcmp("-s", arg))
|
||||
output_option |= OUTPUT_NO_AUTHOR;
|
||||
else if (!strcmp("-w", arg))
|
||||
xdl_opts |= XDF_IGNORE_WHITESPACE;
|
||||
else if (!strcmp("-S", arg) && ++i < argc)
|
||||
revs_file = argv[i];
|
||||
else if (!prefixcmp(arg, "-M")) {
|
||||
opt |= PICKAXE_BLAME_MOVE;
|
||||
blame_move_score = parse_score(arg+2);
|
||||
parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
|
||||
PARSE_OPT_KEEP_ARGV0);
|
||||
for (;;) {
|
||||
switch (parse_options_step(&ctx, options, blame_opt_usage)) {
|
||||
case PARSE_OPT_HELP:
|
||||
exit(129);
|
||||
case PARSE_OPT_DONE:
|
||||
if (ctx.argv[0])
|
||||
dashdash_pos = ctx.cpidx;
|
||||
goto parse_done;
|
||||
}
|
||||
else if (!prefixcmp(arg, "-C")) {
|
||||
/*
|
||||
* -C enables copy from removed files;
|
||||
* -C -C enables copy from existing files, but only
|
||||
* when blaming a new file;
|
||||
* -C -C -C enables copy from existing files for
|
||||
* everybody
|
||||
*/
|
||||
if (opt & PICKAXE_BLAME_COPY_HARDER)
|
||||
opt |= PICKAXE_BLAME_COPY_HARDEST;
|
||||
if (opt & PICKAXE_BLAME_COPY)
|
||||
opt |= PICKAXE_BLAME_COPY_HARDER;
|
||||
opt |= PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE;
|
||||
blame_copy_score = parse_score(arg+2);
|
||||
|
||||
if (!strcmp(ctx.argv[0], "--reverse")) {
|
||||
ctx.argv[0] = "--children";
|
||||
reverse = 1;
|
||||
}
|
||||
else if (!prefixcmp(arg, "-L")) {
|
||||
if (!arg[2]) {
|
||||
if (++i >= argc)
|
||||
usage(blame_usage);
|
||||
arg = argv[i];
|
||||
}
|
||||
else
|
||||
arg += 2;
|
||||
if (bottomtop)
|
||||
die("More than one '-L n,m' option given");
|
||||
bottomtop = arg;
|
||||
}
|
||||
else if (!strcmp("--contents", arg)) {
|
||||
if (++i >= argc)
|
||||
usage(blame_usage);
|
||||
contents_from = argv[i];
|
||||
}
|
||||
else if (!strcmp("--incremental", arg))
|
||||
incremental = 1;
|
||||
else if (!strcmp("--score-debug", arg))
|
||||
output_option |= OUTPUT_SHOW_SCORE;
|
||||
else if (!strcmp("-f", arg) ||
|
||||
!strcmp("--show-name", arg))
|
||||
output_option |= OUTPUT_SHOW_NAME;
|
||||
else if (!strcmp("-n", arg) ||
|
||||
!strcmp("--show-number", arg))
|
||||
output_option |= OUTPUT_SHOW_NUMBER;
|
||||
else if (!strcmp("-p", arg) ||
|
||||
!strcmp("--porcelain", arg))
|
||||
output_option |= OUTPUT_PORCELAIN;
|
||||
else if (!strcmp("--", arg)) {
|
||||
seen_dashdash = 1;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
argv[unk++] = arg;
|
||||
parse_revision_opt(&revs, &ctx, options, blame_opt_usage);
|
||||
}
|
||||
parse_done:
|
||||
argc = parse_options_end(&ctx);
|
||||
|
||||
if (!blame_move_score)
|
||||
blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
|
||||
@ -2238,115 +2335,59 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||
*
|
||||
* The remaining are:
|
||||
*
|
||||
* (1) if seen_dashdash, its either
|
||||
* "-options -- <path>" or
|
||||
* "-options -- <path> <rev>".
|
||||
* but the latter is allowed only if there is no
|
||||
* options that we passed to revision machinery.
|
||||
* (1) if dashdash_pos != 0, its either
|
||||
* "blame [revisions] -- <path>" or
|
||||
* "blame -- <path> <rev>"
|
||||
*
|
||||
* (2) otherwise, we may have "--" somewhere later and
|
||||
* might be looking at the first one of multiple 'rev'
|
||||
* parameters (e.g. " master ^next ^maint -- path").
|
||||
* See if there is a dashdash first, and give the
|
||||
* arguments before that to revision machinery.
|
||||
* After that there must be one 'path'.
|
||||
* (2) otherwise, its one of the two:
|
||||
* "blame [revisions] <path>"
|
||||
* "blame <path> <rev>"
|
||||
*
|
||||
* (3) otherwise, its one of the three:
|
||||
* "-options <path> <rev>"
|
||||
* "-options <rev> <path>"
|
||||
* "-options <path>"
|
||||
* but again the first one is allowed only if
|
||||
* there is no options that we passed to revision
|
||||
* machinery.
|
||||
* Note that we must strip out <path> from the arguments: we do not
|
||||
* want the path pruning but we may want "bottom" processing.
|
||||
*/
|
||||
|
||||
if (seen_dashdash) {
|
||||
/* (1) */
|
||||
if (argc <= i)
|
||||
usage(blame_usage);
|
||||
path = add_prefix(prefix, argv[i]);
|
||||
if (i + 1 == argc - 1) {
|
||||
if (unk != 1)
|
||||
usage(blame_usage);
|
||||
argv[unk++] = argv[i + 1];
|
||||
if (dashdash_pos) {
|
||||
switch (argc - dashdash_pos - 1) {
|
||||
case 2: /* (1b) */
|
||||
if (argc != 4)
|
||||
usage_with_options(blame_opt_usage, options);
|
||||
/* reorder for the new way: <rev> -- <path> */
|
||||
argv[1] = argv[3];
|
||||
argv[3] = argv[2];
|
||||
argv[2] = "--";
|
||||
/* FALLTHROUGH */
|
||||
case 1: /* (1a) */
|
||||
path = add_prefix(prefix, argv[--argc]);
|
||||
argv[argc] = NULL;
|
||||
break;
|
||||
default:
|
||||
usage_with_options(blame_opt_usage, options);
|
||||
}
|
||||
else if (i + 1 != argc)
|
||||
/* garbage at end */
|
||||
usage(blame_usage);
|
||||
}
|
||||
else {
|
||||
int j;
|
||||
for (j = i; !seen_dashdash && j < argc; j++)
|
||||
if (!strcmp(argv[j], "--"))
|
||||
seen_dashdash = j;
|
||||
if (seen_dashdash) {
|
||||
/* (2) */
|
||||
if (seen_dashdash + 1 != argc - 1)
|
||||
usage(blame_usage);
|
||||
path = add_prefix(prefix, argv[seen_dashdash + 1]);
|
||||
for (j = i; j < seen_dashdash; j++)
|
||||
argv[unk++] = argv[j];
|
||||
} else {
|
||||
if (argc < 2)
|
||||
usage_with_options(blame_opt_usage, options);
|
||||
path = add_prefix(prefix, argv[argc - 1]);
|
||||
if (argc == 3 && !has_path_in_work_tree(path)) { /* (2b) */
|
||||
path = add_prefix(prefix, argv[1]);
|
||||
argv[1] = argv[2];
|
||||
}
|
||||
else {
|
||||
/* (3) */
|
||||
if (argc <= i)
|
||||
usage(blame_usage);
|
||||
path = add_prefix(prefix, argv[i]);
|
||||
if (i + 1 == argc - 1) {
|
||||
final_commit_name = argv[i + 1];
|
||||
argv[argc - 1] = "--";
|
||||
|
||||
/* if (unk == 1) we could be getting
|
||||
* old-style
|
||||
*/
|
||||
if (unk == 1 && !has_path_in_work_tree(path)) {
|
||||
path = add_prefix(prefix, argv[i + 1]);
|
||||
final_commit_name = argv[i];
|
||||
}
|
||||
}
|
||||
else if (i != argc - 1)
|
||||
usage(blame_usage); /* garbage at end */
|
||||
|
||||
setup_work_tree();
|
||||
if (!has_path_in_work_tree(path))
|
||||
die("cannot stat path %s: %s",
|
||||
path, strerror(errno));
|
||||
}
|
||||
setup_work_tree();
|
||||
if (!has_path_in_work_tree(path))
|
||||
die("cannot stat path %s: %s", path, strerror(errno));
|
||||
}
|
||||
|
||||
if (final_commit_name)
|
||||
argv[unk++] = final_commit_name;
|
||||
|
||||
/*
|
||||
* Now we got rev and path. We do not want the path pruning
|
||||
* but we may want "bottom" processing.
|
||||
*/
|
||||
argv[unk++] = "--"; /* terminate the rev name */
|
||||
argv[unk] = NULL;
|
||||
|
||||
init_revisions(&revs, NULL);
|
||||
setup_revisions(unk, argv, &revs, NULL);
|
||||
setup_revisions(argc, argv, &revs, NULL);
|
||||
memset(&sb, 0, sizeof(sb));
|
||||
|
||||
/*
|
||||
* There must be one and only one positive commit in the
|
||||
* revs->pending array.
|
||||
*/
|
||||
for (i = 0; i < revs.pending.nr; i++) {
|
||||
struct object *obj = revs.pending.objects[i].item;
|
||||
if (obj->flags & UNINTERESTING)
|
||||
continue;
|
||||
while (obj->type == OBJ_TAG)
|
||||
obj = deref_tag(obj, NULL, 0);
|
||||
if (obj->type != OBJ_COMMIT)
|
||||
die("Non commit %s?",
|
||||
revs.pending.objects[i].name);
|
||||
if (sb.final)
|
||||
die("More than one commit to dig from %s and %s?",
|
||||
revs.pending.objects[i].name,
|
||||
final_commit_name);
|
||||
sb.final = (struct commit *) obj;
|
||||
final_commit_name = revs.pending.objects[i].name;
|
||||
}
|
||||
sb.revs = &revs;
|
||||
if (!reverse)
|
||||
final_commit_name = prepare_final(&sb);
|
||||
else if (contents_from)
|
||||
die("--contents and --children do not blend well.");
|
||||
else
|
||||
final_commit_name = prepare_initial(&sb);
|
||||
|
||||
if (!sb.final) {
|
||||
/*
|
||||
@ -2425,7 +2466,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
||||
if (!incremental)
|
||||
setup_pager();
|
||||
|
||||
assign_blame(&sb, &revs, opt);
|
||||
assign_blame(&sb, opt);
|
||||
|
||||
if (incremental)
|
||||
return 0;
|
||||
|
@ -37,6 +37,7 @@ static const char rev_list_usage[] =
|
||||
" --reverse\n"
|
||||
" formatting output:\n"
|
||||
" --parents\n"
|
||||
" --children\n"
|
||||
" --objects | --objects-edge\n"
|
||||
" --unpacked\n"
|
||||
" --header | --pretty\n"
|
||||
@ -90,6 +91,15 @@ static void show_commit(struct commit *commit)
|
||||
parents = parents->next;
|
||||
}
|
||||
}
|
||||
if (revs.children.name) {
|
||||
struct commit_list *children;
|
||||
|
||||
children = lookup_decoration(&revs.children, &commit->object);
|
||||
while (children) {
|
||||
printf(" %s", sha1_to_hex(children->item->object.sha1));
|
||||
children = children->next;
|
||||
}
|
||||
}
|
||||
show_decorations(commit);
|
||||
if (revs.commit_format == CMIT_FMT_ONELINE)
|
||||
putchar(' ');
|
||||
|
@ -7,9 +7,14 @@
|
||||
#include "utf8.h"
|
||||
#include "mailmap.h"
|
||||
#include "shortlog.h"
|
||||
#include "parse-options.h"
|
||||
|
||||
static const char shortlog_usage[] =
|
||||
"git-shortlog [-n] [-s] [-e] [-w] [<commit-id>... ]";
|
||||
static char const * const shortlog_usage[] = {
|
||||
"git-shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [<commit-id>... ]",
|
||||
"",
|
||||
"[rev-opts] are documented in git-rev-list(1)",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int compare_by_number(const void *a1, const void *a2)
|
||||
{
|
||||
@ -164,21 +169,19 @@ static void get_from_rev(struct rev_info *rev, struct shortlog *log)
|
||||
shortlog_add_commit(log, commit);
|
||||
}
|
||||
|
||||
static int parse_uint(char const **arg, int comma)
|
||||
static int parse_uint(char const **arg, int comma, int defval)
|
||||
{
|
||||
unsigned long ul;
|
||||
int ret;
|
||||
char *endp;
|
||||
|
||||
ul = strtoul(*arg, &endp, 10);
|
||||
if (endp != *arg && *endp && *endp != comma)
|
||||
if (*endp && *endp != comma)
|
||||
return -1;
|
||||
ret = (int) ul;
|
||||
if (ret != ul)
|
||||
if (ul > INT_MAX)
|
||||
return -1;
|
||||
*arg = endp;
|
||||
if (**arg)
|
||||
(*arg)++;
|
||||
ret = *arg == endp ? defval : (int)ul;
|
||||
*arg = *endp ? endp + 1 : endp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -187,30 +190,30 @@ static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
|
||||
#define DEFAULT_INDENT1 6
|
||||
#define DEFAULT_INDENT2 9
|
||||
|
||||
static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
|
||||
static int parse_wrap_args(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
arg += 2; /* skip -w */
|
||||
struct shortlog *log = opt->value;
|
||||
|
||||
*wrap = parse_uint(&arg, ',');
|
||||
if (*wrap < 0)
|
||||
die(wrap_arg_usage);
|
||||
*in1 = parse_uint(&arg, ',');
|
||||
if (*in1 < 0)
|
||||
die(wrap_arg_usage);
|
||||
*in2 = parse_uint(&arg, '\0');
|
||||
if (*in2 < 0)
|
||||
die(wrap_arg_usage);
|
||||
log->wrap_lines = !unset;
|
||||
if (unset)
|
||||
return 0;
|
||||
if (!arg) {
|
||||
log->wrap = DEFAULT_WRAPLEN;
|
||||
log->in1 = DEFAULT_INDENT1;
|
||||
log->in2 = DEFAULT_INDENT2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!*wrap)
|
||||
*wrap = DEFAULT_WRAPLEN;
|
||||
if (!*in1)
|
||||
*in1 = DEFAULT_INDENT1;
|
||||
if (!*in2)
|
||||
*in2 = DEFAULT_INDENT2;
|
||||
if (*wrap &&
|
||||
((*in1 && *wrap <= *in1) ||
|
||||
(*in2 && *wrap <= *in2)))
|
||||
die(wrap_arg_usage);
|
||||
log->wrap = parse_uint(&arg, ',', DEFAULT_WRAPLEN);
|
||||
log->in1 = parse_uint(&arg, ',', DEFAULT_INDENT1);
|
||||
log->in2 = parse_uint(&arg, '\0', DEFAULT_INDENT2);
|
||||
if (log->wrap < 0 || log->in1 < 0 || log->in2 < 0)
|
||||
return error(wrap_arg_usage);
|
||||
if (log->wrap &&
|
||||
((log->in1 && log->wrap <= log->in1) ||
|
||||
(log->in2 && log->wrap <= log->in2)))
|
||||
return error(wrap_arg_usage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void shortlog_init(struct shortlog *log)
|
||||
@ -227,38 +230,46 @@ void shortlog_init(struct shortlog *log)
|
||||
|
||||
int cmd_shortlog(int argc, const char **argv, const char *prefix)
|
||||
{
|
||||
struct shortlog log;
|
||||
struct rev_info rev;
|
||||
static struct shortlog log;
|
||||
static struct rev_info rev;
|
||||
int nongit;
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
|
||||
"sort output according to the number of commits per author"),
|
||||
OPT_BOOLEAN('s', "summary", &log.summary,
|
||||
"Suppress commit descriptions, only provides commit count"),
|
||||
OPT_BOOLEAN('e', "email", &log.email,
|
||||
"Show the email address of each author"),
|
||||
{ OPTION_CALLBACK, 'w', NULL, &log, "w[,i1[,i2]]",
|
||||
"Linewrap output", PARSE_OPT_OPTARG, &parse_wrap_args },
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
struct parse_opt_ctx_t ctx;
|
||||
|
||||
prefix = setup_git_directory_gently(&nongit);
|
||||
shortlog_init(&log);
|
||||
|
||||
/* since -n is a shadowed rev argument, parse our args first */
|
||||
while (argc > 1) {
|
||||
if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
|
||||
log.sort_by_number = 1;
|
||||
else if (!strcmp(argv[1], "-s") ||
|
||||
!strcmp(argv[1], "--summary"))
|
||||
log.summary = 1;
|
||||
else if (!strcmp(argv[1], "-e") ||
|
||||
!strcmp(argv[1], "--email"))
|
||||
log.email = 1;
|
||||
else if (!prefixcmp(argv[1], "-w")) {
|
||||
log.wrap_lines = 1;
|
||||
parse_wrap_args(argv[1], &log.in1, &log.in2, &log.wrap);
|
||||
}
|
||||
else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
|
||||
usage(shortlog_usage);
|
||||
else
|
||||
break;
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
init_revisions(&rev, prefix);
|
||||
argc = setup_revisions(argc, argv, &rev, NULL);
|
||||
if (argc > 1)
|
||||
die ("unrecognized argument: %s", argv[1]);
|
||||
parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
|
||||
PARSE_OPT_KEEP_ARGV0);
|
||||
|
||||
for (;;) {
|
||||
switch (parse_options_step(&ctx, options, shortlog_usage)) {
|
||||
case PARSE_OPT_HELP:
|
||||
exit(129);
|
||||
case PARSE_OPT_DONE:
|
||||
goto parse_done;
|
||||
}
|
||||
parse_revision_opt(&rev, &ctx, options, shortlog_usage);
|
||||
}
|
||||
parse_done:
|
||||
argc = parse_options_end(&ctx);
|
||||
|
||||
if (setup_revisions(argc, argv, &rev, NULL) != 1) {
|
||||
error("unrecognized argument: %s", argv[1]);
|
||||
usage_with_options(shortlog_usage, options);
|
||||
}
|
||||
|
||||
/* assume HEAD if from a tty */
|
||||
if (!nongit && !rev.pending.nr && isatty(0))
|
||||
|
165
parse-options.c
165
parse-options.c
@ -1,17 +1,11 @@
|
||||
#include "git-compat-util.h"
|
||||
#include "parse-options.h"
|
||||
#include "cache.h"
|
||||
|
||||
#define OPT_SHORT 1
|
||||
#define OPT_UNSET 2
|
||||
|
||||
struct optparse_t {
|
||||
const char **argv;
|
||||
const char **out;
|
||||
int argc, cpidx;
|
||||
const char *opt;
|
||||
};
|
||||
|
||||
static inline const char *get_arg(struct optparse_t *p)
|
||||
static inline const char *get_arg(struct parse_opt_ctx_t *p)
|
||||
{
|
||||
if (p->opt) {
|
||||
const char *res = p->opt;
|
||||
@ -37,7 +31,7 @@ static int opterror(const struct option *opt, const char *reason, int flags)
|
||||
return error("option `%s' %s", opt->long_name, reason);
|
||||
}
|
||||
|
||||
static int get_value(struct optparse_t *p,
|
||||
static int get_value(struct parse_opt_ctx_t *p,
|
||||
const struct option *opt, int flags)
|
||||
{
|
||||
const char *s, *arg;
|
||||
@ -101,14 +95,14 @@ static int get_value(struct optparse_t *p,
|
||||
|
||||
case OPTION_CALLBACK:
|
||||
if (unset)
|
||||
return (*opt->callback)(opt, NULL, 1);
|
||||
return (*opt->callback)(opt, NULL, 1) ? (-1) : 0;
|
||||
if (opt->flags & PARSE_OPT_NOARG)
|
||||
return (*opt->callback)(opt, NULL, 0);
|
||||
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
|
||||
if (opt->flags & PARSE_OPT_OPTARG && !p->opt)
|
||||
return (*opt->callback)(opt, NULL, 0);
|
||||
return (*opt->callback)(opt, NULL, 0) ? (-1) : 0;
|
||||
if (!arg)
|
||||
return opterror(opt, "requires a value", flags);
|
||||
return (*opt->callback)(opt, get_arg(p), 0);
|
||||
return (*opt->callback)(opt, get_arg(p), 0) ? (-1) : 0;
|
||||
|
||||
case OPTION_INTEGER:
|
||||
if (unset) {
|
||||
@ -131,7 +125,7 @@ static int get_value(struct optparse_t *p,
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_short_opt(struct optparse_t *p, const struct option *options)
|
||||
static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *options)
|
||||
{
|
||||
for (; options->type != OPTION_END; options++) {
|
||||
if (options->short_name == *p->opt) {
|
||||
@ -139,10 +133,10 @@ static int parse_short_opt(struct optparse_t *p, const struct option *options)
|
||||
return get_value(p, options, OPT_SHORT);
|
||||
}
|
||||
}
|
||||
return error("unknown switch `%c'", *p->opt);
|
||||
return -2;
|
||||
}
|
||||
|
||||
static int parse_long_opt(struct optparse_t *p, const char *arg,
|
||||
static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg,
|
||||
const struct option *options)
|
||||
{
|
||||
const char *arg_end = strchr(arg, '=');
|
||||
@ -224,7 +218,7 @@ is_abbreviated:
|
||||
abbrev_option->long_name);
|
||||
if (abbrev_option)
|
||||
return get_value(p, abbrev_option, abbrev_flags);
|
||||
return error("unknown option `%s'", arg);
|
||||
return -2;
|
||||
}
|
||||
|
||||
void check_typos(const char *arg, const struct option *options)
|
||||
@ -247,67 +241,126 @@ void check_typos(const char *arg, const struct option *options)
|
||||
}
|
||||
}
|
||||
|
||||
static NORETURN void usage_with_options_internal(const char * const *,
|
||||
const struct option *, int);
|
||||
|
||||
int parse_options(int argc, const char **argv, const struct option *options,
|
||||
const char * const usagestr[], int flags)
|
||||
void parse_options_start(struct parse_opt_ctx_t *ctx,
|
||||
int argc, const char **argv, int flags)
|
||||
{
|
||||
struct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL };
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->argc = argc - 1;
|
||||
ctx->argv = argv + 1;
|
||||
ctx->out = argv;
|
||||
ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0);
|
||||
ctx->flags = flags;
|
||||
}
|
||||
|
||||
for (; args.argc; args.argc--, args.argv++) {
|
||||
const char *arg = args.argv[0];
|
||||
static int usage_with_options_internal(const char * const *,
|
||||
const struct option *, int);
|
||||
|
||||
int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[])
|
||||
{
|
||||
/* we must reset ->opt, unknown short option leave it dangling */
|
||||
ctx->opt = NULL;
|
||||
|
||||
for (; ctx->argc; ctx->argc--, ctx->argv++) {
|
||||
const char *arg = ctx->argv[0];
|
||||
|
||||
if (*arg != '-' || !arg[1]) {
|
||||
if (flags & PARSE_OPT_STOP_AT_NON_OPTION)
|
||||
if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION)
|
||||
break;
|
||||
args.out[args.cpidx++] = args.argv[0];
|
||||
ctx->out[ctx->cpidx++] = ctx->argv[0];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg[1] != '-') {
|
||||
args.opt = arg + 1;
|
||||
if (*args.opt == 'h')
|
||||
usage_with_options(usagestr, options);
|
||||
if (parse_short_opt(&args, options) < 0)
|
||||
usage_with_options(usagestr, options);
|
||||
if (args.opt)
|
||||
ctx->opt = arg + 1;
|
||||
if (*ctx->opt == 'h')
|
||||
return parse_options_usage(usagestr, options);
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
case -2:
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
}
|
||||
if (ctx->opt)
|
||||
check_typos(arg + 1, options);
|
||||
while (args.opt) {
|
||||
if (*args.opt == 'h')
|
||||
usage_with_options(usagestr, options);
|
||||
if (parse_short_opt(&args, options) < 0)
|
||||
usage_with_options(usagestr, options);
|
||||
while (ctx->opt) {
|
||||
if (*ctx->opt == 'h')
|
||||
return parse_options_usage(usagestr, options);
|
||||
switch (parse_short_opt(ctx, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
case -2:
|
||||
/* fake a short option thing to hide the fact that we may have
|
||||
* started to parse aggregated stuff
|
||||
*
|
||||
* This is leaky, too bad.
|
||||
*/
|
||||
ctx->argv[0] = xstrdup(ctx->opt - 1);
|
||||
*(char *)ctx->argv[0] = '-';
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!arg[2]) { /* "--" */
|
||||
if (!(flags & PARSE_OPT_KEEP_DASHDASH)) {
|
||||
args.argc--;
|
||||
args.argv++;
|
||||
if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
|
||||
ctx->argc--;
|
||||
ctx->argv++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcmp(arg + 2, "help-all"))
|
||||
usage_with_options_internal(usagestr, options, 1);
|
||||
return usage_with_options_internal(usagestr, options, 1);
|
||||
if (!strcmp(arg + 2, "help"))
|
||||
usage_with_options(usagestr, options);
|
||||
if (parse_long_opt(&args, arg + 2, options))
|
||||
usage_with_options(usagestr, options);
|
||||
return parse_options_usage(usagestr, options);
|
||||
switch (parse_long_opt(ctx, arg + 2, options)) {
|
||||
case -1:
|
||||
return parse_options_usage(usagestr, options);
|
||||
case -2:
|
||||
return PARSE_OPT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
return PARSE_OPT_DONE;
|
||||
}
|
||||
|
||||
int parse_options_end(struct parse_opt_ctx_t *ctx)
|
||||
{
|
||||
memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
|
||||
ctx->out[ctx->cpidx + ctx->argc] = NULL;
|
||||
return ctx->cpidx + ctx->argc;
|
||||
}
|
||||
|
||||
int parse_options(int argc, const char **argv, const struct option *options,
|
||||
const char * const usagestr[], int flags)
|
||||
{
|
||||
struct parse_opt_ctx_t ctx;
|
||||
|
||||
parse_options_start(&ctx, argc, argv, flags);
|
||||
switch (parse_options_step(&ctx, options, usagestr)) {
|
||||
case PARSE_OPT_HELP:
|
||||
exit(129);
|
||||
case PARSE_OPT_DONE:
|
||||
break;
|
||||
default: /* PARSE_OPT_UNKNOWN */
|
||||
if (ctx.argv[0][1] == '-') {
|
||||
error("unknown option `%s'", ctx.argv[0] + 2);
|
||||
} else {
|
||||
error("unknown switch `%c'", *ctx.opt);
|
||||
}
|
||||
usage_with_options(usagestr, options);
|
||||
}
|
||||
|
||||
memmove(args.out + args.cpidx, args.argv, args.argc * sizeof(*args.out));
|
||||
args.out[args.cpidx + args.argc] = NULL;
|
||||
return args.cpidx + args.argc;
|
||||
return parse_options_end(&ctx);
|
||||
}
|
||||
|
||||
#define USAGE_OPTS_WIDTH 24
|
||||
#define USAGE_GAP 2
|
||||
|
||||
void usage_with_options_internal(const char * const *usagestr,
|
||||
const struct option *opts, int full)
|
||||
int usage_with_options_internal(const char * const *usagestr,
|
||||
const struct option *opts, int full)
|
||||
{
|
||||
fprintf(stderr, "usage: %s\n", *usagestr++);
|
||||
while (*usagestr && **usagestr)
|
||||
@ -392,15 +445,23 @@ void usage_with_options_internal(const char * const *usagestr,
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
|
||||
exit(129);
|
||||
return PARSE_OPT_HELP;
|
||||
}
|
||||
|
||||
void usage_with_options(const char * const *usagestr,
|
||||
const struct option *opts)
|
||||
const struct option *opts)
|
||||
{
|
||||
usage_with_options_internal(usagestr, opts, 0);
|
||||
exit(129);
|
||||
}
|
||||
|
||||
int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts)
|
||||
{
|
||||
return usage_with_options_internal(usagestr, opts, 0);
|
||||
}
|
||||
|
||||
|
||||
/*----- some often used options -----*/
|
||||
#include "cache.h"
|
||||
|
||||
|
@ -20,6 +20,7 @@ enum parse_opt_type {
|
||||
enum parse_opt_flags {
|
||||
PARSE_OPT_KEEP_DASHDASH = 1,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION = 2,
|
||||
PARSE_OPT_KEEP_ARGV0 = 4,
|
||||
};
|
||||
|
||||
enum parse_opt_option_flags {
|
||||
@ -111,6 +112,40 @@ extern int parse_options(int argc, const char **argv,
|
||||
extern NORETURN void usage_with_options(const char * const *usagestr,
|
||||
const struct option *options);
|
||||
|
||||
/*----- incremantal advanced APIs -----*/
|
||||
|
||||
enum {
|
||||
PARSE_OPT_HELP = -1,
|
||||
PARSE_OPT_DONE,
|
||||
PARSE_OPT_UNKNOWN,
|
||||
};
|
||||
|
||||
/*
|
||||
* It's okay for the caller to consume argv/argc in the usual way.
|
||||
* Other fields of that structure are private to parse-options and should not
|
||||
* be modified in any way.
|
||||
*/
|
||||
struct parse_opt_ctx_t {
|
||||
const char **argv;
|
||||
const char **out;
|
||||
int argc, cpidx;
|
||||
const char *opt;
|
||||
int flags;
|
||||
};
|
||||
|
||||
extern int parse_options_usage(const char * const *usagestr,
|
||||
const struct option *opts);
|
||||
|
||||
extern void parse_options_start(struct parse_opt_ctx_t *ctx,
|
||||
int argc, const char **argv, int flags);
|
||||
|
||||
extern int parse_options_step(struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[]);
|
||||
|
||||
extern int parse_options_end(struct parse_opt_ctx_t *ctx);
|
||||
|
||||
|
||||
/*----- some often used options -----*/
|
||||
extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
|
||||
extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
|
||||
|
591
revision.c
591
revision.c
@ -10,6 +10,7 @@
|
||||
#include "grep.h"
|
||||
#include "reflog-walk.h"
|
||||
#include "patch-ids.h"
|
||||
#include "decorate.h"
|
||||
|
||||
volatile show_early_output_fn_t show_early_output;
|
||||
|
||||
@ -973,6 +974,226 @@ static void add_ignore_packed(struct rev_info *revs, const char *name)
|
||||
revs->ignore_packed[num] = NULL;
|
||||
}
|
||||
|
||||
static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv,
|
||||
int *unkc, const char **unkv)
|
||||
{
|
||||
const char *arg = argv[0];
|
||||
|
||||
/* pseudo revision arguments */
|
||||
if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
|
||||
!strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
|
||||
!strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
|
||||
!strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk"))
|
||||
{
|
||||
unkv[(*unkc)++] = arg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!prefixcmp(arg, "--max-count=")) {
|
||||
revs->max_count = atoi(arg + 12);
|
||||
} else if (!prefixcmp(arg, "--skip=")) {
|
||||
revs->skip_count = atoi(arg + 7);
|
||||
} else if ((*arg == '-') && isdigit(arg[1])) {
|
||||
/* accept -<digit>, like traditional "head" */
|
||||
revs->max_count = atoi(arg + 1);
|
||||
} else if (!strcmp(arg, "-n")) {
|
||||
if (argc <= 1)
|
||||
return error("-n requires an argument");
|
||||
revs->max_count = atoi(argv[1]);
|
||||
return 2;
|
||||
} else if (!prefixcmp(arg, "-n")) {
|
||||
revs->max_count = atoi(arg + 2);
|
||||
} else if (!prefixcmp(arg, "--max-age=")) {
|
||||
revs->max_age = atoi(arg + 10);
|
||||
} else if (!prefixcmp(arg, "--since=")) {
|
||||
revs->max_age = approxidate(arg + 8);
|
||||
} else if (!prefixcmp(arg, "--after=")) {
|
||||
revs->max_age = approxidate(arg + 8);
|
||||
} else if (!prefixcmp(arg, "--min-age=")) {
|
||||
revs->min_age = atoi(arg + 10);
|
||||
} else if (!prefixcmp(arg, "--before=")) {
|
||||
revs->min_age = approxidate(arg + 9);
|
||||
} else if (!prefixcmp(arg, "--until=")) {
|
||||
revs->min_age = approxidate(arg + 8);
|
||||
} else if (!strcmp(arg, "--first-parent")) {
|
||||
revs->first_parent_only = 1;
|
||||
} else if (!strcmp(arg, "-g") || !strcmp(arg, "--walk-reflogs")) {
|
||||
init_reflog_walk(&revs->reflog_info);
|
||||
} else if (!strcmp(arg, "--default")) {
|
||||
if (argc <= 1)
|
||||
return error("bad --default argument");
|
||||
revs->def = argv[1];
|
||||
return 2;
|
||||
} else if (!strcmp(arg, "--merge")) {
|
||||
revs->show_merge = 1;
|
||||
} else if (!strcmp(arg, "--topo-order")) {
|
||||
revs->lifo = 1;
|
||||
revs->topo_order = 1;
|
||||
} else if (!strcmp(arg, "--date-order")) {
|
||||
revs->lifo = 0;
|
||||
revs->topo_order = 1;
|
||||
} else if (!prefixcmp(arg, "--early-output")) {
|
||||
int count = 100;
|
||||
switch (arg[14]) {
|
||||
case '=':
|
||||
count = atoi(arg+15);
|
||||
/* Fallthrough */
|
||||
case 0:
|
||||
revs->topo_order = 1;
|
||||
revs->early_output = count;
|
||||
}
|
||||
} else if (!strcmp(arg, "--parents")) {
|
||||
revs->rewrite_parents = 1;
|
||||
revs->print_parents = 1;
|
||||
} else if (!strcmp(arg, "--dense")) {
|
||||
revs->dense = 1;
|
||||
} else if (!strcmp(arg, "--sparse")) {
|
||||
revs->dense = 0;
|
||||
} else if (!strcmp(arg, "--show-all")) {
|
||||
revs->show_all = 1;
|
||||
} else if (!strcmp(arg, "--remove-empty")) {
|
||||
revs->remove_empty_trees = 1;
|
||||
} else if (!strcmp(arg, "--no-merges")) {
|
||||
revs->no_merges = 1;
|
||||
} else if (!strcmp(arg, "--boundary")) {
|
||||
revs->boundary = 1;
|
||||
} else if (!strcmp(arg, "--left-right")) {
|
||||
revs->left_right = 1;
|
||||
} else if (!strcmp(arg, "--cherry-pick")) {
|
||||
revs->cherry_pick = 1;
|
||||
revs->limited = 1;
|
||||
} else if (!strcmp(arg, "--objects")) {
|
||||
revs->tag_objects = 1;
|
||||
revs->tree_objects = 1;
|
||||
revs->blob_objects = 1;
|
||||
} else if (!strcmp(arg, "--objects-edge")) {
|
||||
revs->tag_objects = 1;
|
||||
revs->tree_objects = 1;
|
||||
revs->blob_objects = 1;
|
||||
revs->edge_hint = 1;
|
||||
} else if (!strcmp(arg, "--unpacked")) {
|
||||
revs->unpacked = 1;
|
||||
free(revs->ignore_packed);
|
||||
revs->ignore_packed = NULL;
|
||||
revs->num_ignore_packed = 0;
|
||||
} else if (!prefixcmp(arg, "--unpacked=")) {
|
||||
revs->unpacked = 1;
|
||||
add_ignore_packed(revs, arg+11);
|
||||
} else if (!strcmp(arg, "-r")) {
|
||||
revs->diff = 1;
|
||||
DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
|
||||
} else if (!strcmp(arg, "-t")) {
|
||||
revs->diff = 1;
|
||||
DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
|
||||
DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
|
||||
} else if (!strcmp(arg, "-m")) {
|
||||
revs->ignore_merges = 0;
|
||||
} else if (!strcmp(arg, "-c")) {
|
||||
revs->diff = 1;
|
||||
revs->dense_combined_merges = 0;
|
||||
revs->combine_merges = 1;
|
||||
} else if (!strcmp(arg, "--cc")) {
|
||||
revs->diff = 1;
|
||||
revs->dense_combined_merges = 1;
|
||||
revs->combine_merges = 1;
|
||||
} else if (!strcmp(arg, "-v")) {
|
||||
revs->verbose_header = 1;
|
||||
} else if (!strcmp(arg, "--pretty")) {
|
||||
revs->verbose_header = 1;
|
||||
get_commit_format(arg+8, revs);
|
||||
} else if (!prefixcmp(arg, "--pretty=")) {
|
||||
revs->verbose_header = 1;
|
||||
get_commit_format(arg+9, revs);
|
||||
} else if (!strcmp(arg, "--graph")) {
|
||||
revs->topo_order = 1;
|
||||
revs->rewrite_parents = 1;
|
||||
revs->graph = graph_init(revs);
|
||||
} else if (!strcmp(arg, "--root")) {
|
||||
revs->show_root_diff = 1;
|
||||
} else if (!strcmp(arg, "--no-commit-id")) {
|
||||
revs->no_commit_id = 1;
|
||||
} else if (!strcmp(arg, "--always")) {
|
||||
revs->always_show_header = 1;
|
||||
} else if (!strcmp(arg, "--no-abbrev")) {
|
||||
revs->abbrev = 0;
|
||||
} else if (!strcmp(arg, "--abbrev")) {
|
||||
revs->abbrev = DEFAULT_ABBREV;
|
||||
} else if (!prefixcmp(arg, "--abbrev=")) {
|
||||
revs->abbrev = strtoul(arg + 9, NULL, 10);
|
||||
if (revs->abbrev < MINIMUM_ABBREV)
|
||||
revs->abbrev = MINIMUM_ABBREV;
|
||||
else if (revs->abbrev > 40)
|
||||
revs->abbrev = 40;
|
||||
} else if (!strcmp(arg, "--abbrev-commit")) {
|
||||
revs->abbrev_commit = 1;
|
||||
} else if (!strcmp(arg, "--full-diff")) {
|
||||
revs->diff = 1;
|
||||
revs->full_diff = 1;
|
||||
} else if (!strcmp(arg, "--full-history")) {
|
||||
revs->simplify_history = 0;
|
||||
} else if (!strcmp(arg, "--relative-date")) {
|
||||
revs->date_mode = DATE_RELATIVE;
|
||||
} else if (!strncmp(arg, "--date=", 7)) {
|
||||
revs->date_mode = parse_date_format(arg + 7);
|
||||
} else if (!strcmp(arg, "--log-size")) {
|
||||
revs->show_log_size = 1;
|
||||
}
|
||||
/*
|
||||
* Grepping the commit log
|
||||
*/
|
||||
else if (!prefixcmp(arg, "--author=")) {
|
||||
add_header_grep(revs, "author", arg+9);
|
||||
} else if (!prefixcmp(arg, "--committer=")) {
|
||||
add_header_grep(revs, "committer", arg+12);
|
||||
} else if (!prefixcmp(arg, "--grep=")) {
|
||||
add_message_grep(revs, arg+7);
|
||||
} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
|
||||
if (revs->grep_filter)
|
||||
revs->grep_filter->regflags |= REG_EXTENDED;
|
||||
} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
|
||||
if (revs->grep_filter)
|
||||
revs->grep_filter->regflags |= REG_ICASE;
|
||||
} else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
|
||||
if (revs->grep_filter)
|
||||
revs->grep_filter->fixed = 1;
|
||||
} else if (!strcmp(arg, "--all-match")) {
|
||||
if (revs->grep_filter)
|
||||
revs->grep_filter->all_match = 1;
|
||||
} else if (!prefixcmp(arg, "--encoding=")) {
|
||||
arg += 11;
|
||||
if (strcmp(arg, "none"))
|
||||
git_log_output_encoding = xstrdup(arg);
|
||||
else
|
||||
git_log_output_encoding = "";
|
||||
} else if (!strcmp(arg, "--reverse")) {
|
||||
revs->reverse ^= 1;
|
||||
} else if (!strcmp(arg, "--children")) {
|
||||
revs->children.name = "children";
|
||||
revs->limited = 1;
|
||||
} else {
|
||||
int opts = diff_opt_parse(&revs->diffopt, argv, argc);
|
||||
if (!opts)
|
||||
unkv[(*unkc)++] = arg;
|
||||
return opts;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[])
|
||||
{
|
||||
int n = handle_revision_opt(revs, ctx->argc, ctx->argv,
|
||||
&ctx->cpidx, ctx->out);
|
||||
if (n <= 0) {
|
||||
error("unknown option `%s'", ctx->argv[0]);
|
||||
usage_with_options(usagestr, options);
|
||||
}
|
||||
ctx->argv += n;
|
||||
ctx->argc -= n;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse revision information, filling in the "rev_info" structure,
|
||||
* and removing the used arguments from the argument list.
|
||||
@ -982,12 +1203,7 @@ static void add_ignore_packed(struct rev_info *revs, const char *name)
|
||||
*/
|
||||
int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
|
||||
{
|
||||
int i, flags, seen_dashdash, show_merge;
|
||||
const char **unrecognized = argv + 1;
|
||||
int left = 1;
|
||||
int all_match = 0;
|
||||
int regflags = 0;
|
||||
int fixed = 0;
|
||||
int i, flags, left, seen_dashdash;
|
||||
|
||||
/* First, search for "--" */
|
||||
seen_dashdash = 0;
|
||||
@ -1003,58 +1219,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
break;
|
||||
}
|
||||
|
||||
flags = show_merge = 0;
|
||||
for (i = 1; i < argc; i++) {
|
||||
/* Second, deal with arguments and options */
|
||||
flags = 0;
|
||||
for (left = i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (*arg == '-') {
|
||||
int opts;
|
||||
if (!prefixcmp(arg, "--max-count=")) {
|
||||
revs->max_count = atoi(arg + 12);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--skip=")) {
|
||||
revs->skip_count = atoi(arg + 7);
|
||||
continue;
|
||||
}
|
||||
/* accept -<digit>, like traditional "head" */
|
||||
if ((*arg == '-') && isdigit(arg[1])) {
|
||||
revs->max_count = atoi(arg + 1);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-n")) {
|
||||
if (argc <= i + 1)
|
||||
die("-n requires an argument");
|
||||
revs->max_count = atoi(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "-n")) {
|
||||
revs->max_count = atoi(arg + 2);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--max-age=")) {
|
||||
revs->max_age = atoi(arg + 10);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--since=")) {
|
||||
revs->max_age = approxidate(arg + 8);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--after=")) {
|
||||
revs->max_age = approxidate(arg + 8);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--min-age=")) {
|
||||
revs->min_age = atoi(arg + 10);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--before=")) {
|
||||
revs->min_age = approxidate(arg + 9);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--until=")) {
|
||||
revs->min_age = approxidate(arg + 8);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(arg, "--all")) {
|
||||
handle_refs(revs, flags, for_each_ref);
|
||||
continue;
|
||||
@ -1071,265 +1242,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
handle_refs(revs, flags, for_each_remote_ref);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--first-parent")) {
|
||||
revs->first_parent_only = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--reflog")) {
|
||||
handle_reflog(revs, flags);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-g") ||
|
||||
!strcmp(arg, "--walk-reflogs")) {
|
||||
init_reflog_walk(&revs->reflog_info);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--not")) {
|
||||
flags ^= UNINTERESTING;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--default")) {
|
||||
if (++i >= argc)
|
||||
die("bad --default argument");
|
||||
def = argv[i];
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--merge")) {
|
||||
show_merge = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--topo-order")) {
|
||||
revs->lifo = 1;
|
||||
revs->topo_order = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--date-order")) {
|
||||
revs->lifo = 0;
|
||||
revs->topo_order = 1;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--early-output")) {
|
||||
int count = 100;
|
||||
switch (arg[14]) {
|
||||
case '=':
|
||||
count = atoi(arg+15);
|
||||
/* Fallthrough */
|
||||
case 0:
|
||||
revs->topo_order = 1;
|
||||
revs->early_output = count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!strcmp(arg, "--parents")) {
|
||||
revs->rewrite_parents = 1;
|
||||
revs->print_parents = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--dense")) {
|
||||
revs->dense = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--sparse")) {
|
||||
revs->dense = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--show-all")) {
|
||||
revs->show_all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--remove-empty")) {
|
||||
revs->remove_empty_trees = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-merges")) {
|
||||
revs->no_merges = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--boundary")) {
|
||||
revs->boundary = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--left-right")) {
|
||||
revs->left_right = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--cherry-pick")) {
|
||||
revs->cherry_pick = 1;
|
||||
revs->limited = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--objects")) {
|
||||
revs->tag_objects = 1;
|
||||
revs->tree_objects = 1;
|
||||
revs->blob_objects = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--objects-edge")) {
|
||||
revs->tag_objects = 1;
|
||||
revs->tree_objects = 1;
|
||||
revs->blob_objects = 1;
|
||||
revs->edge_hint = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--unpacked")) {
|
||||
revs->unpacked = 1;
|
||||
free(revs->ignore_packed);
|
||||
revs->ignore_packed = NULL;
|
||||
revs->num_ignore_packed = 0;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--unpacked=")) {
|
||||
revs->unpacked = 1;
|
||||
add_ignore_packed(revs, arg+11);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-r")) {
|
||||
revs->diff = 1;
|
||||
DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-t")) {
|
||||
revs->diff = 1;
|
||||
DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
|
||||
DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-m")) {
|
||||
revs->ignore_merges = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-c")) {
|
||||
revs->diff = 1;
|
||||
revs->dense_combined_merges = 0;
|
||||
revs->combine_merges = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--cc")) {
|
||||
revs->diff = 1;
|
||||
revs->dense_combined_merges = 1;
|
||||
revs->combine_merges = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-v")) {
|
||||
revs->verbose_header = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--pretty")) {
|
||||
revs->verbose_header = 1;
|
||||
get_commit_format(arg+8, revs);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--pretty=")) {
|
||||
revs->verbose_header = 1;
|
||||
get_commit_format(arg+9, revs);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--graph")) {
|
||||
revs->topo_order = 1;
|
||||
revs->rewrite_parents = 1;
|
||||
revs->graph = graph_init(revs);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--root")) {
|
||||
revs->show_root_diff = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-commit-id")) {
|
||||
revs->no_commit_id = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--always")) {
|
||||
revs->always_show_header = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-abbrev")) {
|
||||
revs->abbrev = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--abbrev")) {
|
||||
revs->abbrev = DEFAULT_ABBREV;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--abbrev=")) {
|
||||
revs->abbrev = strtoul(arg + 9, NULL, 10);
|
||||
if (revs->abbrev < MINIMUM_ABBREV)
|
||||
revs->abbrev = MINIMUM_ABBREV;
|
||||
else if (revs->abbrev > 40)
|
||||
revs->abbrev = 40;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--abbrev-commit")) {
|
||||
revs->abbrev_commit = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--full-diff")) {
|
||||
revs->diff = 1;
|
||||
revs->full_diff = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--full-history")) {
|
||||
revs->simplify_history = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--relative-date")) {
|
||||
revs->date_mode = DATE_RELATIVE;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(arg, "--date=", 7)) {
|
||||
revs->date_mode = parse_date_format(arg + 7);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--log-size")) {
|
||||
revs->show_log_size = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Grepping the commit log
|
||||
*/
|
||||
if (!prefixcmp(arg, "--author=")) {
|
||||
add_header_grep(revs, "author", arg+9);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--committer=")) {
|
||||
add_header_grep(revs, "committer", arg+12);
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--grep=")) {
|
||||
add_message_grep(revs, arg+7);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--extended-regexp") ||
|
||||
!strcmp(arg, "-E")) {
|
||||
regflags |= REG_EXTENDED;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--regexp-ignore-case") ||
|
||||
!strcmp(arg, "-i")) {
|
||||
regflags |= REG_ICASE;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--fixed-strings") ||
|
||||
!strcmp(arg, "-F")) {
|
||||
fixed = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--all-match")) {
|
||||
all_match = 1;
|
||||
continue;
|
||||
}
|
||||
if (!prefixcmp(arg, "--encoding=")) {
|
||||
arg += 11;
|
||||
if (strcmp(arg, "none"))
|
||||
git_log_output_encoding = xstrdup(arg);
|
||||
else
|
||||
git_log_output_encoding = "";
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--reverse")) {
|
||||
revs->reverse ^= 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-walk")) {
|
||||
revs->no_walk = 1;
|
||||
continue;
|
||||
@ -1339,13 +1259,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
continue;
|
||||
}
|
||||
|
||||
opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
|
||||
opts = handle_revision_opt(revs, argc - i, argv + i, &left, argv);
|
||||
if (opts > 0) {
|
||||
i += opts - 1;
|
||||
continue;
|
||||
}
|
||||
*unrecognized++ = arg;
|
||||
left++;
|
||||
if (opts < 0)
|
||||
exit(128);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1369,21 +1289,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
}
|
||||
}
|
||||
|
||||
if (revs->grep_filter) {
|
||||
revs->grep_filter->regflags |= regflags;
|
||||
revs->grep_filter->fixed = fixed;
|
||||
}
|
||||
|
||||
if (show_merge)
|
||||
if (revs->def == NULL)
|
||||
revs->def = def;
|
||||
if (revs->show_merge)
|
||||
prepare_show_merge(revs);
|
||||
if (def && !revs->pending.nr) {
|
||||
if (revs->def && !revs->pending.nr) {
|
||||
unsigned char sha1[20];
|
||||
struct object *object;
|
||||
unsigned mode;
|
||||
if (get_sha1_with_mode(def, sha1, &mode))
|
||||
die("bad default revision '%s'", def);
|
||||
object = get_reference(revs, def, sha1, 0);
|
||||
add_pending_object_with_mode(revs, object, def, mode);
|
||||
if (get_sha1_with_mode(revs->def, sha1, &mode))
|
||||
die("bad default revision '%s'", revs->def);
|
||||
object = get_reference(revs, revs->def, sha1, 0);
|
||||
add_pending_object_with_mode(revs, object, revs->def, mode);
|
||||
}
|
||||
|
||||
/* Did the user ask for any diff output? Run the diff! */
|
||||
@ -1417,12 +1334,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
die("diff_setup_done failed");
|
||||
|
||||
if (revs->grep_filter) {
|
||||
revs->grep_filter->all_match = all_match;
|
||||
compile_grep_patterns(revs->grep_filter);
|
||||
}
|
||||
|
||||
if (revs->reverse && revs->reflog_info)
|
||||
die("cannot combine --reverse with --walk-reflogs");
|
||||
if (revs->rewrite_parents && revs->children.name)
|
||||
die("cannot combine --parents and --children");
|
||||
|
||||
/*
|
||||
* Limitations on the graph functionality
|
||||
@ -1436,6 +1354,26 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
|
||||
return left;
|
||||
}
|
||||
|
||||
static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
|
||||
{
|
||||
struct commit_list *l = xcalloc(1, sizeof(*l));
|
||||
|
||||
l->item = child;
|
||||
l->next = add_decoration(&revs->children, &parent->object, l);
|
||||
}
|
||||
|
||||
static void set_children(struct rev_info *revs)
|
||||
{
|
||||
struct commit_list *l;
|
||||
for (l = revs->commits; l; l = l->next) {
|
||||
struct commit *commit = l->item;
|
||||
struct commit_list *p;
|
||||
|
||||
for (p = commit->parents; p; p = p->next)
|
||||
add_child(revs, p->item, commit);
|
||||
}
|
||||
}
|
||||
|
||||
int prepare_revision_walk(struct rev_info *revs)
|
||||
{
|
||||
int nr = revs->pending.nr;
|
||||
@ -1464,6 +1402,8 @@ int prepare_revision_walk(struct rev_info *revs)
|
||||
return -1;
|
||||
if (revs->topo_order)
|
||||
sort_in_topological_order(&revs->commits, revs->lifo);
|
||||
if (revs->children.name)
|
||||
set_children(revs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1541,6 +1481,11 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
|
||||
commit->buffer, strlen(commit->buffer));
|
||||
}
|
||||
|
||||
static inline int want_ancestry(struct rev_info *revs)
|
||||
{
|
||||
return (revs->rewrite_parents || revs->children.name);
|
||||
}
|
||||
|
||||
enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
|
||||
{
|
||||
if (commit->object.flags & SHOWN)
|
||||
@ -1561,13 +1506,13 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
|
||||
/* Commit without changes? */
|
||||
if (commit->object.flags & TREESAME) {
|
||||
/* drop merges unless we want parenthood */
|
||||
if (!revs->rewrite_parents)
|
||||
if (!want_ancestry(revs))
|
||||
return commit_ignore;
|
||||
/* non-merge - always ignore it */
|
||||
if (!commit->parents || !commit->parents->next)
|
||||
return commit_ignore;
|
||||
}
|
||||
if (revs->rewrite_parents && rewrite_parents(revs, commit) < 0)
|
||||
if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0)
|
||||
return commit_error;
|
||||
}
|
||||
return commit_show;
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef REVISION_H
|
||||
#define REVISION_H
|
||||
|
||||
#include "parse-options.h"
|
||||
|
||||
#define SEEN (1u<<0)
|
||||
#define UNINTERESTING (1u<<1)
|
||||
#define TREESAME (1u<<2)
|
||||
@ -26,6 +28,7 @@ struct rev_info {
|
||||
|
||||
/* Basic information */
|
||||
const char *prefix;
|
||||
const char *def;
|
||||
void *prune_data;
|
||||
unsigned int early_output;
|
||||
|
||||
@ -66,6 +69,7 @@ struct rev_info {
|
||||
|
||||
/* Format info */
|
||||
unsigned int shown_one:1,
|
||||
show_merge:1,
|
||||
abbrev_commit:1,
|
||||
use_terminator:1,
|
||||
missing_newline:1;
|
||||
@ -105,6 +109,7 @@ struct rev_info {
|
||||
struct diff_options pruning;
|
||||
|
||||
struct reflog_walk_info *reflog_info;
|
||||
struct decoration children;
|
||||
};
|
||||
|
||||
#define REV_TREE_SAME 0
|
||||
@ -119,6 +124,9 @@ volatile show_early_output_fn_t show_early_output;
|
||||
|
||||
extern void init_revisions(struct rev_info *revs, const char *prefix);
|
||||
extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
|
||||
extern void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
|
||||
const struct option *options,
|
||||
const char * const usagestr[]);
|
||||
extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
|
||||
|
||||
extern int prepare_revision_walk(struct rev_info *revs);
|
||||
|
Loading…
Reference in New Issue
Block a user