int main(int ac, char **av) { struct commit *rev[MAX_REVS], *commit; struct commit_list *list = NULL, *seen = NULL; unsigned int rev_mask[MAX_REVS]; int num_rev, i, extra = 0; int all_heads = 0, all_tags = 0; int all_mask, all_revs; char head_path[128]; const char *head_path_p; int head_path_len; unsigned char head_sha1[20]; int merge_base = 0; int independent = 0; int no_name = 0; int sha1_name = 0; int shown_merge_point = 0; int topo_order = 0; setup_git_directory(); while (1 < ac && av[1][0] == '-') { char *arg = av[1]; if (!strcmp(arg, "--all")) all_heads = all_tags = 1; else if (!strcmp(arg, "--heads")) all_heads = 1; else if (!strcmp(arg, "--tags")) all_tags = 1; else if (!strcmp(arg, "--more")) extra = 1; else if (!strcmp(arg, "--list")) extra = -1; else if (!strcmp(arg, "--no-name")) no_name = 1; else if (!strcmp(arg, "--sha1-name")) sha1_name = 1; else if (!strncmp(arg, "--more=", 7)) extra = atoi(arg + 7); else if (!strcmp(arg, "--merge-base")) merge_base = 1; else if (!strcmp(arg, "--independent")) independent = 1; else if (!strcmp(arg, "--topo-order")) topo_order = 1; else usage(show_branch_usage); ac--; av++; } ac--; av++; /* Only one of these is allowed */ if (1 < independent + merge_base + (extra != 0)) usage(show_branch_usage); if (all_heads + all_tags) snarf_refs(all_heads, all_tags); if (ac) { while (0 < ac) { append_one_rev(*av); ac--; av++; } } else { /* If no revs given, then add heads */ snarf_refs(1, 0); } if (!ref_name_cnt) { fprintf(stderr, "No revs to be shown.\n"); exit(0); } for (num_rev = 0; ref_name[num_rev]; num_rev++) { unsigned char revkey[20]; unsigned int flag = 1u << (num_rev + REV_SHIFT); if (MAX_REVS <= num_rev) die("cannot handle more than %d revs.", MAX_REVS); if (get_sha1(ref_name[num_rev], revkey)) die("'%s' is not a valid ref.\n", ref_name[num_rev]); commit = lookup_commit_reference(revkey); if (!commit) die("cannot find commit %s (%s)", ref_name[num_rev], 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) 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); head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1); if (head_path_p) { head_path_len = strlen(head_path_p); memcpy(head_path, head_path_p, head_path_len + 1); } else { head_path_len = 0; head_path[0] = 0; } 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_path, head_path_len, ref_name[i], head_sha1, rev[i]->object.sha1); if (extra < 0) printf("%c [%s] ", is_head ? '*' : ' ', ref_name[i]); else { for (j = 0; j < i; j++) putchar(' '); printf("%c [%s] ", is_head ? '*' : '!', ref_name[i]); } /* header lines never need name */ show_one_commit(rev[i], 1); } if (0 <= extra) { for (i = 0; i < num_rev; i++) putchar('-'); putchar('\n'); } } if (extra < 0) exit(0); /* Sort topologically */ if (topo_order) sort_in_topological_order(&seen); /* 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_one_commit(&seen); int this_flag = commit->object.flags; shown_merge_point |= ((this_flag & all_revs) == all_revs); if (1 < num_rev) { for (i = 0; i < num_rev; i++) putchar((this_flag & (1u << (i + REV_SHIFT))) ? '+' : ' '); putchar(' '); } show_one_commit(commit, no_name); if (shown_merge_point && --extra < 0) break; } return 0; }
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; int lifo = 1; char head[128]; const char *head_p; int head_len; unsigned char head_sha1[20]; 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_BOOLEAN('a', "all", &all_heads, "show remote-tracking and local branches"), OPT_BOOLEAN('r', "remotes", &all_remotes, "show remote-tracking branches"), OPT__COLOR(&showbranch_use_color, "color '*!+-' corresponding to the branch"), { OPTION_INTEGER, 0, "more", &extra, "n", "show <n> more commits after the common ancestor", PARSE_OPT_OPTARG, NULL, (intptr_t)1 }, OPT_SET_INT(0, "list", &extra, "synonym to more=-1", -1), OPT_BOOLEAN(0, "no-name", &no_name, "suppress naming strings"), OPT_BOOLEAN(0, "current", &with_current_branch, "include the current branch"), OPT_BOOLEAN(0, "sha1-name", &sha1_name, "name commits with their object names"), OPT_BOOLEAN(0, "merge-base", &merge_base, "show possible merge bases"), OPT_BOOLEAN(0, "independent", &independent, "show refs unreachable from any other ref"), OPT_BOOLEAN(0, "topo-order", &lifo, "show commits in topological order"), OPT_BOOLEAN(0, "topics", &topics, "show only commits not on the first branch"), OPT_SET_INT(0, "sparse", &dense, "show merges reachable from only one tip", 0), OPT_SET_INT(0, "date-order", &lifo, "show commits where no parent comes before its " "children", 0), { OPTION_CALLBACK, 'g', "reflog", &reflog_base, "<n>[,<base>]", "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 (showbranch_use_color == -1) showbranch_use_color = git_use_color_default; /* If nothing is specified, try the default first */ if (ac == 1 && default_num) { ac = default_num; av = default_arg; } 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 + all_heads + all_remotes == 0) all_heads = 1; if (reflog) { unsigned char sha1[20]; char nth_desc[256]; char *ref; int base = 0; if (ac == 0) { static const char *fake_av[2]; const char *refname; refname = resolve_ref("HEAD", sha1, 1, NULL); fake_av[0] = xstrdup(refname); fake_av[1] = NULL; av = fake_av; ac = 1; } if (ac != 1) die("--reflog option needs one branch name"); if (MAX_REVS < reflog) die("Only %d entries can be shown at one time.", MAX_REVS); if (!dwim_ref(*av, strlen(*av), sha1, &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... */ unsigned long at; at = approxidate(reflog_base); read_ref_at(ref, at, -1, sha1, NULL, NULL, NULL, &base); } } for (i = 0; i < reflog; i++) { char *logmsg, *m; const char *msg; unsigned long timestamp; int tz; if (read_ref_at(ref, 0, base+i, sha1, &logmsg, ×tamp, &tz, NULL)) { reflog = i; break; } msg = strchr(logmsg, '\t'); if (!msg) msg = "(none)"; else msg++; m = xmalloc(strlen(msg) + 200); sprintf(m, "(%s) %s", show_date(timestamp, tz, 1), msg); reflog_msg[i] = m; free(logmsg); sprintf(nth_desc, "%s@{%d}", *av, base+i); append_ref(nth_desc, sha1, 1); } } else if (all_heads + all_remotes) snarf_refs(all_heads, all_remotes); else { while (0 < ac) { append_one_rev(*av); ac--; av++; } } head_p = resolve_ref("HEAD", head_sha1, 1, NULL); if (head_p) { head_len = strlen(head_p); memcpy(head, head_p, head_len + 1); } else { head_len = 0; head[0] = 0; } if (with_current_branch && head_p) { 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, head_len, ref_name[i], head_sha1, NULL)) has_head++; } if (!has_head) { int offset = !prefixcmp(head, "refs/heads/") ? 11 : 0; append_one_rev(head + offset); } } if (!ref_name_cnt) { fprintf(stderr, "No revs to be shown.\n"); exit(0); } for (num_rev = 0; ref_name[num_rev]; num_rev++) { unsigned char revkey[20]; unsigned int flag = 1u << (num_rev + REV_SHIFT); if (MAX_REVS <= num_rev) die("cannot handle more than %d revs.", MAX_REVS); if (get_sha1(ref_name[num_rev], revkey)) 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], 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, head_len, ref_name[i], head_sha1, rev[i]->object.sha1); 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 % COLUMN_COLORS_MAX), 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, lifo); /* 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_one_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 % COLUMN_COLORS_MAX), mark, get_color_reset_code()); } putchar(' '); } show_one_commit(commit, no_name); if (shown_merge_point && --extra < 0) break; } return 0; }
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; int lifo = 1; char head[128]; const char *head_p; int head_len; unsigned char head_sha1[20]; 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; int reflog = 0; const char *reflog_base = NULL; git_config(git_show_branch_config); /* If nothing is specified, try the default first */ if (ac == 1 && default_num) { ac = default_num + 1; av = default_arg - 1; /* ick; we would not address av[0] */ } while (1 < ac && av[1][0] == '-') { const char *arg = av[1]; if (!strcmp(arg, "--")) { ac--; av++; break; } else if (!strcmp(arg, "--all") || !strcmp(arg, "-a")) all_heads = all_remotes = 1; else if (!strcmp(arg, "--remotes") || !strcmp(arg, "-r")) all_remotes = 1; else if (!strcmp(arg, "--more")) extra = 1; else if (!strcmp(arg, "--list")) extra = -1; else if (!strcmp(arg, "--no-name")) no_name = 1; else if (!strcmp(arg, "--current")) with_current_branch = 1; else if (!strcmp(arg, "--sha1-name")) sha1_name = 1; else if (!prefixcmp(arg, "--more=")) extra = atoi(arg + 7); else if (!strcmp(arg, "--merge-base")) merge_base = 1; else if (!strcmp(arg, "--independent")) independent = 1; else if (!strcmp(arg, "--topo-order")) lifo = 1; else if (!strcmp(arg, "--topics")) topics = 1; else if (!strcmp(arg, "--sparse")) dense = 0; else if (!strcmp(arg, "--date-order")) lifo = 0; else if (!strcmp(arg, "--reflog") || !strcmp(arg, "-g")) { reflog = DEFAULT_REFLOG; } else if (!prefixcmp(arg, "--reflog=")) parse_reflog_param(arg + 9, &reflog, &reflog_base); else if (!prefixcmp(arg, "-g=")) parse_reflog_param(arg + 3, &reflog, &reflog_base); else usage(show_branch_usage); ac--; av++; } ac--; av++; if (extra || reflog) { /* "listing" mode is incompatible with * independent nor merge-base modes. */ if (independent || merge_base) usage(show_branch_usage); 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. */ usage(show_branch_usage_reflog); } /* If nothing is specified, show all branches by default */ if (ac + all_heads + all_remotes == 0) all_heads = 1; if (reflog) { unsigned char sha1[20]; char nth_desc[256]; char *ref; int base = 0; if (ac == 0) { static const char *fake_av[2]; const char *refname; refname = resolve_ref("HEAD", sha1, 1, NULL); fake_av[0] = xstrdup(refname); fake_av[1] = NULL; av = fake_av; ac = 1; } if (ac != 1) die("--reflog option needs one branch name"); if (MAX_REVS < reflog) die("Only %d entries can be shown at one time.", MAX_REVS); if (!dwim_ref(*av, strlen(*av), sha1, &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... */ unsigned long at; at = approxidate(reflog_base); read_ref_at(ref, at, -1, sha1, NULL, NULL, NULL, &base); } } for (i = 0; i < reflog; i++) { char *logmsg, *m; const char *msg; unsigned long timestamp; int tz; if (read_ref_at(ref, 0, base+i, sha1, &logmsg, ×tamp, &tz, NULL)) { reflog = i; break; } msg = strchr(logmsg, '\t'); if (!msg) msg = "(none)"; else msg++; m = xmalloc(strlen(msg) + 200); sprintf(m, "(%s) %s", show_date(timestamp, tz, 1), msg); reflog_msg[i] = m; free(logmsg); sprintf(nth_desc, "%s@{%d}", *av, base+i); append_ref(nth_desc, sha1, 1); } } else if (all_heads + all_remotes) snarf_refs(all_heads, all_remotes); else { while (0 < ac) { append_one_rev(*av); ac--; av++; } } head_p = resolve_ref("HEAD", head_sha1, 1, NULL); if (head_p) { head_len = strlen(head_p); memcpy(head, head_p, head_len + 1); } else { head_len = 0; head[0] = 0; } if (with_current_branch && head_p) { 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, head_len, ref_name[i], head_sha1, NULL)) has_head++; } if (!has_head) { int pfxlen = strlen("refs/heads/"); append_one_rev(head + pfxlen); } } if (!ref_name_cnt) { fprintf(stderr, "No revs to be shown.\n"); exit(0); } for (num_rev = 0; ref_name[num_rev]; num_rev++) { unsigned char revkey[20]; unsigned int flag = 1u << (num_rev + REV_SHIFT); if (MAX_REVS <= num_rev) die("cannot handle more than %d revs.", MAX_REVS); if (get_sha1(ref_name[num_rev], revkey)) 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], 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) 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); 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, head_len, ref_name[i], head_sha1, rev[i]->object.sha1); if (extra < 0) printf("%c [%s] ", is_head ? '*' : ' ', ref_name[i]); else { for (j = 0; j < i; j++) putchar(' '); printf("%c [%s] ", is_head ? '*' : '!', 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, lifo); /* 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_one_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 = '+'; putchar(mark); } putchar(' '); } show_one_commit(commit, no_name); if (shown_merge_point && --extra < 0) break; } return 0; }