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