Coverage Report

Created: 2024-09-08 06:23

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