From: Junio C Hamano Date: Tue, 8 Jul 2008 22:25:44 +0000 (-0700) Subject: Merge branch 'jc/blame' (early part) into HEAD X-Git-Tag: v1.6.0-rc0~66^2~6 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/8bb65883d164843d5bb7475281af09bae18ab22d?ds=inline;hp=-c Merge branch 'jc/blame' (early part) into HEAD * 'jc/blame' (early part): 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 Conflicts: Documentation/rev-list-options.txt revision.c --- 8bb65883d164843d5bb7475281af09bae18ab22d diff --combined Documentation/rev-list-options.txt index 37dd1d61ea,e5823950e2..b6f5d87e72 --- a/Documentation/rev-list-options.txt +++ b/Documentation/rev-list-options.txt @@@ -13,11 -13,10 +13,11 @@@ include::pretty-options.txt[ Synonym for `--date=relative`. ---date={relative,local,default,iso,rfc}:: +--date={relative,local,default,iso,rfc,short}:: Only takes effect for dates shown in human-readable format, such - as when using "--pretty". + as when using "--pretty". `log.date` config variable sets a default + value for log command's --date option. + `--date=relative` shows dates relative to the current time, e.g. "2 hours ago". @@@ -34,21 -33,21 +34,25 @@@ format, often found in E-mail messages `--date=default` shows timestamps in the original timezone (either committer's or author's). +ifdef::git-rev-list[] --header:: Print the contents of the commit in raw-format; each record is separated with a NUL character. +endif::git-rev-list[] --parents:: Print the parents of the commit. + --children:: + + Print the children of the commit. + +ifdef::git-rev-list[] --timestamp:: Print the raw commit timestamp. +endif::git-rev-list[] --left-right:: @@@ -80,16 -79,6 +84,16 @@@ you would get an output line this -xxxxxxx... 1st on a ----------------------------------------------------------------------- +--graph:: + + Draw a text-based graphical representation of the commit history + on the left hand side of the output. This may cause extra lines + to be printed in between commits, in order for the graph history + to be drawn properly. ++ +This implies the '--topo-order' option by default, but the +'--date-order' option may also be specified. + Diff Formatting ~~~~~~~~~~~~~~~ @@@ -129,8 -118,7 +133,8 @@@ limiting may be applied -- --n 'number', --max-count='number':: +-n 'number':: +--max-count='number':: Limit the number of commits output. @@@ -138,25 -126,21 +142,25 @@@ Skip 'number' commits before starting to show the commit output. ---since='date', --after='date':: +--since='date':: +--after='date':: Show commits more recent than a specific date. ---until='date', --before='date':: +--until='date':: +--before='date':: Show commits older than a specific date. ifdef::git-rev-list[] ---max-age='timestamp', --min-age='timestamp':: +--max-age='timestamp':: +--min-age='timestamp':: Limit the commits output to specified time range. endif::git-rev-list[] ---author='pattern', --committer='pattern':: +--author='pattern':: +--committer='pattern':: Limit the commits output to ones with author/committer header lines that match the specified pattern (regular expression). @@@ -166,19 -150,16 +170,19 @@@ Limit the commits output to ones with log message that matches the specified pattern (regular expression). --i, --regexp-ignore-case:: +-i:: +--regexp-ignore-case:: Match the regexp limiting patterns without regard to letters case. --E, --extended-regexp:: +-E:: +--extended-regexp:: Consider the limiting patterns to be extended regular expressions instead of the default basic regular expressions. --F, --fixed-strings:: +-F:: +--fixed-strings:: Consider the limiting patterns to be fixed strings (don't interpret pattern as a regular expression). @@@ -218,7 -199,6 +222,7 @@@ Pretend as if all the refs in `$GIT_DIR/refs/` are listed on the command line as ''. +ifdef::git-rev-list[] --stdin:: In addition to the '' listed on the command @@@ -231,7 -211,6 +235,7 @@@ test the exit status to see if a range of objects is fully connected (or not). It is faster than redirecting stdout to /dev/null as the output does not have to be formatted. +endif::git-rev-list[] --cherry-pick:: @@@ -247,8 -226,7 +251,8 @@@ from the other branch (for example, "3r from branch A). With this option, such pairs of commits are excluded from the output. --g, --walk-reflogs:: +-g:: +--walk-reflogs:: Instead of walking the commit ancestry chain, walk reflog entries from the most recent one to older ones. @@@ -277,8 -255,7 +281,8 @@@ See also linkgit:git-reflog[1] Output uninteresting commits at the boundary, which are usually not shown. ---dense, --sparse:: +--dense:: +--sparse:: When optional paths are given, the default behaviour ('--dense') is to only output commits that changes at least one of them, and also ignore diff --combined builtin-blame.c index b451f6c64d,5c7546db25..cf41511c79 --- a/builtin-blame.c +++ b/builtin-blame.c @@@ -43,6 -43,7 +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; @@@ -91,7 -92,7 +92,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 -107,6 +107,6 @@@ } else *file = o->file; - return file->ptr; } /* @@@ -178,7 -178,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; /* @@@ -1192,18 -1192,48 +1192,48 @@@ static void pass_whole_blame(struct sco } } - #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; - - memset(parent_origin, 0, sizeof(parent_origin)); + struct commit_list *sg; + struct origin *sg_buf[MAXSG]; + struct origin *porigin, **sg_origin = sg_buf; + + 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 -1241,13 +1241,13 @@@ 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 -1260,24 +1260,24 @@@ 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 -1288,10 +1288,10 @@@ * 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 -1302,25 +1302,25 @@@ * 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 -1519,10 +1519,10 @@@ static void found_guilty_entry(struct b * 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 -1543,9 +1543,9 @@@ 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; @@@ -1993,7 -2028,7 +2028,7 @@@ static void prepare_blame_range(struct usage(blame_usage); } -static int git_blame_config(const char *var, const char *value) +static int git_blame_config(const char *var, const char *value, void *cb) { if (!strcmp(var, "blame.showroot")) { show_root = git_config_bool(var, value); @@@ -2003,9 -2038,13 +2038,13 @@@ blank_boundary = git_config_bool(var, value); return 0; } - return git_default_config(var, value); + 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 -2161,64 +2161,64 @@@ 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; + } + int cmd_blame(int argc, const char **argv, const char *prefix) { struct rev_info revs; @@@ -2141,7 -2238,7 +2238,7 @@@ cmd_is_annotate = !strcmp(argv[0], "annotate"); - git_config(git_blame_config); + git_config(git_blame_config, NULL); save_commit_buffer = 0; opt = 0; @@@ -2154,6 -2251,10 +2251,10 @@@ 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)) @@@ -2327,26 -2428,13 +2428,13 @@@ 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; - } + 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 -2513,7 +2513,7 @@@ if (!incremental) setup_pager(); - assign_blame(&sb, &revs, opt); + assign_blame(&sb, opt); if (incremental) return 0; diff --combined builtin-rev-list.c index 83a7b1349e,9da2f76375..11a7eae551 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@@ -10,7 -10,6 +10,7 @@@ #include "list-objects.h" #include "builtin.h" #include "log-tree.h" +#include "graph.h" /* bits #0-15 in revision.h */ @@@ -37,6 -36,7 +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" @@@ -59,37 -59,41 +60,46 @@@ static const char *header_prefix static void finish_commit(struct commit *commit); static void show_commit(struct commit *commit) { + graph_show_commit(revs.graph); + if (show_timestamp) printf("%lu ", commit->date); if (header_prefix) fputs(header_prefix, stdout); - if (commit->object.flags & BOUNDARY) - putchar('-'); - else if (commit->object.flags & UNINTERESTING) - putchar('^'); - else if (revs.left_right) { - if (commit->object.flags & SYMMETRIC_LEFT) - putchar('<'); - else - putchar('>'); + + if (!revs.graph) { + if (commit->object.flags & BOUNDARY) + putchar('-'); + else if (commit->object.flags & UNINTERESTING) + putchar('^'); + else if (revs.left_right) { + if (commit->object.flags & SYMMETRIC_LEFT) + putchar('<'); + else + putchar('>'); + } } if (revs.abbrev_commit && revs.abbrev) fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev), stdout); else fputs(sha1_to_hex(commit->object.sha1), stdout); - if (revs.parents) { + if (revs.print_parents) { struct commit_list *parents = commit->parents; while (parents) { printf(" %s", sha1_to_hex(parents->item->object.sha1)); 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(' '); @@@ -102,48 -106,9 +112,48 @@@ pretty_print_commit(revs.commit_format, commit, &buf, revs.abbrev, NULL, NULL, revs.date_mode, 0); - if (buf.len) - printf("%s%c", buf.buf, hdr_termination); + if (revs.graph) { + if (buf.len) { + if (revs.commit_format != CMIT_FMT_ONELINE) + graph_show_oneline(revs.graph); + + graph_show_commit_msg(revs.graph, &buf); + + /* + * Add a newline after the commit message. + * + * Usually, this newline produces a blank + * padding line between entries, in which case + * we need to add graph padding on this line. + * + * However, the commit message may not end in a + * newline. In this case the newline simply + * ends the last line of the commit message, + * and we don't need any graph output. (This + * always happens with CMIT_FMT_ONELINE, and it + * happens with CMIT_FMT_USERFORMAT when the + * format doesn't explicitly end in a newline.) + */ + if (buf.len && buf.buf[buf.len - 1] == '\n') + graph_show_padding(revs.graph); + putchar('\n'); + } else { + /* + * If the message buffer is empty, just show + * the rest of the graph output for this + * commit. + */ + if (graph_show_remainder(revs.graph)) + putchar('\n'); + } + } else { + if (buf.len) + printf("%s%c", buf.buf, hdr_termination); + } strbuf_release(&buf); + } else { + if (graph_show_remainder(revs.graph)) + putchar('\n'); } maybe_flush_or_die(stdout, "stdout"); finish_commit(commit); @@@ -591,7 -556,7 +601,7 @@@ int cmd_rev_list(int argc, const char * int bisect_find_all = 0; int quiet = 0; - git_config(git_default_config); + git_config(git_default_config, NULL); init_revisions(&revs, prefix); revs.abbrev = 0; revs.commit_format = CMIT_FMT_UNSPECIFIED; diff --combined revision.c index fc66755259,979241eb0d..5a1a948a41 --- a/revision.c +++ b/revision.c @@@ -6,10 -6,10 +6,11 @@@ #include "diff.h" #include "refs.h" #include "revision.h" +#include "graph.h" #include "grep.h" #include "reflog-walk.h" #include "patch-ids.h" + #include "decorate.h" volatile show_early_output_fn_t show_early_output; @@@ -416,6 -416,7 +417,6 @@@ static int add_parents_to_list(struct r { struct commit_list *parent = commit->parents; unsigned left_flag; - int add, rest; if (commit->object.flags & ADDED) return 0; @@@ -462,18 -463,19 +463,18 @@@ left_flag = (commit->object.flags & SYMMETRIC_LEFT); - rest = !revs->first_parent_only; - for (parent = commit->parents, add = 1; parent; add = rest) { + for (parent = commit->parents; parent; parent = parent->next) { struct commit *p = parent->item; - parent = parent->next; if (parse_commit(p) < 0) return -1; p->object.flags |= left_flag; - if (p->object.flags & SEEN) - continue; - p->object.flags |= SEEN; - if (add) + if (!(p->object.flags & SEEN)) { + p->object.flags |= SEEN; insert_by_date(p, list); + } + if(revs->first_parent_only) + break; } return 0; } @@@ -1104,8 -1106,7 +1105,8 @@@ int setup_revisions(int argc, const cha } } if (!strcmp(arg, "--parents")) { - revs->parents = 1; + revs->rewrite_parents = 1; + revs->print_parents = 1; continue; } if (!strcmp(arg, "--dense")) { @@@ -1197,20 -1198,9 +1198,20 @@@ revs->verbose_header = 1; continue; } - if (!prefixcmp(arg, "--pretty")) { + if (!strcmp(arg, "--pretty")) { + revs->verbose_header = 1; + get_commit_format(arg+8, revs); + continue; + } + if (!prefixcmp(arg, "--pretty=")) { revs->verbose_header = 1; - revs->commit_format = get_commit_format(arg+8); + 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")) { @@@ -1321,6 -1311,11 +1322,11 @@@ 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) { @@@ -1406,19 -1401,31 +1412,41 @@@ if (revs->reverse && revs->reflog_info) die("cannot combine --reverse with --walk-reflogs"); - if (revs->parents && revs->children.name) ++ if (revs->rewrite_parents && revs->children.name) + die("cannot combine --parents and --children"); + + /* + * Limitations on the graph functionality + */ + if (revs->reverse && revs->graph) + die("cannot combine --reverse with --graph"); + + if (revs->reflog_info && revs->graph) + die("cannot combine --walk-reflogs with --graph"); + 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; @@@ -1447,6 -1454,8 +1475,8 @@@ return -1; if (revs->topo_order) sort_in_topological_order(&revs->commits, revs->lifo); + if (revs->children.name) + set_children(revs); return 0; } @@@ -1524,6 -1533,11 +1554,11 @@@ static int commit_match(struct commit * commit->buffer, strlen(commit->buffer)); } + static inline int want_ancestry(struct rev_info *revs) + { - return (revs->parents || revs->children.name); ++ return (revs->rewrite_parents || revs->children.name); + } + enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit) { if (commit->object.flags & SHOWN) @@@ -1544,13 -1558,13 +1579,13 @@@ /* 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; @@@ -1617,62 -1631,28 +1652,62 @@@ static void gc_boundary(struct object_a } } -struct commit *get_revision(struct rev_info *revs) +static void create_boundary_commit_list(struct rev_info *revs) +{ + unsigned i; + struct commit *c; + struct object_array *array = &revs->boundary_commits; + struct object_array_entry *objects = array->objects; + + /* + * If revs->commits is non-NULL at this point, an error occurred in + * get_revision_1(). Ignore the error and continue printing the + * boundary commits anyway. (This is what the code has always + * done.) + */ + if (revs->commits) { + free_commit_list(revs->commits); + revs->commits = NULL; + } + + /* + * Put all of the actual boundary commits from revs->boundary_commits + * into revs->commits + */ + for (i = 0; i < array->nr; i++) { + c = (struct commit *)(objects[i].item); + if (!c) + continue; + if (!(c->object.flags & CHILD_SHOWN)) + continue; + if (c->object.flags & (SHOWN | BOUNDARY)) + continue; + c->object.flags |= BOUNDARY; + commit_list_insert(c, &revs->commits); + } + + /* + * If revs->topo_order is set, sort the boundary commits + * in topological order + */ + sort_in_topological_order(&revs->commits, revs->lifo); +} + +static struct commit *get_revision_internal(struct rev_info *revs) { struct commit *c = NULL; struct commit_list *l; if (revs->boundary == 2) { - unsigned i; - struct object_array *array = &revs->boundary_commits; - struct object_array_entry *objects = array->objects; - for (i = 0; i < array->nr; i++) { - c = (struct commit *)(objects[i].item); - if (!c) - continue; - if (!(c->object.flags & CHILD_SHOWN)) - continue; - if (!(c->object.flags & SHOWN)) - break; - } - if (array->nr <= i) - return NULL; - - c->object.flags |= SHOWN | BOUNDARY; + /* + * All of the normal commits have already been returned, + * and we are now returning boundary commits. + * create_boundary_commit_list() has populated + * revs->commits with the remaining commits to return. + */ + c = pop_commit(&revs->commits); + if (c) + c->object.flags |= SHOWN; return c; } @@@ -1736,14 -1716,7 +1771,14 @@@ * switch to boundary commits output mode. */ revs->boundary = 2; - return get_revision(revs); + + /* + * Update revs->commits to contain the list of + * boundary commits. + */ + create_boundary_commit_list(revs); + + return get_revision_internal(revs); } /* @@@ -1765,11 -1738,3 +1800,11 @@@ return c; } + +struct commit *get_revision(struct rev_info *revs) +{ + struct commit *c = get_revision_internal(revs); + if (c && revs->graph) + graph_update(revs->graph, c); + return c; +} diff --combined revision.h index abce5001f1,966116cd5b..dcf08e089a --- a/revision.h +++ b/revision.h @@@ -46,8 -46,7 +46,8 @@@ struct rev_info unpacked:1, /* see also ignore_packed below */ boundary:2, left_right:1, - parents:1, + rewrite_parents:1, + print_parents:1, reverse:1, cherry_pick:1, first_parent_only:1; @@@ -65,9 -64,7 +65,9 @@@ /* Format info */ unsigned int shown_one:1, - abbrev_commit:1; + abbrev_commit:1, + use_terminator:1, + missing_newline:1; enum date_mode date_mode; const char **ignore_packed; /* pretend objects in these are unpacked */ @@@ -90,9 -87,6 +90,9 @@@ /* Filter by commit log message */ struct grep_opt *grep_filter; + /* Display history graph */ + struct git_graph *graph; + /* special limits */ int skip_count; int max_count; @@@ -104,6 -98,7 +104,7 @@@ struct diff_options pruning; struct reflog_walk_info *reflog_info; + struct decoration children; }; #define REV_TREE_SAME 0