static int reachable(struct commit *want) { struct commit_list *work = NULL; commit_list_insert_by_date(want, &work); while (work) { struct commit_list *list; struct commit *commit = pop_commit(&work); if (commit->object.flags & THEY_HAVE) { want->object.flags |= COMMON_KNOWN; break; } if (!commit->object.parsed) parse_object(commit->object.oid.hash); if (commit->object.flags & REACHABLE) continue; commit->object.flags |= REACHABLE; if (commit->date < oldest_have) continue; for (list = commit->parents; list; list = list->next) { struct commit *parent = list->item; if (!(parent->object.flags & REACHABLE)) commit_list_insert_by_date(parent, &work); } } want->object.flags |= REACHABLE; clear_commit_marks(want, REACHABLE); free_commit_list(work); return (want->object.flags & COMMON_KNOWN); }
static struct commit_list *reduce_parents(struct commit *head_commit, int *head_subsumed, struct commit_list *remoteheads) { struct commit_list *parents, **remotes; /* * Is the current HEAD reachable from another commit being * merged? If so we do not want to record it as a parent of * the resulting merge, unless --no-ff is given. We will flip * this variable to 0 when we find HEAD among the independent * tips being merged. */ *head_subsumed = 1; /* Find what parents to record by checking independent ones. */ parents = reduce_heads(remoteheads); remoteheads = NULL; remotes = &remoteheads; while (parents) { struct commit *commit = pop_commit(&parents); if (commit == head_commit) *head_subsumed = 0; else remotes = &commit_list_insert(commit, remotes)->next; } return remoteheads; }
static unsigned long finish_depth_computation( struct commit_list **list, struct possible_tag *best) { unsigned long seen_commits = 0; while (*list) { struct commit *c = pop_commit(list); struct commit_list *parents = c->parents; seen_commits++; if (c->object.flags & best->flag_within) { struct commit_list *a = *list; while (a) { struct commit *i = a->item; if (!(i->object.flags & best->flag_within)) break; a = a->next; } if (!a) break; } else best->depth++; while (parents) { struct commit *p = parents->item; parse_commit(p); if (!(p->object.flags & SEEN)) commit_list_insert_by_date(p, list); p->object.flags |= c->object.flags; parents = parents->next; } } return seen_commits; }
/* * Starting from commits in the cb->mark_list, mark commits that are * reachable from them. Stop the traversal at commits older than * the expire_limit and queue them back, so that the caller can call * us again to restart the traversal with longer expire_limit. */ static void mark_reachable(struct expire_reflog_policy_cb *cb) { struct commit_list *pending; timestamp_t expire_limit = cb->mark_limit; struct commit_list *leftover = NULL; for (pending = cb->mark_list; pending; pending = pending->next) pending->item->object.flags &= ~REACHABLE; pending = cb->mark_list; while (pending) { struct commit_list *parent; struct commit *commit = pop_commit(&pending); if (commit->object.flags & REACHABLE) continue; if (parse_commit(commit)) continue; commit->object.flags |= REACHABLE; if (commit->date < expire_limit) { commit_list_insert(commit, &leftover); continue; } commit->object.flags |= REACHABLE; parent = commit->parents; while (parent) { commit = parent->item; parent = parent->next; if (commit->object.flags & REACHABLE) continue; commit_list_insert(commit, &pending); } } cb->mark_list = leftover; }
static int try_difference(const char *arg) { char *dotdot; struct object_id start_oid; struct object_id end_oid; const char *end; const char *start; int symmetric; static const char head_by_default[] = "HEAD"; if (!(dotdot = strstr(arg, ".."))) return 0; end = dotdot + 2; start = arg; symmetric = (*end == '.'); *dotdot = 0; end += symmetric; if (!*end) end = head_by_default; if (dotdot == arg) start = head_by_default; if (start == head_by_default && end == head_by_default && !symmetric) { /* * Just ".."? That is not a range but the * pathspec for the parent directory. */ *dotdot = '.'; return 0; } if (!get_oid_committish(start, &start_oid) && !get_oid_committish(end, &end_oid)) { show_rev(NORMAL, &end_oid, end); show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start); if (symmetric) { struct commit_list *exclude; struct commit *a, *b; a = lookup_commit_reference(the_repository, &start_oid); b = lookup_commit_reference(the_repository, &end_oid); if (!a || !b) { *dotdot = '.'; return 0; } exclude = get_merge_bases(a, b); while (exclude) { struct commit *commit = pop_commit(&exclude); show_rev(REVERSED, &commit->object.oid, NULL); } } *dotdot = '.'; return 1; } *dotdot = '.'; return 0; }
static int try_difference(const char *arg) { char *dotdot; unsigned char sha1[20]; unsigned char end[20]; const char *next; const char *this; int symmetric; static const char head_by_default[] = "HEAD"; if (!(dotdot = strstr(arg, ".."))) return 0; next = dotdot + 2; this = arg; symmetric = (*next == '.'); *dotdot = 0; next += symmetric; if (!*next) next = head_by_default; if (dotdot == arg) this = head_by_default; if (this == head_by_default && next == head_by_default && !symmetric) { /* * Just ".."? That is not a range but the * pathspec for the parent directory. */ *dotdot = '.'; return 0; } if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) { show_rev(NORMAL, end, next); show_rev(symmetric ? NORMAL : REVERSED, sha1, this); if (symmetric) { struct commit_list *exclude; struct commit *a, *b; a = lookup_commit_reference(sha1); b = lookup_commit_reference(end); exclude = get_merge_bases(a, b); while (exclude) { struct commit *commit = pop_commit(&exclude); show_rev(REVERSED, commit->object.oid.hash, NULL); } } *dotdot = '.'; return 1; } *dotdot = '.'; return 0; }
static int show_merge_base(struct commit_list *seen, int num_rev) { int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); int all_revs = all_mask & ~((1u << REV_SHIFT) - 1); int exit_status = 1; while (seen) { struct commit *commit = pop_commit(&seen); int flags = commit->object.flags & all_mask; if (!(flags & UNINTERESTING) && ((flags & all_revs) == all_revs)) { puts(oid_to_hex(&commit->object.oid)); exit_status = 0; commit->object.flags |= UNINTERESTING; } } return exit_status; }
struct commit *get_revision(struct rev_info *revs) { struct commit *c; struct commit_list *reversed; if (revs->reverse) { reversed = NULL; while ((c = get_revision_internal(revs))) { commit_list_insert(c, &reversed); } revs->commits = reversed; revs->reverse = 0; revs->reverse_output_stage = 1; } if (revs->reverse_output_stage) return pop_commit(&revs->commits); c = get_revision_internal(revs); if (c && revs->graph) graph_update(revs->graph, c); return c; }
static void describe(const char *arg, int last_one) { unsigned char sha1[20]; struct commit *cmit, *gave_up_on = NULL; struct commit_list *list; struct commit_name *n; struct possible_tag all_matches[MAX_TAGS]; unsigned int match_cnt = 0, annotated_cnt = 0, cur_match; unsigned long seen_commits = 0; unsigned int unannotated_cnt = 0; if (get_sha1(arg, sha1)) die(_("Not a valid object name %s"), arg); cmit = lookup_commit_reference(sha1); if (!cmit) die(_("%s is not a valid '%s' object"), arg, commit_type); n = find_commit_name(cmit->object.sha1); if (n && (tags || all || n->prio == 2)) { /* * Exact match to an existing ref. */ display_name(n); if (longformat) show_suffix(0, n->tag ? n->tag->tagged->sha1 : sha1); if (dirty) printf("%s", dirty); printf("\n"); return; } if (!max_candidates) die(_("no tag exactly matches '%s'"), sha1_to_hex(cmit->object.sha1)); if (debug) fprintf(stderr, _("searching to describe %s\n"), arg); if (!have_util) { for_each_hash(&names, set_util, NULL); have_util = 1; } list = NULL; cmit->object.flags = SEEN; commit_list_insert(cmit, &list); while (list) { struct commit *c = pop_commit(&list); struct commit_list *parents = c->parents; seen_commits++; n = c->util; if (n) { if (!tags && !all && n->prio < 2) { unannotated_cnt++; } else if (match_cnt < max_candidates) { struct possible_tag *t = &all_matches[match_cnt++]; t->name = n; t->depth = seen_commits - 1; t->flag_within = 1u << match_cnt; t->found_order = match_cnt; c->object.flags |= t->flag_within; if (n->prio == 2) annotated_cnt++; } else { gave_up_on = c; break; } } for (cur_match = 0; cur_match < match_cnt; cur_match++) { struct possible_tag *t = &all_matches[cur_match]; if (!(c->object.flags & t->flag_within)) t->depth++; } if (annotated_cnt && !list) { if (debug) fprintf(stderr, _("finished search at %s\n"), sha1_to_hex(c->object.sha1)); break; } while (parents) { struct commit *p = parents->item; parse_commit(p); if (!(p->object.flags & SEEN)) commit_list_insert_by_date(p, &list); p->object.flags |= c->object.flags; parents = parents->next; } } if (!match_cnt) { const unsigned char *sha1 = cmit->object.sha1; if (always) { printf("%s", find_unique_abbrev(sha1, abbrev)); if (dirty) printf("%s", dirty); printf("\n"); return; } if (unannotated_cnt) die(_("No annotated tags can describe '%s'.\n" "However, there were unannotated tags: try --tags."), sha1_to_hex(sha1)); else die(_("No tags can describe '%s'.\n" "Try --always, or create some tags."), sha1_to_hex(sha1)); } qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt); if (gave_up_on) { commit_list_insert_by_date(gave_up_on, &list); seen_commits--; } seen_commits += finish_depth_computation(&list, &all_matches[0]); free_commit_list(list); if (debug) { for (cur_match = 0; cur_match < match_cnt; cur_match++) { struct possible_tag *t = &all_matches[cur_match]; fprintf(stderr, " %-11s %8d %s\n", prio_names[t->name->prio], t->depth, t->name->path); } fprintf(stderr, _("traversed %lu commits\n"), seen_commits); if (gave_up_on) { fprintf(stderr, _("more than %i tags found; listed %i most recent\n" "gave up search at %s\n"), max_candidates, max_candidates, sha1_to_hex(gave_up_on->object.sha1)); } } display_name(all_matches[0].name); if (abbrev) show_suffix(all_matches[0].depth, cmit->object.sha1); if (dirty) printf("%s", dirty); printf("\n"); if (!last_one) clear_commit_marks(cmit, -1); }
static struct commit *get_revision_internal(struct rev_info *revs) { struct commit *c = NULL; struct commit_list *l; if (revs->boundary == 2) { /* * 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; } /* * Now pick up what they want to give us */ c = get_revision_1(revs); if (c) { while (0 < revs->skip_count) { revs->skip_count--; c = get_revision_1(revs); if (!c) break; } } /* * Check the max_count. */ switch (revs->max_count) { case -1: break; case 0: c = NULL; break; default: revs->max_count--; } if (c) c->object.flags |= SHOWN; if (!revs->boundary) { return c; } if (!c) { /* * get_revision_1() runs out the commits, and * we are done computing the boundaries. * switch to boundary commits output mode. */ revs->boundary = 2; /* * Update revs->commits to contain the list of * boundary commits. */ create_boundary_commit_list(revs); return get_revision_internal(revs); } /* * boundary commits are the commits that are parents of the * ones we got from get_revision_1() but they themselves are * not returned from get_revision_1(). Before returning * 'c', we need to mark its parents that they could be boundaries. */ for (l = c->parents; l; l = l->next) { struct object *p; p = &(l->item->object); if (p->flags & (CHILD_SHOWN | SHOWN)) continue; p->flags |= CHILD_SHOWN; gc_boundary(&revs->boundary_commits); add_object_array(p, NULL, &revs->boundary_commits); } return c; }
/* * Merge the commits h1 and h2, return the resulting virtual * commit object and a flag indicating the cleanness of the merge. */ int merge_recursive(struct merge_options *o, struct commit *h1, struct commit *h2, struct commit_list *ca, struct commit **result) { struct commit_list *iter; struct commit *merged_common_ancestors; struct tree *mrtree = mrtree; int clean; if (show(o, 4)) { output(o, 4, "Merging:"); output_commit_title(o, h1); output_commit_title(o, h2); } if (!ca) { ca = get_merge_bases(h1, h2, 1); ca = reverse_commit_list(ca); } if (show(o, 5)) { output(o, 5, "found %u common ancestor(s):", commit_list_count(ca)); for (iter = ca; iter; iter = iter->next) output_commit_title(o, iter->item); } merged_common_ancestors = pop_commit(&ca); if (merged_common_ancestors == NULL) { /* if there is no common ancestor, make an empty tree */ struct tree *tree = xcalloc(1, sizeof(struct tree)); tree->object.parsed = 1; tree->object.type = OBJ_TREE; pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1); merged_common_ancestors = make_virtual_commit(tree, "ancestor"); } for (iter = ca; iter; iter = iter->next) { const char *saved_b1, *saved_b2; o->call_depth++; /* * When the merge fails, the result contains files * with conflict markers. The cleanness flag is * ignored, it was never actually used, as result of * merge_trees has always overwritten it: the committed * "conflicts" were already resolved. */ discard_cache(); saved_b1 = o->branch1; saved_b2 = o->branch2; o->branch1 = "Temporary merge branch 1"; o->branch2 = "Temporary merge branch 2"; merge_recursive(o, merged_common_ancestors, iter->item, NULL, &merged_common_ancestors); o->branch1 = saved_b1; o->branch2 = saved_b2; o->call_depth--; if (!merged_common_ancestors) die("merge returned no commit"); } discard_cache(); if (!o->call_depth) read_cache(); clean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree, &mrtree); if (o->call_depth) { *result = make_virtual_commit(mrtree, "merged tree"); commit_list_insert(h1, &(*result)->parents); commit_list_insert(h2, &(*result)->parents->next); } flush_output(o); return clean; }
/* * Performs an in-place topological sort on the list supplied. */ void sort_in_topological_order(struct commit_list ** list) { struct commit_list * next = *list; struct commit_list * work = NULL, **insert; struct commit_list ** pptr = list; struct sort_node * nodes; struct sort_node * next_nodes; int count = 0; /* determine the size of the list */ while (next) { next = next->next; count++; } /* allocate an array to help sort the list */ nodes = xcalloc(count, sizeof(*nodes)); /* link the list to the array */ next_nodes = nodes; next=*list; while (next) { next_nodes->list_item = next; next->item->object.util = next_nodes; next_nodes++; next = next->next; } /* update the indegree */ next=*list; while (next) { struct commit_list * parents = next->item->parents; while (parents) { struct commit * parent=parents->item; struct sort_node * pn = (struct sort_node *)parent->object.util; if (pn) pn->indegree++; parents=parents->next; } next=next->next; } /* * find the tips * * tips are nodes not reachable from any other node in the list * * the tips serve as a starting set for the work queue. */ next=*list; insert = &work; while (next) { struct sort_node * node = (struct sort_node *)next->item->object.util; if (node->indegree == 0) { insert = &commit_list_insert(next->item, insert)->next; } next=next->next; } /* process the list in topological order */ while (work) { struct commit * work_item = pop_commit(&work); struct sort_node * work_node = (struct sort_node *)work_item->object.util; struct commit_list * parents = work_item->parents; while (parents) { struct commit * parent=parents->item; struct sort_node * pn = (struct sort_node *)parent->object.util; if (pn) { /* * parents are only enqueued for emission * when all their children have been emitted thereby * guaranteeing topological order. */ pn->indegree--; if (!pn->indegree) commit_list_insert(parent, &work); } parents=parents->next; } /* * work_item is a commit all of whose children * have already been emitted. we can emit it now. */ *pptr = work_node->list_item; pptr = &(*pptr)->next; *pptr = NULL; work_item->object.util = NULL; } free(nodes); }
int cmd_show_branch(int ac, const char **av, const char *prefix) { struct commit *rev[MAX_REVS], *commit; char *reflog_msg[MAX_REVS]; struct commit_list *list = NULL, *seen = NULL; unsigned int rev_mask[MAX_REVS]; int num_rev, i, extra = 0; int all_heads = 0, all_remotes = 0; int all_mask, all_revs; enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER; char *head; struct object_id head_oid; int merge_base = 0; int independent = 0; int no_name = 0; int sha1_name = 0; int shown_merge_point = 0; int with_current_branch = 0; int head_at = -1; int topics = 0; int dense = 1; const char *reflog_base = NULL; struct option builtin_show_branch_options[] = { OPT_BOOL('a', "all", &all_heads, N_("show remote-tracking and local branches")), OPT_BOOL('r', "remotes", &all_remotes, N_("show remote-tracking branches")), OPT__COLOR(&showbranch_use_color, N_("color '*!+-' corresponding to the branch")), { OPTION_INTEGER, 0, "more", &extra, N_("n"), N_("show <n> more commits after the common ancestor"), PARSE_OPT_OPTARG, NULL, (intptr_t)1 }, OPT_SET_INT(0, "list", &extra, N_("synonym to more=-1"), -1), OPT_BOOL(0, "no-name", &no_name, N_("suppress naming strings")), OPT_BOOL(0, "current", &with_current_branch, N_("include the current branch")), OPT_BOOL(0, "sha1-name", &sha1_name, N_("name commits with their object names")), OPT_BOOL(0, "merge-base", &merge_base, N_("show possible merge bases")), OPT_BOOL(0, "independent", &independent, N_("show refs unreachable from any other ref")), OPT_SET_INT(0, "topo-order", &sort_order, N_("show commits in topological order"), REV_SORT_IN_GRAPH_ORDER), OPT_BOOL(0, "topics", &topics, N_("show only commits not on the first branch")), OPT_SET_INT(0, "sparse", &dense, N_("show merges reachable from only one tip"), 0), OPT_SET_INT(0, "date-order", &sort_order, N_("topologically sort, maintaining date order " "where possible"), REV_SORT_BY_COMMIT_DATE), { OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"), N_("show <n> most recent ref-log entries starting at " "base"), PARSE_OPT_OPTARG | PARSE_OPT_LITERAL_ARGHELP, parse_reflog_param }, OPT_END() }; git_config(git_show_branch_config, NULL); /* If nothing is specified, try the default first */ if (ac == 1 && default_args.argc) { ac = default_args.argc; av = default_args.argv; } ac = parse_options(ac, av, prefix, builtin_show_branch_options, show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (all_heads) all_remotes = 1; if (extra || reflog) { /* "listing" mode is incompatible with * independent nor merge-base modes. */ if (independent || merge_base) usage_with_options(show_branch_usage, builtin_show_branch_options); if (reflog && ((0 < extra) || all_heads || all_remotes)) /* * Asking for --more in reflog mode does not * make sense. --list is Ok. * * Also --all and --remotes do not make sense either. */ die(_("--reflog is incompatible with --all, --remotes, " "--independent or --merge-base")); } /* If nothing is specified, show all branches by default */ if (ac <= topics && all_heads + all_remotes == 0) all_heads = 1; if (reflog) { struct object_id oid; char *ref; int base = 0; unsigned int flags = 0; if (ac == 0) { static const char *fake_av[2]; fake_av[0] = resolve_refdup("HEAD", RESOLVE_REF_READING, oid.hash, NULL); fake_av[1] = NULL; av = fake_av; ac = 1; if (!*av) die(_("no branches given, and HEAD is not valid")); } if (ac != 1) die(_("--reflog option needs one branch name")); if (MAX_REVS < reflog) die(Q_("only %d entry can be shown at one time.", "only %d entries can be shown at one time.", MAX_REVS), MAX_REVS); if (!dwim_ref(*av, strlen(*av), oid.hash, &ref)) die(_("no such ref %s"), *av); /* Has the base been specified? */ if (reflog_base) { char *ep; base = strtoul(reflog_base, &ep, 10); if (*ep) { /* Ah, that is a date spec... */ timestamp_t at; at = approxidate(reflog_base); read_ref_at(ref, flags, at, -1, oid.hash, NULL, NULL, NULL, &base); } } for (i = 0; i < reflog; i++) { char *logmsg; char *nth_desc; const char *msg; timestamp_t timestamp; int tz; if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg, ×tamp, &tz, NULL)) { reflog = i; break; } msg = strchr(logmsg, '\t'); if (!msg) msg = "(none)"; else msg++; reflog_msg[i] = xstrfmt("(%s) %s", show_date(timestamp, tz, DATE_MODE(RELATIVE)), msg); free(logmsg); nth_desc = xstrfmt("%s@{%d}", *av, base+i); append_ref(nth_desc, &oid, 1); free(nth_desc); } free(ref); } else { while (0 < ac) { append_one_rev(*av); ac--; av++; } if (all_heads + all_remotes) snarf_refs(all_heads, all_remotes); } head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL); if (with_current_branch && head) { int has_head = 0; for (i = 0; !has_head && i < ref_name_cnt; i++) { /* We are only interested in adding the branch * HEAD points at. */ if (rev_is_head(head, ref_name[i], head_oid.hash, NULL)) has_head++; } if (!has_head) { const char *name = head; skip_prefix(name, "refs/heads/", &name); append_one_rev(name); } } if (!ref_name_cnt) { fprintf(stderr, "No revs to be shown.\n"); exit(0); } for (num_rev = 0; ref_name[num_rev]; num_rev++) { struct object_id revkey; unsigned int flag = 1u << (num_rev + REV_SHIFT); if (MAX_REVS <= num_rev) die(Q_("cannot handle more than %d rev.", "cannot handle more than %d revs.", MAX_REVS), MAX_REVS); if (get_sha1(ref_name[num_rev], revkey.hash)) die(_("'%s' is not a valid ref."), ref_name[num_rev]); commit = lookup_commit_reference(&revkey); if (!commit) die(_("cannot find commit %s (%s)"), ref_name[num_rev], oid_to_hex(&revkey)); parse_commit(commit); mark_seen(commit, &seen); /* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1, * and so on. REV_SHIFT bits from bit 0 are used for * internal bookkeeping. */ commit->object.flags |= flag; if (commit->object.flags == flag) commit_list_insert_by_date(commit, &list); rev[num_rev] = commit; } for (i = 0; i < num_rev; i++) rev_mask[i] = rev[i]->object.flags; if (0 <= extra) join_revs(&list, &seen, num_rev, extra); commit_list_sort_by_date(&seen); if (merge_base) return show_merge_base(seen, num_rev); if (independent) return show_independent(rev, num_rev, ref_name, rev_mask); /* Show list; --more=-1 means list-only */ if (1 < num_rev || extra < 0) { for (i = 0; i < num_rev; i++) { int j; int is_head = rev_is_head(head, ref_name[i], head_oid.hash, rev[i]->object.oid.hash); if (extra < 0) printf("%c [%s] ", is_head ? '*' : ' ', ref_name[i]); else { for (j = 0; j < i; j++) putchar(' '); printf("%s%c%s [%s] ", get_color_code(i), is_head ? '*' : '!', get_color_reset_code(), ref_name[i]); } if (!reflog) { /* header lines never need name */ show_one_commit(rev[i], 1); } else puts(reflog_msg[i]); if (is_head) head_at = i; } if (0 <= extra) { for (i = 0; i < num_rev; i++) putchar('-'); putchar('\n'); } } if (extra < 0) exit(0); /* Sort topologically */ sort_in_topological_order(&seen, sort_order); /* Give names to commits */ if (!sha1_name && !no_name) name_commits(seen, rev, ref_name, num_rev); all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); all_revs = all_mask & ~((1u << REV_SHIFT) - 1); while (seen) { struct commit *commit = pop_commit(&seen); int this_flag = commit->object.flags; int is_merge_point = ((this_flag & all_revs) == all_revs); shown_merge_point |= is_merge_point; if (1 < num_rev) { int is_merge = !!(commit->parents && commit->parents->next); if (topics && !is_merge_point && (this_flag & (1u << REV_SHIFT))) continue; if (dense && is_merge && omit_in_dense(commit, rev, num_rev)) continue; for (i = 0; i < num_rev; i++) { int mark; if (!(this_flag & (1u << (i + REV_SHIFT)))) mark = ' '; else if (is_merge) mark = '-'; else if (i == head_at) mark = '*'; else mark = '+'; printf("%s%c%s", get_color_code(i), mark, get_color_reset_code()); } putchar(' '); } show_one_commit(commit, no_name); if (shown_merge_point && --extra < 0) break; } return 0; }
static void join_revs(struct commit_list **list_p, struct commit_list **seen_p, int num_rev, int extra) { int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); int all_revs = all_mask & ~((1u << REV_SHIFT) - 1); while (*list_p) { struct commit_list *parents; int still_interesting = !!interesting(*list_p); struct commit *commit = pop_commit(list_p); int flags = commit->object.flags & all_mask; if (!still_interesting && extra <= 0) break; mark_seen(commit, seen_p); if ((flags & all_revs) == all_revs) flags |= UNINTERESTING; parents = commit->parents; while (parents) { struct commit *p = parents->item; int this_flag = p->object.flags; parents = parents->next; if ((this_flag & flags) == flags) continue; parse_commit(p); if (mark_seen(p, seen_p) && !still_interesting) extra--; p->object.flags |= flags; commit_list_insert_by_date(p, list_p); } } /* * Postprocess to complete well-poisoning. * * At this point we have all the commits we have seen in * seen_p list. Mark anything that can be reached from * uninteresting commits not interesting. */ for (;;) { int changed = 0; struct commit_list *s; for (s = *seen_p; s; s = s->next) { struct commit *c = s->item; struct commit_list *parents; if (((c->object.flags & all_revs) != all_revs) && !(c->object.flags & UNINTERESTING)) continue; /* The current commit is either a merge base or * already uninteresting one. Mark its parents * as uninteresting commits _only_ if they are * already parsed. No reason to find new ones * here. */ parents = c->parents; while (parents) { struct commit *p = parents->item; parents = parents->next; if (!(p->object.flags & UNINTERESTING)) { p->object.flags |= UNINTERESTING; changed = 1; } } } if (!changed) break; } }