Git fork
at reftables-rust 1011 lines 25 kB view raw
1#define USE_THE_REPOSITORY_VARIABLE 2#define DISABLE_SIGN_COMPARE_WARNINGS 3 4#include "builtin.h" 5#include "config.h" 6#include "environment.h" 7#include "gettext.h" 8#include "hash.h" 9#include "hex.h" 10#include "pretty.h" 11#include "refs.h" 12#include "color.h" 13#include "strvec.h" 14#include "object-name.h" 15#include "parse-options.h" 16 17#include "dir.h" 18#include "commit-slab.h" 19#include "date.h" 20#include "wildmatch.h" 21 22static const char*const show_branch_usage[] = { 23 N_("git show-branch [-a | --all] [-r | --remotes] [--topo-order | --date-order]\n" 24 " [--current] [--color[=<when>] | --no-color] [--sparse]\n" 25 " [--more=<n> | --list | --independent | --merge-base]\n" 26 " [--no-name | --sha1-name] [--topics]\n" 27 " [(<rev> | <glob>)...]"), 28 N_("git show-branch (-g | --reflog)[=<n>[,<base>]] [--list] [<ref>]"), 29 NULL 30}; 31 32static enum git_colorbool showbranch_use_color = GIT_COLOR_UNKNOWN; 33 34static struct strvec default_args = STRVEC_INIT; 35 36/* 37 * TODO: convert this use of commit->object.flags to commit-slab 38 * instead to store a pointer to ref name directly. Then use the same 39 * UNINTERESTING definition from revision.h here. 40 */ 41#define UNINTERESTING 01 42 43#define REV_SHIFT 2 44#define MAX_REVS (FLAG_BITS - REV_SHIFT) /* should not exceed bits_per_int - REV_SHIFT */ 45 46#define DEFAULT_REFLOG 4 47 48static const char *get_color_code(int idx) 49{ 50 if (want_color(showbranch_use_color)) 51 return column_colors_ansi[idx % column_colors_ansi_max]; 52 return ""; 53} 54 55static const char *get_color_reset_code(void) 56{ 57 if (want_color(showbranch_use_color)) 58 return GIT_COLOR_RESET; 59 return ""; 60} 61 62static struct commit *interesting(struct commit_list *list) 63{ 64 while (list) { 65 struct commit *commit = list->item; 66 list = list->next; 67 if (commit->object.flags & UNINTERESTING) 68 continue; 69 return commit; 70 } 71 return NULL; 72} 73 74struct commit_name { 75 const char *head_name; /* which head's ancestor? */ 76 int generation; /* how many parents away from head_name */ 77}; 78 79define_commit_slab(commit_name_slab, struct commit_name *); 80static struct commit_name_slab name_slab; 81 82static struct commit_name *commit_to_name(struct commit *commit) 83{ 84 return *commit_name_slab_at(&name_slab, commit); 85} 86 87 88/* Name the commit as nth generation ancestor of head_name; 89 * we count only the first-parent relationship for naming purposes. 90 */ 91static void name_commit(struct commit *commit, const char *head_name, int nth) 92{ 93 struct commit_name *name; 94 95 name = *commit_name_slab_at(&name_slab, commit); 96 if (!name) { 97 name = xmalloc(sizeof(*name)); 98 *commit_name_slab_at(&name_slab, commit) = name; 99 } 100 name->head_name = head_name; 101 name->generation = nth; 102} 103 104/* Parent is the first parent of the commit. We may name it 105 * as (n+1)th generation ancestor of the same head_name as 106 * commit is nth generation ancestor of, if that generation 107 * number is better than the name it already has. 108 */ 109static void name_parent(struct commit *commit, struct commit *parent) 110{ 111 struct commit_name *commit_name = commit_to_name(commit); 112 struct commit_name *parent_name = commit_to_name(parent); 113 if (!commit_name) 114 return; 115 if (!parent_name || 116 commit_name->generation + 1 < parent_name->generation) 117 name_commit(parent, commit_name->head_name, 118 commit_name->generation + 1); 119} 120 121static int name_first_parent_chain(struct commit *c) 122{ 123 int i = 0; 124 while (c) { 125 struct commit *p; 126 if (!commit_to_name(c)) 127 break; 128 if (!c->parents) 129 break; 130 p = c->parents->item; 131 if (!commit_to_name(p)) { 132 name_parent(c, p); 133 i++; 134 } 135 else 136 break; 137 c = p; 138 } 139 return i; 140} 141 142static void name_commits(struct commit_list *list, 143 struct commit **rev, 144 char **ref_name, 145 int num_rev) 146{ 147 struct commit_list *cl; 148 struct commit *c; 149 int i; 150 151 /* First give names to the given heads */ 152 for (cl = list; cl; cl = cl->next) { 153 c = cl->item; 154 if (commit_to_name(c)) 155 continue; 156 for (i = 0; i < num_rev; i++) { 157 if (rev[i] == c) { 158 name_commit(c, ref_name[i], 0); 159 break; 160 } 161 } 162 } 163 164 /* Then commits on the first parent ancestry chain */ 165 do { 166 i = 0; 167 for (cl = list; cl; cl = cl->next) { 168 i += name_first_parent_chain(cl->item); 169 } 170 } while (i); 171 172 /* Finally, any unnamed commits */ 173 do { 174 i = 0; 175 for (cl = list; cl; cl = cl->next) { 176 struct commit_list *parents; 177 struct commit_name *n; 178 int nth; 179 c = cl->item; 180 if (!commit_to_name(c)) 181 continue; 182 n = commit_to_name(c); 183 parents = c->parents; 184 nth = 0; 185 while (parents) { 186 struct commit *p = parents->item; 187 struct strbuf newname = STRBUF_INIT; 188 parents = parents->next; 189 nth++; 190 if (commit_to_name(p)) 191 continue; 192 switch (n->generation) { 193 case 0: 194 strbuf_addstr(&newname, n->head_name); 195 break; 196 case 1: 197 strbuf_addf(&newname, "%s^", n->head_name); 198 break; 199 default: 200 strbuf_addf(&newname, "%s~%d", 201 n->head_name, n->generation); 202 break; 203 } 204 if (nth == 1) 205 strbuf_addch(&newname, '^'); 206 else 207 strbuf_addf(&newname, "^%d", nth); 208 name_commit(p, strbuf_detach(&newname, NULL), 0); 209 i++; 210 name_first_parent_chain(p); 211 } 212 } 213 } while (i); 214} 215 216static int mark_seen(struct commit *commit, struct commit_list **seen_p) 217{ 218 if (!commit->object.flags) { 219 commit_list_insert(commit, seen_p); 220 return 1; 221 } 222 return 0; 223} 224 225static void join_revs(struct commit_list **list_p, 226 struct commit_list **seen_p, 227 int num_rev, int extra) 228{ 229 int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); 230 int all_revs = all_mask & ~((1u << REV_SHIFT) - 1); 231 232 while (*list_p) { 233 struct commit_list *parents; 234 int still_interesting = !!interesting(*list_p); 235 struct commit *commit = pop_commit(list_p); 236 int flags = commit->object.flags & all_mask; 237 238 if (!still_interesting && extra <= 0) 239 break; 240 241 mark_seen(commit, seen_p); 242 if ((flags & all_revs) == all_revs) 243 flags |= UNINTERESTING; 244 parents = commit->parents; 245 246 while (parents) { 247 struct commit *p = parents->item; 248 int this_flag = p->object.flags; 249 parents = parents->next; 250 if ((this_flag & flags) == flags) 251 continue; 252 repo_parse_commit(the_repository, p); 253 if (mark_seen(p, seen_p) && !still_interesting) 254 extra--; 255 p->object.flags |= flags; 256 commit_list_insert_by_date(p, list_p); 257 } 258 } 259 260 /* 261 * Postprocess to complete well-poisoning. 262 * 263 * At this point we have all the commits we have seen in 264 * seen_p list. Mark anything that can be reached from 265 * uninteresting commits not interesting. 266 */ 267 for (;;) { 268 int changed = 0; 269 struct commit_list *s; 270 for (s = *seen_p; s; s = s->next) { 271 struct commit *c = s->item; 272 struct commit_list *parents; 273 274 if (((c->object.flags & all_revs) != all_revs) && 275 !(c->object.flags & UNINTERESTING)) 276 continue; 277 278 /* The current commit is either a merge base or 279 * already uninteresting one. Mark its parents 280 * as uninteresting commits _only_ if they are 281 * already parsed. No reason to find new ones 282 * here. 283 */ 284 parents = c->parents; 285 while (parents) { 286 struct commit *p = parents->item; 287 parents = parents->next; 288 if (!(p->object.flags & UNINTERESTING)) { 289 p->object.flags |= UNINTERESTING; 290 changed = 1; 291 } 292 } 293 } 294 if (!changed) 295 break; 296 } 297} 298 299static void show_one_commit(struct commit *commit, int no_name) 300{ 301 struct strbuf pretty = STRBUF_INIT; 302 const char *pretty_str = "(unavailable)"; 303 struct commit_name *name = commit_to_name(commit); 304 305 if (commit->object.parsed) { 306 pp_commit_easy(CMIT_FMT_ONELINE, commit, &pretty); 307 pretty_str = pretty.buf; 308 } 309 skip_prefix(pretty_str, "[PATCH] ", &pretty_str); 310 311 if (!no_name) { 312 if (name && name->head_name) { 313 printf("[%s", name->head_name); 314 if (name->generation) { 315 if (name->generation == 1) 316 printf("^"); 317 else 318 printf("~%d", name->generation); 319 } 320 printf("] "); 321 } 322 else 323 printf("[%s] ", 324 repo_find_unique_abbrev(the_repository, &commit->object.oid, 325 DEFAULT_ABBREV)); 326 } 327 puts(pretty_str); 328 strbuf_release(&pretty); 329} 330 331static char *ref_name[MAX_REVS + 1]; 332static int ref_name_cnt; 333 334static const char *find_digit_prefix(const char *s, int *v) 335{ 336 const char *p; 337 int ver; 338 char ch; 339 340 for (p = s, ver = 0; 341 '0' <= (ch = *p) && ch <= '9'; 342 p++) 343 ver = ver * 10 + ch - '0'; 344 *v = ver; 345 return p; 346} 347 348 349static int version_cmp(const char *a, const char *b) 350{ 351 while (1) { 352 int va, vb; 353 354 a = find_digit_prefix(a, &va); 355 b = find_digit_prefix(b, &vb); 356 if (va != vb) 357 return va - vb; 358 359 while (1) { 360 int ca = *a; 361 int cb = *b; 362 if ('0' <= ca && ca <= '9') 363 ca = 0; 364 if ('0' <= cb && cb <= '9') 365 cb = 0; 366 if (ca != cb) 367 return ca - cb; 368 if (!ca) 369 break; 370 a++; 371 b++; 372 } 373 if (!*a && !*b) 374 return 0; 375 } 376} 377 378static int compare_ref_name(const void *a_, const void *b_) 379{ 380 const char * const*a = a_, * const*b = b_; 381 return version_cmp(*a, *b); 382} 383 384static void sort_ref_range(int bottom, int top) 385{ 386 QSORT(ref_name + bottom, top - bottom, compare_ref_name); 387} 388 389static int append_ref(const char *refname, const struct object_id *oid, 390 int allow_dups) 391{ 392 struct commit *commit = lookup_commit_reference_gently(the_repository, 393 oid, 1); 394 int i; 395 396 if (!commit) 397 return 0; 398 399 if (!allow_dups) { 400 /* Avoid adding the same thing twice */ 401 for (i = 0; i < ref_name_cnt; i++) 402 if (!strcmp(refname, ref_name[i])) 403 return 0; 404 } 405 if (MAX_REVS <= ref_name_cnt) { 406 warning(Q_("ignoring %s; cannot handle more than %d ref", 407 "ignoring %s; cannot handle more than %d refs", 408 MAX_REVS), refname, MAX_REVS); 409 return 0; 410 } 411 ref_name[ref_name_cnt++] = xstrdup(refname); 412 ref_name[ref_name_cnt] = NULL; 413 return 0; 414} 415 416static int append_head_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid, 417 int flag UNUSED, void *cb_data UNUSED) 418{ 419 struct object_id tmp; 420 int ofs = 11; 421 if (!starts_with(refname, "refs/heads/")) 422 return 0; 423 /* If both heads/foo and tags/foo exists, get_sha1 would 424 * get confused. 425 */ 426 if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid)) 427 ofs = 5; 428 return append_ref(refname + ofs, oid, 0); 429} 430 431static int append_remote_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid, 432 int flag UNUSED, void *cb_data UNUSED) 433{ 434 struct object_id tmp; 435 int ofs = 13; 436 if (!starts_with(refname, "refs/remotes/")) 437 return 0; 438 /* If both heads/foo and tags/foo exists, get_sha1 would 439 * get confused. 440 */ 441 if (repo_get_oid(the_repository, refname + ofs, &tmp) || !oideq(&tmp, oid)) 442 ofs = 5; 443 return append_ref(refname + ofs, oid, 0); 444} 445 446static int append_tag_ref(const char *refname, const struct object_id *oid, 447 int flag UNUSED, void *cb_data UNUSED) 448{ 449 if (!starts_with(refname, "refs/tags/")) 450 return 0; 451 return append_ref(refname + 5, oid, 0); 452} 453 454static const char *match_ref_pattern = NULL; 455static int match_ref_slash = 0; 456 457static int append_matching_ref(const char *refname, const char *referent UNUSED, const struct object_id *oid, 458 int flag, void *cb_data) 459{ 460 /* we want to allow pattern hold/<asterisk> to show all 461 * branches under refs/heads/hold/, and v0.99.9? to show 462 * refs/tags/v0.99.9a and friends. 463 */ 464 const char *tail; 465 int slash = count_slashes(refname); 466 for (tail = refname; *tail && match_ref_slash < slash; ) 467 if (*tail++ == '/') 468 slash--; 469 if (!*tail) 470 return 0; 471 if (wildmatch(match_ref_pattern, tail, 0)) 472 return 0; 473 if (starts_with(refname, "refs/heads/")) 474 return append_head_ref(refname, NULL, oid, flag, cb_data); 475 if (starts_with(refname, "refs/tags/")) 476 return append_tag_ref(refname, oid, flag, cb_data); 477 return append_ref(refname, oid, 0); 478} 479 480static void snarf_refs(int head, int remotes) 481{ 482 if (head) { 483 int orig_cnt = ref_name_cnt; 484 485 refs_for_each_ref(get_main_ref_store(the_repository), 486 append_head_ref, NULL); 487 sort_ref_range(orig_cnt, ref_name_cnt); 488 } 489 if (remotes) { 490 int orig_cnt = ref_name_cnt; 491 492 refs_for_each_ref(get_main_ref_store(the_repository), 493 append_remote_ref, NULL); 494 sort_ref_range(orig_cnt, ref_name_cnt); 495 } 496} 497 498static int rev_is_head(const char *head, const char *name) 499{ 500 if (!head) 501 return 0; 502 skip_prefix(head, "refs/heads/", &head); 503 if (!skip_prefix(name, "refs/heads/", &name)) 504 skip_prefix(name, "heads/", &name); 505 return !strcmp(head, name); 506} 507 508static int show_merge_base(const struct commit_list *seen, int num_rev) 509{ 510 int all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); 511 int all_revs = all_mask & ~((1u << REV_SHIFT) - 1); 512 int exit_status = 1; 513 514 for (const struct commit_list *s = seen; s; s = s->next) { 515 struct commit *commit = s->item; 516 int flags = commit->object.flags & all_mask; 517 if (!(flags & UNINTERESTING) && 518 ((flags & all_revs) == all_revs)) { 519 puts(oid_to_hex(&commit->object.oid)); 520 exit_status = 0; 521 commit->object.flags |= UNINTERESTING; 522 } 523 } 524 return exit_status; 525} 526 527static int show_independent(struct commit **rev, 528 int num_rev, 529 unsigned int *rev_mask) 530{ 531 int i; 532 533 for (i = 0; i < num_rev; i++) { 534 struct commit *commit = rev[i]; 535 unsigned int flag = rev_mask[i]; 536 537 if (commit->object.flags == flag) 538 puts(oid_to_hex(&commit->object.oid)); 539 commit->object.flags |= UNINTERESTING; 540 } 541 return 0; 542} 543 544static void append_one_rev(const char *av) 545{ 546 struct object_id revkey; 547 if (!repo_get_oid(the_repository, av, &revkey)) { 548 append_ref(av, &revkey, 0); 549 return; 550 } 551 if (strpbrk(av, "*?[")) { 552 /* glob style match */ 553 int saved_matches = ref_name_cnt; 554 555 match_ref_pattern = av; 556 match_ref_slash = count_slashes(av); 557 refs_for_each_ref(get_main_ref_store(the_repository), 558 append_matching_ref, NULL); 559 if (saved_matches == ref_name_cnt && 560 ref_name_cnt < MAX_REVS) 561 error(_("no matching refs with %s"), av); 562 sort_ref_range(saved_matches, ref_name_cnt); 563 return; 564 } 565 die("bad sha1 reference %s", av); 566} 567 568static int git_show_branch_config(const char *var, const char *value, 569 const struct config_context *ctx, void *cb) 570{ 571 if (!strcmp(var, "showbranch.default")) { 572 if (!value) 573 return config_error_nonbool(var); 574 /* 575 * default_arg is now passed to parse_options(), so we need to 576 * mimic the real argv a bit better. 577 */ 578 if (!default_args.nr) 579 strvec_push(&default_args, "show-branch"); 580 strvec_push(&default_args, value); 581 return 0; 582 } 583 584 if (!strcmp(var, "color.showbranch")) { 585 showbranch_use_color = git_config_colorbool(var, value); 586 return 0; 587 } 588 589 if (git_color_config(var, value, cb) < 0) 590 return -1; 591 592 return git_default_config(var, value, ctx, cb); 593} 594 595static int omit_in_dense(struct commit *commit, struct commit **rev, int n) 596{ 597 /* If the commit is tip of the named branches, do not 598 * omit it. 599 * Otherwise, if it is a merge that is reachable from only one 600 * tip, it is not that interesting. 601 */ 602 int i, flag, count; 603 for (i = 0; i < n; i++) 604 if (rev[i] == commit) 605 return 0; 606 flag = commit->object.flags; 607 for (i = count = 0; i < n; i++) { 608 if (flag & (1u << (i + REV_SHIFT))) 609 count++; 610 } 611 if (count == 1) 612 return 1; 613 return 0; 614} 615 616static int reflog = 0; 617 618static int parse_reflog_param(const struct option *opt, const char *arg, 619 int unset) 620{ 621 char *ep; 622 const char **base = (const char **)opt->value; 623 BUG_ON_OPT_NEG(unset); 624 if (!arg) 625 arg = ""; 626 reflog = strtoul(arg, &ep, 10); 627 if (*ep == ',') 628 *base = ep + 1; 629 else if (*ep) 630 return error("unrecognized reflog param '%s'", arg); 631 else 632 *base = NULL; 633 if (reflog <= 0) 634 reflog = DEFAULT_REFLOG; 635 return 0; 636} 637 638int cmd_show_branch(int ac, 639 const char **av, 640 const char *prefix, 641 struct repository *repo UNUSED) 642{ 643 struct commit *rev[MAX_REVS], *commit; 644 char *reflog_msg[MAX_REVS] = {0}; 645 struct commit_list *list = NULL, *seen = NULL; 646 unsigned int rev_mask[MAX_REVS]; 647 int num_rev, i, extra = 0; 648 int all_heads = 0, all_remotes = 0; 649 int all_mask, all_revs; 650 enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER; 651 char *head; 652 struct object_id head_oid; 653 int merge_base = 0; 654 int independent = 0; 655 int no_name = 0; 656 int sha1_name = 0; 657 int shown_merge_point = 0; 658 int with_current_branch = 0; 659 int head_at = -1; 660 int topics = 0; 661 int sparse = 0; 662 const char *reflog_base = NULL; 663 struct option builtin_show_branch_options[] = { 664 OPT_BOOL('a', "all", &all_heads, 665 N_("show remote-tracking and local branches")), 666 OPT_BOOL('r', "remotes", &all_remotes, 667 N_("show remote-tracking branches")), 668 OPT__COLOR(&showbranch_use_color, 669 N_("color '*!+-' corresponding to the branch")), 670 { 671 .type = OPTION_INTEGER, 672 .long_name = "more", 673 .value = &extra, 674 .precision = sizeof(extra), 675 .argh = N_("n"), 676 .help = N_("show <n> more commits after the common ancestor"), 677 .flags = PARSE_OPT_OPTARG, 678 .defval = 1, 679 }, 680 OPT_SET_INT(0, "list", &extra, N_("synonym to more=-1"), -1), 681 OPT_BOOL(0, "no-name", &no_name, N_("suppress naming strings")), 682 OPT_BOOL(0, "current", &with_current_branch, 683 N_("include the current branch")), 684 OPT_BOOL(0, "sha1-name", &sha1_name, 685 N_("name commits with their object names")), 686 OPT_BOOL(0, "merge-base", &merge_base, 687 N_("show possible merge bases")), 688 OPT_BOOL(0, "independent", &independent, 689 N_("show refs unreachable from any other ref")), 690 OPT_SET_INT_F(0, "topo-order", &sort_order, 691 N_("show commits in topological order"), 692 REV_SORT_IN_GRAPH_ORDER, PARSE_OPT_NONEG), 693 OPT_BOOL(0, "topics", &topics, 694 N_("show only commits not on the first branch")), 695 OPT_SET_INT(0, "sparse", &sparse, 696 N_("show merges reachable from only one tip"), 1), 697 OPT_SET_INT_F(0, "date-order", &sort_order, 698 N_("topologically sort, maintaining date order " 699 "where possible"), 700 REV_SORT_BY_COMMIT_DATE, PARSE_OPT_NONEG), 701 OPT_CALLBACK_F('g', "reflog", &reflog_base, N_("<n>[,<base>]"), 702 N_("show <n> most recent ref-log entries starting at " 703 "base"), 704 PARSE_OPT_OPTARG | PARSE_OPT_NONEG, 705 parse_reflog_param), 706 OPT_END() 707 }; 708 const char **args_copy = NULL; 709 int ret; 710 711 init_commit_name_slab(&name_slab); 712 713 repo_config(the_repository, git_show_branch_config, NULL); 714 715 /* If nothing is specified, try the default first */ 716 if (ac == 1 && default_args.nr) { 717 DUP_ARRAY(args_copy, default_args.v, default_args.nr); 718 ac = default_args.nr; 719 av = args_copy; 720 } 721 722 ac = parse_options(ac, av, prefix, builtin_show_branch_options, 723 show_branch_usage, PARSE_OPT_STOP_AT_NON_OPTION); 724 if (all_heads) 725 all_remotes = 1; 726 727 if (extra || reflog) { 728 /* "listing" mode is incompatible with 729 * independent nor merge-base modes. 730 */ 731 if (independent || merge_base) 732 usage_with_options(show_branch_usage, 733 builtin_show_branch_options); 734 if (reflog && ((0 < extra) || all_heads || all_remotes)) 735 /* 736 * Asking for --more in reflog mode does not 737 * make sense. --list is Ok. 738 * 739 * Also --all and --remotes do not make sense either. 740 */ 741 die(_("options '%s' and '%s' cannot be used together"), "--reflog", 742 "--all/--remotes/--independent/--merge-base"); 743 } 744 745 if (with_current_branch && reflog) 746 die(_("options '%s' and '%s' cannot be used together"), 747 "--reflog", "--current"); 748 749 /* If nothing is specified, show all branches by default */ 750 if (ac <= topics && all_heads + all_remotes == 0) 751 all_heads = 1; 752 753 if (reflog) { 754 struct object_id oid; 755 char *ref; 756 int base = 0; 757 unsigned int flags = 0; 758 759 if (ac == 0) { 760 static const char *fake_av[2]; 761 762 fake_av[0] = refs_resolve_refdup(get_main_ref_store(the_repository), 763 "HEAD", 764 RESOLVE_REF_READING, 765 &oid, 766 NULL); 767 fake_av[1] = NULL; 768 av = fake_av; 769 ac = 1; 770 if (!*av) 771 die(_("no branches given, and HEAD is not valid")); 772 } 773 if (ac != 1) 774 die(_("--reflog option needs one branch name")); 775 776 if (MAX_REVS < reflog) 777 die(Q_("only %d entry can be shown at one time.", 778 "only %d entries can be shown at one time.", 779 MAX_REVS), MAX_REVS); 780 if (!repo_dwim_ref(the_repository, *av, strlen(*av), &oid, 781 &ref, 0)) 782 die(_("no such ref %s"), *av); 783 784 /* Has the base been specified? */ 785 if (reflog_base) { 786 char *ep; 787 base = strtoul(reflog_base, &ep, 10); 788 if (*ep) { 789 /* Ah, that is a date spec... */ 790 timestamp_t at; 791 at = approxidate(reflog_base); 792 read_ref_at(get_main_ref_store(the_repository), 793 ref, flags, at, -1, &oid, NULL, 794 NULL, NULL, &base); 795 } 796 } 797 798 for (i = 0; i < reflog; i++) { 799 char *logmsg = NULL; 800 char *nth_desc; 801 const char *msg; 802 char *end; 803 timestamp_t timestamp; 804 int tz; 805 806 if (read_ref_at(get_main_ref_store(the_repository), 807 ref, flags, 0, base + i, &oid, &logmsg, 808 &timestamp, &tz, NULL)) { 809 free(logmsg); 810 reflog = i; 811 break; 812 } 813 814 end = strchr(logmsg, '\n'); 815 if (end) 816 *end = '\0'; 817 818 msg = (*logmsg == '\0') ? "(none)" : logmsg; 819 reflog_msg[i] = xstrfmt("(%s) %s", 820 show_date(timestamp, tz, 821 DATE_MODE(RELATIVE)), 822 msg); 823 free(logmsg); 824 825 nth_desc = xstrfmt("%s@{%d}", *av, base+i); 826 append_ref(nth_desc, &oid, 1); 827 free(nth_desc); 828 } 829 free(ref); 830 } 831 else { 832 while (0 < ac) { 833 append_one_rev(*av); 834 ac--; av++; 835 } 836 if (all_heads + all_remotes) 837 snarf_refs(all_heads, all_remotes); 838 } 839 840 head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD", 841 RESOLVE_REF_READING, 842 &head_oid, NULL); 843 844 if (with_current_branch && head) { 845 int has_head = 0; 846 for (i = 0; !has_head && i < ref_name_cnt; i++) { 847 /* We are only interested in adding the branch 848 * HEAD points at. 849 */ 850 if (rev_is_head(head, ref_name[i])) 851 has_head++; 852 } 853 if (!has_head) { 854 const char *name = head; 855 skip_prefix(name, "refs/heads/", &name); 856 append_one_rev(name); 857 } 858 } 859 860 if (!ref_name_cnt) { 861 fprintf(stderr, "No revs to be shown.\n"); 862 ret = 0; 863 goto out; 864 } 865 866 for (num_rev = 0; ref_name[num_rev]; num_rev++) { 867 struct object_id revkey; 868 unsigned int flag = 1u << (num_rev + REV_SHIFT); 869 870 if (MAX_REVS <= num_rev) 871 die(Q_("cannot handle more than %d rev.", 872 "cannot handle more than %d revs.", 873 MAX_REVS), MAX_REVS); 874 if (repo_get_oid(the_repository, ref_name[num_rev], &revkey)) 875 die(_("'%s' is not a valid ref."), ref_name[num_rev]); 876 commit = lookup_commit_reference(the_repository, &revkey); 877 if (!commit) 878 die(_("cannot find commit %s (%s)"), 879 ref_name[num_rev], oid_to_hex(&revkey)); 880 repo_parse_commit(the_repository, commit); 881 mark_seen(commit, &seen); 882 883 /* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1, 884 * and so on. REV_SHIFT bits from bit 0 are used for 885 * internal bookkeeping. 886 */ 887 commit->object.flags |= flag; 888 if (commit->object.flags == flag) 889 commit_list_insert_by_date(commit, &list); 890 rev[num_rev] = commit; 891 } 892 for (i = 0; i < num_rev; i++) 893 rev_mask[i] = rev[i]->object.flags; 894 895 if (0 <= extra) 896 join_revs(&list, &seen, num_rev, extra); 897 898 commit_list_sort_by_date(&seen); 899 900 if (merge_base) { 901 ret = show_merge_base(seen, num_rev); 902 goto out; 903 } 904 905 if (independent) { 906 ret = show_independent(rev, num_rev, rev_mask); 907 goto out; 908 } 909 910 /* Show list; --more=-1 means list-only */ 911 if (1 < num_rev || extra < 0) { 912 for (i = 0; i < num_rev; i++) { 913 int j; 914 int is_head = rev_is_head(head, ref_name[i]) && 915 oideq(&head_oid, &rev[i]->object.oid); 916 if (extra < 0) 917 printf("%c [%s] ", 918 is_head ? '*' : ' ', ref_name[i]); 919 else { 920 for (j = 0; j < i; j++) 921 putchar(' '); 922 printf("%s%c%s [%s] ", 923 get_color_code(i), 924 is_head ? '*' : '!', 925 get_color_reset_code(), ref_name[i]); 926 } 927 928 if (!reflog) { 929 /* header lines never need name */ 930 show_one_commit(rev[i], 1); 931 } 932 else 933 puts(reflog_msg[i]); 934 935 if (is_head) 936 head_at = i; 937 } 938 if (0 <= extra) { 939 for (i = 0; i < num_rev; i++) 940 putchar('-'); 941 putchar('\n'); 942 } 943 } 944 if (extra < 0) { 945 ret = 0; 946 goto out; 947 } 948 949 /* Sort topologically */ 950 sort_in_topological_order(&seen, sort_order); 951 952 /* Give names to commits */ 953 if (!sha1_name && !no_name) 954 name_commits(seen, rev, ref_name, num_rev); 955 956 all_mask = ((1u << (REV_SHIFT + num_rev)) - 1); 957 all_revs = all_mask & ~((1u << REV_SHIFT) - 1); 958 959 for (struct commit_list *l = seen; l; l = l->next) { 960 struct commit *commit = l->item; 961 int this_flag = commit->object.flags; 962 int is_merge_point = ((this_flag & all_revs) == all_revs); 963 964 shown_merge_point |= is_merge_point; 965 966 if (1 < num_rev) { 967 int is_merge = !!(commit->parents && 968 commit->parents->next); 969 if (topics && 970 !is_merge_point && 971 (this_flag & (1u << REV_SHIFT))) 972 continue; 973 if (!sparse && is_merge && 974 omit_in_dense(commit, rev, num_rev)) 975 continue; 976 for (i = 0; i < num_rev; i++) { 977 int mark; 978 if (!(this_flag & (1u << (i + REV_SHIFT)))) 979 mark = ' '; 980 else if (is_merge) 981 mark = '-'; 982 else if (i == head_at) 983 mark = '*'; 984 else 985 mark = '+'; 986 if (mark == ' ') 987 putchar(mark); 988 else 989 printf("%s%c%s", 990 get_color_code(i), 991 mark, get_color_reset_code()); 992 } 993 putchar(' '); 994 } 995 show_one_commit(commit, no_name); 996 997 if (shown_merge_point && --extra < 0) 998 break; 999 } 1000 1001 ret = 0; 1002 1003out: 1004 for (size_t i = 0; i < ARRAY_SIZE(reflog_msg); i++) 1005 free(reflog_msg[i]); 1006 free_commit_list(seen); 1007 free_commit_list(list); 1008 free(args_copy); 1009 free(head); 1010 return ret; 1011}