From f35f5603f4f07c939754743b2a6cf61bb3a4694e Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 3 Apr 2008 02:12:06 -0700 Subject: [PATCH 01/17] revision traversal: --children option This adds a new --children option to the revision machinery. In addition to the list of parents, child commits of each commit are computed and stored as a decoration to each commit. Signed-off-by: Junio C Hamano --- revision.c | 40 +++++++++++++++++++++++++++++++++++++--- revision.h | 1 + 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/revision.c b/revision.c index ffbed3fbf2..979241eb0d 100644 --- a/revision.c +++ b/revision.c @@ -9,6 +9,7 @@ #include "grep.h" #include "reflog-walk.h" #include "patch-ids.h" +#include "decorate.h" volatile show_early_output_fn_t show_early_output; @@ -1310,6 +1311,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->no_walk = 0; continue; } + if (!strcmp(arg, "--children")) { + revs->children.name = "children"; + revs->limited = 1; + continue; + } opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); if (opts > 0) { @@ -1395,10 +1401,31 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch if (revs->reverse && revs->reflog_info) die("cannot combine --reverse with --walk-reflogs"); - + if (revs->parents && revs->children.name) + die("cannot combine --parents and --children"); 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; @@ -1427,6 +1454,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; } @@ -1504,6 +1533,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->parents || revs->children.name); +} + enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) { if (commit->object.flags & SHOWN) @@ -1524,13 +1558,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->parents) + if (!want_ancestry(revs)) return commit_ignore; /* non-merge - always ignore it */ if (!commit->parents || !commit->parents->next) return commit_ignore; } - if (revs->parents && rewrite_parents(revs, commit) < 0) + if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0) return commit_error; } return commit_show; diff --git a/revision.h b/revision.h index c8b3b948ec..966116cd5b 100644 --- a/revision.h +++ b/revision.h @@ -98,6 +98,7 @@ struct rev_info { struct diff_options pruning; struct reflog_walk_info *reflog_info; + struct decoration children; }; #define REV_TREE_SAME 0 From 72276a3ecbe6353b83ab37e0ce96cc21c94cf6ee Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 3 Apr 2008 23:01:47 -0700 Subject: [PATCH 02/17] rev-list --children Just like --parents option shows the parents of commits, this shows the children of commits. Signed-off-by: Junio C Hamano --- Documentation/rev-list-options.txt | 4 ++++ builtin-rev-list.c | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt index 2648a55085..e5823950e2 100644 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@ -42,6 +42,10 @@ format, often found in E-mail messages. Print the parents of the commit. +--children:: + + Print the children of the commit. + --timestamp:: Print the raw commit timestamp. diff --git a/builtin-rev-list.c b/builtin-rev-list.c index edc0bd35bb..9da2f76375 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -36,6 +36,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" @@ -84,6 +85,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(' '); From f6c07d7d475ffaa67b817beb2635fd73a5e0e962 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 2 Apr 2008 22:17:53 -0700 Subject: [PATCH 03/17] builtin-blame.c: move prepare_final() into a separate function. After parsing the command line, we have a long loop to compute the commit object to start annotating from. Move the logic to a separate function, so that later patches become easier to read. It also makes fill_origin_blob() return void; the check is always done on !file->ptr, and nobody looks at the return value from the function. Signed-off-by: Junio C Hamano --- builtin-blame.c | 56 +++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index bfd562d7d2..996f535767 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -91,7 +91,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 +106,6 @@ static char *fill_origin_blob(struct origin *o, mmfile_t *file) } else *file = o->file; - return file->ptr; } /* @@ -2006,6 +2005,10 @@ static int git_blame_config(const char *var, const char *value) return git_default_config(var, value); } +/* + * 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 +2125,33 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con return commit; } +static const char *prepare_final(struct scoreboard *sb, struct rev_info *revs) +{ + int i; + const char *final_commit_name = NULL; + + /* + * 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; +} + int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -2327,27 +2357,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) setup_revisions(unk, 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; - } - + final_commit_name = prepare_final(&sb, &revs); if (!sb.final) { /* * "--not A B -- path" without anything positive; From 69264f46a193ae9dec5761984b4bae32f4810916 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 3 Apr 2008 00:56:23 -0700 Subject: [PATCH 04/17] builtin-blame.c: allow more than 16 parents This removes the hardcoded 16 parents limit from git-blame by allowing the parent array to be allocated dynamically. As the ultimate objective is not about allowing dodecapus, but about annotating the history upside down, it also renames "parent" in the code to "scapegoat"; the name of the game used to be "pass blame to your parents", but now it is "find a scapegoat to pass blame on". Signed-off-by: Junio C Hamano --- builtin-blame.c | 91 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index 996f535767..fbc441fb9f 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1191,18 +1191,45 @@ 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 commit *commit) +{ + return commit->parents; +} + +static int num_scapegoats(struct commit *commit) +{ + int cnt; + struct commit_list *l = first_scapegoat(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; + 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(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++) { @@ -1210,13 +1237,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(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; @@ -1229,24 +1256,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(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)) @@ -1257,10 +1284,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(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)) @@ -1271,23 +1298,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(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); } /* From 85af7929ee125385c2771fa4eaccfa2f29dc63c9 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 2 Apr 2008 22:17:53 -0700 Subject: [PATCH 05/17] git-blame --reverse This new option allows "git blame" to read an old version of the file, and up to which commit each line survived (i.e. their children rewrote the line out of the contents). The previous revision machinery update to decorate each commit with its children was leading to this change. When the --reverse option is given, we read the old version and pass blame to the children of the current suspect, instead of the usual order of starting from the latest and passing blame to parents. The standard yardstick of "blame" in git.git history is "rev-list.c" which was refactored heavily in its existence. For example: git blame -C -C -w --reverse 9de48752..master -- rev-list.c begins like this: 6c41b801 builtin-rev-list.c (JC Hamano 2008-04-02 1) #include "cache... 6c41b801 builtin-rev-list.c (JC Hamano 2008-04-02 2) #include "commi... 6c41b801 builtin-rev-list.c (JC Hamano 2008-04-02 3) #include "tree.... 6c41b801 builtin-rev-list.c (JC Hamano 2008-04-02 4) #include "blob.... 213523f4 rev-list.c (JC Hamano 2006-03-01 5) #include "epoch... 6c41b801 builtin-rev-list.c (JC Hamano 2008-04-02 6) ab57c8dd rev-list.c (JC Hamano 2006-02-24 7) #define SEEN ab57c8dd rev-list.c (JC Hamano 2006-02-24 8) #define INTERES... 213523f4 rev-list.c (JC Hamano 2006-03-01 9) #define COUNTED... 7e21c29b rev-list.c (LTorvalds 2005-07-06 10) #define SHOWN ... 6c41b801 builtin-rev-list.c (JC Hamano 2008-04-02 11) 6c41b801 builtin-rev-list.c (JC Hamano 2008-04-02 12) static const ch... b1349229 rev-list.c (LTorvalds 2005-07-26 13) "usage: git-... This reveals that the original first four lines survived until now in builtin-rev-list.c , inclusion of "epoch.h" was removed after 213523f4 while the contents was still in rev-list.c. This mode probably needs more tweaking so that the commit that removed the line (i.e. the children of the commits listed in the above sample output) is shown instead to be useful, but then there is a little matter of which child of a fork point to show. For now, you can find the diff that rewrote the fifth line above by doing: $ git log --children 213523f4^.. to find its child, which is 1025fe5 (Merge branch 'lt/rev-list' into next, 2006-03-01), and then look at that child with: $ git show 1025fe5 Signed-off-by: Junio C Hamano --- builtin-blame.c | 81 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 16 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index fbc441fb9f..5c7546db25 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -43,6 +43,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; @@ -177,7 +178,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; /* @@ -1196,15 +1197,17 @@ static void pass_whole_blame(struct scoreboard *sb, * "parent" (and "porigin"), but what we mean is to find scapegoat to * exonerate ourselves. */ -static struct commit_list *first_scapegoat(struct commit *commit) +static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit) { - return commit->parents; + if (!reverse) + return commit->parents; + return lookup_decoration(&revs->children, &commit->object); } -static int num_scapegoats(struct commit *commit) +static int num_scapegoats(struct rev_info *revs, struct commit *commit) { int cnt; - struct commit_list *l = first_scapegoat(commit); + struct commit_list *l = first_scapegoat(revs, commit); for (cnt = 0; l; l = l->next) cnt++; return cnt; @@ -1214,13 +1217,14 @@ static int num_scapegoats(struct commit *commit) static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) { + struct rev_info *revs = sb->revs; int i, pass, num_sg; struct commit *commit = origin->commit; struct commit_list *sg; struct origin *sg_buf[MAXSG]; struct origin *porigin, **sg_origin = sg_buf; - num_sg = num_scapegoats(commit); + num_sg = num_scapegoats(revs, commit); if (!num_sg) goto finish; else if (num_sg < ARRAY_SIZE(sg_buf)) @@ -1237,7 +1241,7 @@ 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, sg = first_scapegoat(commit); + for (i = 0, sg = first_scapegoat(revs, commit); i < num_sg && sg; sg = sg->next, i++) { struct commit *p = sg->item; @@ -1270,7 +1274,7 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt) } num_commits++; - for (i = 0, sg = first_scapegoat(commit); + for (i = 0, sg = first_scapegoat(revs, commit); i < num_sg && sg; sg = sg->next, i++) { struct origin *porigin = sg_origin[i]; @@ -1284,7 +1288,7 @@ 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, sg = first_scapegoat(commit); + for (i = 0, sg = first_scapegoat(revs, commit); i < num_sg && sg; sg = sg->next, i++) { struct origin *porigin = sg_origin[i]; @@ -1298,7 +1302,7 @@ 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, sg = first_scapegoat(commit); + for (i = 0, sg = first_scapegoat(revs, commit); i < num_sg && sg; sg = sg->next, i++) { struct origin *porigin = sg_origin[i]; @@ -1515,8 +1519,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; @@ -1537,8 +1543,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; @@ -2154,10 +2161,11 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con return commit; } -static const char *prepare_final(struct scoreboard *sb, struct rev_info *revs) +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 @@ -2181,6 +2189,36 @@ static const char *prepare_final(struct scoreboard *sb, struct rev_info *revs) 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; +} + int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@ -2213,6 +2251,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix) blank_boundary = 1; else if (!strcmp("--root", arg)) show_root = 1; + else if (!strcmp("--reverse", arg)) { + argv[unk++] = "--children"; + reverse = 1; + } else if (!strcmp(arg, "--show-stats")) show_stats = 1; else if (!strcmp("-c", arg)) @@ -2386,7 +2428,14 @@ int cmd_blame(int argc, const char **argv, const char *prefix) setup_revisions(unk, argv, &revs, NULL); memset(&sb, 0, sizeof(sb)); - final_commit_name = prepare_final(&sb, &revs); + 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) { /* * "--not A B -- path" without anything positive; @@ -2464,7 +2513,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; From 7e7bbcb4b35cbb4bbb5e65cf057c84b16dbd3d39 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 23 Jun 2008 21:59:37 +0200 Subject: [PATCH 06/17] parse-opt: have parse_options_{start,end}. Make the struct optparse_t public under the better name parse_opt_ctx_t. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- parse-options.c | 69 ++++++++++++++++++++++++++++--------------------- parse-options.h | 16 ++++++++++++ 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/parse-options.c b/parse-options.c index b8bde2b04a..9964877edf 100644 --- a/parse-options.c +++ b/parse-options.c @@ -4,14 +4,7 @@ #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 +30,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; @@ -131,7 +124,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) { @@ -142,7 +135,7 @@ static int parse_short_opt(struct optparse_t *p, const struct option *options) return error("unknown switch `%c'", *p->opt); } -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, '='); @@ -247,45 +240,63 @@ void check_typos(const char *arg, const struct option *options) } } +void parse_options_start(struct parse_opt_ctx_t *ctx, + int argc, const char **argv, int flags) +{ + memset(ctx, 0, sizeof(*ctx)); + ctx->argc = argc - 1; + ctx->argv = argv + 1; + ctx->out = argv; + ctx->flags = flags; +} + +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; +} + 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) { - struct optparse_t args = { argv + 1, argv, argc - 1, 0, NULL }; + struct parse_opt_ctx_t ctx; - for (; args.argc; args.argc--, args.argv++) { - const char *arg = args.argv[0]; + parse_options_start(&ctx, argc, argv, flags); + 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') + ctx.opt = arg + 1; + if (*ctx.opt == 'h') usage_with_options(usagestr, options); - if (parse_short_opt(&args, options) < 0) + if (parse_short_opt(&ctx, options) < 0) usage_with_options(usagestr, options); - if (args.opt) + if (ctx.opt) check_typos(arg + 1, options); - while (args.opt) { - if (*args.opt == 'h') + while (ctx.opt) { + if (*ctx.opt == 'h') usage_with_options(usagestr, options); - if (parse_short_opt(&args, options) < 0) + if (parse_short_opt(&ctx, options) < 0) usage_with_options(usagestr, options); } 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; } @@ -294,13 +305,11 @@ int parse_options(int argc, const char **argv, const struct option *options, 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)) + if (parse_long_opt(&ctx, arg + 2, options)) 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 diff --git a/parse-options.h b/parse-options.h index 4ee443dafe..72027f3c66 100644 --- a/parse-options.h +++ b/parse-options.h @@ -111,6 +111,22 @@ 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 -----*/ + +struct parse_opt_ctx_t { + const char **argv; + const char **out; + int argc, cpidx; + const char *opt; + int flags; +}; + +extern void parse_options_start(struct parse_opt_ctx_t *ctx, + int argc, const char **argv, int flags); + +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); From ee68b87a62a245fca46374fe5132aec58d802baa Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 23 Jun 2008 22:28:04 +0200 Subject: [PATCH 07/17] parse-opt: Export a non NORETURN usage dumper. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- parse-options.c | 24 +++++++++++++++++------- parse-options.h | 9 +++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/parse-options.c b/parse-options.c index 9964877edf..007b78eb24 100644 --- a/parse-options.c +++ b/parse-options.c @@ -257,8 +257,8 @@ int parse_options_end(struct parse_opt_ctx_t *ctx) return ctx->cpidx + ctx->argc; } -static NORETURN void usage_with_options_internal(const char * const *, - const struct option *, int); +static int usage_with_options_internal(const char * const *, + const struct option *, int, int); int parse_options(int argc, const char **argv, const struct option *options, const char * const usagestr[], int flags) @@ -302,7 +302,7 @@ int parse_options(int argc, const char **argv, const struct option *options, } if (!strcmp(arg + 2, "help-all")) - usage_with_options_internal(usagestr, options, 1); + usage_with_options_internal(usagestr, options, 1, 1); if (!strcmp(arg + 2, "help")) usage_with_options(usagestr, options); if (parse_long_opt(&ctx, arg + 2, options)) @@ -315,8 +315,8 @@ int parse_options(int argc, const char **argv, const struct option *options, #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, int do_exit) { fprintf(stderr, "usage: %s\n", *usagestr++); while (*usagestr && **usagestr) @@ -401,15 +401,25 @@ void usage_with_options_internal(const char * const *usagestr, } fputc('\n', stderr); - exit(129); + if (do_exit) + exit(129); + return PARSE_OPT_HELP; } void usage_with_options(const char * const *usagestr, const struct option *opts) { - usage_with_options_internal(usagestr, opts, 0); + usage_with_options_internal(usagestr, opts, 0, 1); + exit(129); /* make gcc happy */ } +int parse_options_usage(const char * const *usagestr, + const struct option *opts) +{ + return usage_with_options_internal(usagestr, opts, 0, 0); +} + + /*----- some often used options -----*/ #include "cache.h" diff --git a/parse-options.h b/parse-options.h index 72027f3c66..f66ca352dc 100644 --- a/parse-options.h +++ b/parse-options.h @@ -113,6 +113,12 @@ extern NORETURN void usage_with_options(const char * const *usagestr, /*----- incremantal advanced APIs -----*/ +enum { + PARSE_OPT_HELP = -1, + PARSE_OPT_DONE, + PARSE_OPT_UNKNOWN, +}; + struct parse_opt_ctx_t { const char **argv; const char **out; @@ -121,6 +127,9 @@ struct parse_opt_ctx_t { 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); From ff43ec3e2d2f71482fe17ba9ba5f4e8074cc54ee Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 23 Jun 2008 22:38:58 +0200 Subject: [PATCH 08/17] parse-opt: create parse_options_step. For now it's unable to stop at unknown options, this commit merely reorganize some code around. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- parse-options.c | 117 +++++++++++++++++++++++++++--------------------- parse-options.h | 4 ++ 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/parse-options.c b/parse-options.c index 007b78eb24..04adf219ca 100644 --- a/parse-options.c +++ b/parse-options.c @@ -250,6 +250,58 @@ void parse_options_start(struct parse_opt_ctx_t *ctx, ctx->flags = flags; } +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[]) +{ + for (; ctx->argc; ctx->argc--, ctx->argv++) { + const char *arg = ctx->argv[0]; + + if (*arg != '-' || !arg[1]) { + if (ctx->flags & PARSE_OPT_STOP_AT_NON_OPTION) + break; + ctx->out[ctx->cpidx++] = ctx->argv[0]; + continue; + } + + if (arg[1] != '-') { + ctx->opt = arg + 1; + if (*ctx->opt == 'h') + return parse_options_usage(usagestr, options); + if (parse_short_opt(ctx, options) < 0) + usage_with_options(usagestr, options); + if (ctx->opt) + check_typos(arg + 1, options); + while (ctx->opt) { + if (*ctx->opt == 'h') + return parse_options_usage(usagestr, options); + if (parse_short_opt(ctx, options) < 0) + usage_with_options(usagestr, options); + } + continue; + } + + if (!arg[2]) { /* "--" */ + if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) { + ctx->argc--; + ctx->argv++; + } + break; + } + + if (!strcmp(arg + 2, "help-all")) + return usage_with_options_internal(usagestr, options, 1); + if (!strcmp(arg + 2, "help")) + return parse_options_usage(usagestr, options); + if (parse_long_opt(ctx, arg + 2, options)) + usage_with_options(usagestr, options); + } + 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)); @@ -257,56 +309,19 @@ int parse_options_end(struct parse_opt_ctx_t *ctx) return ctx->cpidx + ctx->argc; } -static int usage_with_options_internal(const char * const *, - const struct option *, int, int); - int parse_options(int argc, const char **argv, const struct option *options, - const char * const usagestr[], int flags) + const char * const usagestr[], int flags) { struct parse_opt_ctx_t ctx; parse_options_start(&ctx, argc, argv, flags); - for (; ctx.argc; ctx.argc--, ctx.argv++) { - const char *arg = ctx.argv[0]; - - if (*arg != '-' || !arg[1]) { - if (ctx.flags & PARSE_OPT_STOP_AT_NON_OPTION) - break; - ctx.out[ctx.cpidx++] = ctx.argv[0]; - continue; - } - - if (arg[1] != '-') { - ctx.opt = arg + 1; - if (*ctx.opt == 'h') - usage_with_options(usagestr, options); - if (parse_short_opt(&ctx, options) < 0) - usage_with_options(usagestr, options); - if (ctx.opt) - check_typos(arg + 1, options); - while (ctx.opt) { - if (*ctx.opt == 'h') - usage_with_options(usagestr, options); - if (parse_short_opt(&ctx, options) < 0) - usage_with_options(usagestr, options); - } - continue; - } - - if (!arg[2]) { /* "--" */ - 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, 1); - if (!strcmp(arg + 2, "help")) - usage_with_options(usagestr, options); - if (parse_long_opt(&ctx, arg + 2, options)) - usage_with_options(usagestr, options); + switch (parse_options_step(&ctx, options, usagestr)) { + case PARSE_OPT_HELP: + exit(129); + case PARSE_OPT_DONE: + break; + default: /* PARSE_OPT_UNKNOWN */ + abort(); /* unreached yet */ } return parse_options_end(&ctx); @@ -316,7 +331,7 @@ int parse_options(int argc, const char **argv, const struct option *options, #define USAGE_GAP 2 int usage_with_options_internal(const char * const *usagestr, - const struct option *opts, int full, int do_exit) + const struct option *opts, int full) { fprintf(stderr, "usage: %s\n", *usagestr++); while (*usagestr && **usagestr) @@ -401,22 +416,20 @@ int usage_with_options_internal(const char * const *usagestr, } fputc('\n', stderr); - if (do_exit) - 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, 1); - exit(129); /* make gcc happy */ + 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, 0); + return usage_with_options_internal(usagestr, opts, 0); } diff --git a/parse-options.h b/parse-options.h index f66ca352dc..33c683cb54 100644 --- a/parse-options.h +++ b/parse-options.h @@ -133,6 +133,10 @@ extern int parse_options_usage(const char * const *usagestr, 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); From 07fe54db3cdf42500ac2e893b670fd74841afdc4 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 23 Jun 2008 22:46:36 +0200 Subject: [PATCH 09/17] parse-opt: do not print errors on unknown options, return -2 intead. This way we can catch "unknown" options more easily. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- parse-options.c | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/parse-options.c b/parse-options.c index 04adf219ca..19fc849f4b 100644 --- a/parse-options.c +++ b/parse-options.c @@ -94,14 +94,14 @@ static int get_value(struct parse_opt_ctx_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) { @@ -132,7 +132,7 @@ static int parse_short_opt(struct parse_opt_ctx_t *p, const struct option *optio return get_value(p, options, OPT_SHORT); } } - return error("unknown switch `%c'", *p->opt); + return -2; } static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, @@ -217,7 +217,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) @@ -271,15 +271,23 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, ctx->opt = arg + 1; if (*ctx->opt == 'h') return parse_options_usage(usagestr, options); - if (parse_short_opt(ctx, options) < 0) - usage_with_options(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 (ctx->opt) { if (*ctx->opt == 'h') return parse_options_usage(usagestr, options); - if (parse_short_opt(ctx, options) < 0) - usage_with_options(usagestr, options); + switch (parse_short_opt(ctx, options)) { + case -1: + return parse_options_usage(usagestr, options); + case -2: + return PARSE_OPT_UNKNOWN; + } } continue; } @@ -296,8 +304,12 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, return usage_with_options_internal(usagestr, options, 1); if (!strcmp(arg + 2, "help")) return parse_options_usage(usagestr, options); - if (parse_long_opt(ctx, arg + 2, options)) - usage_with_options(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; } @@ -321,7 +333,12 @@ int parse_options(int argc, const char **argv, const struct option *options, case PARSE_OPT_DONE: break; default: /* PARSE_OPT_UNKNOWN */ - abort(); /* unreached yet */ + 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); } return parse_options_end(&ctx); From 26141b5b60eea36f1d771312f6cae9e56dbbf760 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Mon, 23 Jun 2008 22:55:11 +0200 Subject: [PATCH 10/17] parse-opt: fake short strings for callers to believe in. If we begin to parse -abc and that the parser knew about -a and -b, it will fake a -c switch for the caller to deal with. Of course in the case of -acb (supposing -c is not taking an argument) the caller will have to be especially clever to do the same thing. We could think about exposing an API to do so if it's really needed, but oh well... Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- parse-options.c | 11 +++++++++++ parse-options.h | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/parse-options.c b/parse-options.c index 19fc849f4b..0d3818ab48 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1,5 +1,6 @@ #include "git-compat-util.h" #include "parse-options.h" +#include "cache.h" #define OPT_SHORT 1 #define OPT_UNSET 2 @@ -257,6 +258,9 @@ 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]; @@ -286,6 +290,13 @@ int parse_options_step(struct parse_opt_ctx_t *ctx, 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; } } diff --git a/parse-options.h b/parse-options.h index 33c683cb54..aeed627e97 100644 --- a/parse-options.h +++ b/parse-options.h @@ -119,6 +119,11 @@ enum { 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; From a32a4eaa36527ab1c9a999357f9edd5e04591a4a Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 24 Jun 2008 00:31:31 +0200 Subject: [PATCH 11/17] parse-opt: add PARSE_OPT_KEEP_ARGV0 parser option. This way, argv[0] isn't clobbered when parse-options filters argv[]. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- parse-options.c | 1 + parse-options.h | 1 + 2 files changed, 2 insertions(+) diff --git a/parse-options.c b/parse-options.c index 0d3818ab48..469831d21b 100644 --- a/parse-options.c +++ b/parse-options.c @@ -248,6 +248,7 @@ void parse_options_start(struct parse_opt_ctx_t *ctx, ctx->argc = argc - 1; ctx->argv = argv + 1; ctx->out = argv; + ctx->cpidx = ((flags & PARSE_OPT_KEEP_ARGV0) != 0); ctx->flags = flags; } diff --git a/parse-options.h b/parse-options.h index aeed627e97..c5f0b4b4da 100644 --- a/parse-options.h +++ b/parse-options.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 { From 02e542206f26cf06817ec2e9ffecf4f416e8e332 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 8 Jul 2008 15:19:33 +0200 Subject: [PATCH 12/17] revisions: split handle_revision_opt() from setup_revisions() Add two fields to struct rev_info: - .def to store --default argument; and - .show_merge 1-bit field. handle_revision_opt() is able to deal with any revision option, and consumes them, and leaves revision arguments or pseudo arguments (like --all, --not, ...) in place. For now setup_revisions() does a pass of handle_revision_opt() again so that code not using it in a parse-opt parser still work the same. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- revision.c | 548 ++++++++++++++++++++++------------------------------- revision.h | 4 + 2 files changed, 226 insertions(+), 326 deletions(-) diff --git a/revision.c b/revision.c index 5a1a948a41..4b6925be08 100644 --- a/revision.c +++ b/revision.c @@ -957,6 +957,212 @@ static void add_ignore_packed(struct rev_info *revs, const char *name) revs->ignore_packed[num] = NULL; } +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 -, 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; +} + /* * Parse revision information, filling in the "rev_info" structure, * and removing the used arguments from the argument list. @@ -966,12 +1172,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; @@ -987,58 +1188,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 -, 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; @@ -1055,265 +1211,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; @@ -1322,19 +1227,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch revs->no_walk = 0; continue; } - if (!strcmp(arg, "--children")) { - revs->children.name = "children"; - revs->limited = 1; - 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; } @@ -1358,21 +1258,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! */ @@ -1406,7 +1303,6 @@ 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); } diff --git a/revision.h b/revision.h index dcf08e089a..c44498e3b3 100644 --- a/revision.h +++ b/revision.h @@ -25,6 +25,7 @@ struct rev_info { /* Basic information */ const char *prefix; + const char *def; void *prune_data; unsigned int early_output; @@ -65,6 +66,7 @@ struct rev_info { /* Format info */ unsigned int shown_one:1, + show_merge:1, abbrev_commit:1, use_terminator:1, missing_newline:1; @@ -117,6 +119,8 @@ 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 int handle_revision_opt(struct rev_info *revs, int argc, const char **argv, + int *unkc, const char **unkv); 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); From 5817da01434a24e693ea4d5ba6680d488f5f543f Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 8 Jul 2008 15:19:34 +0200 Subject: [PATCH 13/17] git-blame: migrate to incremental parse-option [1/2] This step merely moves the parser to an incremental version, still using parse_revisions. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-blame.c | 220 ++++++++++++++++++++++++++---------------------- 1 file changed, 118 insertions(+), 102 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index cf41511c79..5e82cd3d54 100644 --- a/builtin-blame.c +++ b/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 ] [-M] [-C] [-C] [--contents ] [--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 '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-parse(1)", + NULL +}; static int longest_file; static int longest_author; @@ -2219,6 +2211,50 @@ static const char *prepare_initial(struct scoreboard *sb) 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; @@ -2226,98 +2262,79 @@ 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; + int i, seen_dashdash, unk; long bottom, top, lno; - int output_option = 0; - int show_stats = 0; - const char *revs_file = NULL; 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 instead of calling git-rev-list"), + OPT_STRING(0, "contents", &contents_from, "file", "Use '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; - opt = 0; + parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH | + PARSE_OPT_KEEP_ARGV0); + for (;;) { + int n; + + switch (parse_options_step(&ctx, options, blame_opt_usage)) { + case PARSE_OPT_HELP: + exit(129); + case PARSE_OPT_DONE: + goto parse_done; + } + + if (!strcmp(ctx.argv[0], "--reverse")) { + ctx.argv[0] = "--children"; + reverse = 1; + } + 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(blame_opt_usage, options); + } + ctx.argv += n; + ctx.argc -= n; + } +parse_done: + argc = parse_options_end(&ctx); + 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("--reverse", arg)) { - argv[unk++] = "--children"; - reverse = 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); - } - 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); - } - 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++; @@ -2364,16 +2381,16 @@ int cmd_blame(int argc, const char **argv, const char *prefix) if (seen_dashdash) { /* (1) */ if (argc <= i) - usage(blame_usage); + usage_with_options(blame_opt_usage, options); path = add_prefix(prefix, argv[i]); if (i + 1 == argc - 1) { if (unk != 1) - usage(blame_usage); + usage_with_options(blame_opt_usage, options); argv[unk++] = argv[i + 1]; } else if (i + 1 != argc) /* garbage at end */ - usage(blame_usage); + usage_with_options(blame_opt_usage, options); } else { int j; @@ -2383,7 +2400,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) if (seen_dashdash) { /* (2) */ if (seen_dashdash + 1 != argc - 1) - usage(blame_usage); + usage_with_options(blame_opt_usage, options); path = add_prefix(prefix, argv[seen_dashdash + 1]); for (j = i; j < seen_dashdash; j++) argv[unk++] = argv[j]; @@ -2391,7 +2408,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) else { /* (3) */ if (argc <= i) - usage(blame_usage); + usage_with_options(blame_opt_usage, options); path = add_prefix(prefix, argv[i]); if (i + 1 == argc - 1) { final_commit_name = argv[i + 1]; @@ -2405,7 +2422,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) } } else if (i != argc - 1) - usage(blame_usage); /* garbage at end */ + usage_with_options(blame_opt_usage, options); setup_work_tree(); if (!has_path_in_work_tree(path)) @@ -2424,7 +2441,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix) argv[unk++] = "--"; /* terminate the rev name */ argv[unk] = NULL; - init_revisions(&revs, NULL); setup_revisions(unk, argv, &revs, NULL); memset(&sb, 0, sizeof(sb)); From 3f8d5204896a85d9268c579fc3e31b22b33fb803 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Tue, 8 Jul 2008 15:19:35 +0200 Subject: [PATCH 14/17] git-blame: migrate to incremental parse-option [2/2] Now use handle_revision_args instead of parse_revisions, and simplify the handling of old-style arguments a lot thanks to the filtering. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-blame.c | 130 ++++++++++++++---------------------------------- 1 file changed, 38 insertions(+), 92 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index 5e82cd3d54..99f5140013 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2262,8 +2262,7 @@ 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; - long bottom, top, lno; + long dashdash_pos, bottom, top, lno; const char *final_commit_name = NULL; enum object_type type; @@ -2301,6 +2300,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) git_config(git_blame_config, NULL); init_revisions(&revs, NULL); save_commit_buffer = 0; + dashdash_pos = 0; parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); @@ -2311,6 +2311,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix) case PARSE_OPT_HELP: exit(129); case PARSE_OPT_DONE: + if (ctx.argv[0]) + dashdash_pos = ctx.cpidx; goto parse_done; } @@ -2330,20 +2332,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix) parse_done: argc = parse_options_end(&ctx); - seen_dashdash = 0; - for (unk = i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (*arg != '-') - break; - else if (!strcmp("--", arg)) { - seen_dashdash = 1; - i++; - break; - } - else - argv[unk++] = arg; - } - if (!blame_move_score) blame_move_score = BLAME_DEFAULT_MOVE_SCORE; if (!blame_copy_score) @@ -2356,92 +2344,50 @@ parse_done: * * The remaining are: * - * (1) if seen_dashdash, its either - * "-options -- " or - * "-options -- ". - * 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] -- " or + * "blame -- " * - * (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] " + * "blame " * - * (3) otherwise, its one of the three: - * "-options " - * "-options " - * "-options " - * 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 from the arguments: we do not + * want the path pruning but we may want "bottom" processing. */ - - if (seen_dashdash) { - /* (1) */ - if (argc <= i) + 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: -- */ + 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); - path = add_prefix(prefix, argv[i]); - if (i + 1 == argc - 1) { - if (unk != 1) - usage_with_options(blame_opt_usage, options); - argv[unk++] = argv[i + 1]; } - else if (i + 1 != argc) - /* garbage at end */ + } else { + if (argc < 2) usage_with_options(blame_opt_usage, options); - } - 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_with_options(blame_opt_usage, options); - path = add_prefix(prefix, argv[seen_dashdash + 1]); - for (j = i; j < seen_dashdash; j++) - argv[unk++] = argv[j]; + 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_with_options(blame_opt_usage, options); - 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_with_options(blame_opt_usage, options); - - 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; - - setup_revisions(unk, argv, &revs, NULL); + setup_revisions(argc, argv, &revs, NULL); memset(&sb, 0, sizeof(sb)); sb.revs = &revs; From 76447d235ce2f4ccee7905971a33d3baef252701 Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 9 Jul 2008 22:47:38 +0200 Subject: [PATCH 15/17] git-blame: fix lapsus Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-blame.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin-blame.c b/builtin-blame.c index 99f5140013..73d26c6e90 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -25,7 +25,7 @@ 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-parse(1)", + "[rev-opts] are documented in git-rev-list(1)", NULL }; From 14ec9cbdae1991a14aa1cce251e44ea5cfee5ade Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 9 Jul 2008 23:38:33 +0200 Subject: [PATCH 16/17] git-shortlog: migrate to parse-options partially. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-shortlog.c | 133 ++++++++++++++++++++++++++------------------- 1 file changed, 76 insertions(+), 57 deletions(-) diff --git a/builtin-shortlog.c b/builtin-shortlog.c index e6a2865019..9107bffb9b 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -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] [... ]"; +static char const * const shortlog_usage[] = { + "git-shortlog [-n] [-s] [-e] [-w] [rev-opts] [--] [... ]", + "", + "[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[[,[,]]]"; #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,54 @@ 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 (;;) { + int n; + switch (parse_options_step(&ctx, options, shortlog_usage)) { + case PARSE_OPT_HELP: + exit(129); + case PARSE_OPT_DONE: + goto parse_done; + } + n = handle_revision_opt(&rev, ctx.argc, ctx.argv, + &ctx.cpidx, ctx.out); + if (n <= 0) { + error("unknown option `%s'", ctx.argv[0]); + usage_with_options(shortlog_usage, options); + } + ctx.argv += n; + ctx.argc -= n; + } +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)) From 6b61ec0564993d2e60f7eb56c0f0fd9c313d5e2c Mon Sep 17 00:00:00 2001 From: Pierre Habouzit Date: Wed, 9 Jul 2008 23:38:34 +0200 Subject: [PATCH 17/17] revisions: refactor handle_revision_opt into parse_revision_opt. It seems we're using handle_revision_opt the same way each time, have a wrapper around it that does the 9-liner we copy each time instead. handle_revision_opt can be static in the module for now, it's always possible to make it public again if needed. Signed-off-by: Pierre Habouzit Signed-off-by: Junio C Hamano --- builtin-blame.c | 11 +---------- builtin-shortlog.c | 10 +--------- revision.c | 18 ++++++++++++++++-- revision.h | 7 +++++-- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/builtin-blame.c b/builtin-blame.c index 73d26c6e90..06c7de4297 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -2305,8 +2305,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix) parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); for (;;) { - int n; - switch (parse_options_step(&ctx, options, blame_opt_usage)) { case PARSE_OPT_HELP: exit(129); @@ -2320,14 +2318,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix) ctx.argv[0] = "--children"; reverse = 1; } - 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(blame_opt_usage, options); - } - ctx.argv += n; - ctx.argc -= n; + parse_revision_opt(&revs, &ctx, options, blame_opt_usage); } parse_done: argc = parse_options_end(&ctx); diff --git a/builtin-shortlog.c b/builtin-shortlog.c index 9107bffb9b..01362022c0 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -255,21 +255,13 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_ARGV0); for (;;) { - int n; switch (parse_options_step(&ctx, options, shortlog_usage)) { case PARSE_OPT_HELP: exit(129); case PARSE_OPT_DONE: goto parse_done; } - n = handle_revision_opt(&rev, ctx.argc, ctx.argv, - &ctx.cpidx, ctx.out); - if (n <= 0) { - error("unknown option `%s'", ctx.argv[0]); - usage_with_options(shortlog_usage, options); - } - ctx.argv += n; - ctx.argc -= n; + parse_revision_opt(&rev, &ctx, options, shortlog_usage); } parse_done: argc = parse_options_end(&ctx); diff --git a/revision.c b/revision.c index 4b6925be08..93918da666 100644 --- a/revision.c +++ b/revision.c @@ -957,8 +957,8 @@ static void add_ignore_packed(struct rev_info *revs, const char *name) revs->ignore_packed[num] = NULL; } -int handle_revision_opt(struct rev_info *revs, int argc, const char **argv, - int *unkc, const char **unkv) +static int handle_revision_opt(struct rev_info *revs, int argc, const char **argv, + int *unkc, const char **unkv) { const char *arg = argv[0]; @@ -1163,6 +1163,20 @@ int handle_revision_opt(struct rev_info *revs, int argc, const char **argv, 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. diff --git a/revision.h b/revision.h index c44498e3b3..15dc14925f 100644 --- a/revision.h +++ b/revision.h @@ -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) @@ -119,8 +121,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 int handle_revision_opt(struct rev_info *revs, int argc, const char **argv, - int *unkc, const char **unkv); +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);