Coverage Report

Created: 2023-11-19 07:08

/src/git/builtin/rev-parse.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * rev-parse.c
3
 *
4
 * Copyright (C) Linus Torvalds, 2005
5
 */
6
#define USE_THE_INDEX_VARIABLE
7
#include "builtin.h"
8
#include "abspath.h"
9
#include "config.h"
10
#include "commit.h"
11
#include "environment.h"
12
#include "gettext.h"
13
#include "hash.h"
14
#include "hex.h"
15
#include "refs.h"
16
#include "quote.h"
17
#include "object-name.h"
18
#include "parse-options.h"
19
#include "path.h"
20
#include "diff.h"
21
#include "read-cache-ll.h"
22
#include "revision.h"
23
#include "setup.h"
24
#include "split-index.h"
25
#include "submodule.h"
26
#include "commit-reach.h"
27
#include "shallow.h"
28
29
0
#define DO_REVS   1
30
0
#define DO_NOREV  2
31
0
#define DO_FLAGS  4
32
0
#define DO_NONFLAGS 8
33
static int filter = ~0;
34
35
static const char *def;
36
37
0
#define NORMAL 0
38
0
#define REVERSED 1
39
static int show_type = NORMAL;
40
41
0
#define SHOW_SYMBOLIC_ASIS 1
42
0
#define SHOW_SYMBOLIC_FULL 2
43
static int symbolic;
44
static int abbrev;
45
static int abbrev_ref;
46
static int abbrev_ref_strict;
47
static int output_sq;
48
49
static int stuck_long;
50
static struct ref_exclusions ref_excludes = REF_EXCLUSIONS_INIT;
51
52
/*
53
 * Some arguments are relevant "revision" arguments,
54
 * others are about output format or other details.
55
 * This sorts it all out.
56
 */
57
static int is_rev_argument(const char *arg)
58
0
{
59
0
  static const char *rev_args[] = {
60
0
    "--all",
61
0
    "--bisect",
62
0
    "--dense",
63
0
    "--branches=",
64
0
    "--branches",
65
0
    "--header",
66
0
    "--ignore-missing",
67
0
    "--max-age=",
68
0
    "--max-count=",
69
0
    "--min-age=",
70
0
    "--no-merges",
71
0
    "--min-parents=",
72
0
    "--no-min-parents",
73
0
    "--max-parents=",
74
0
    "--no-max-parents",
75
0
    "--objects",
76
0
    "--objects-edge",
77
0
    "--parents",
78
0
    "--pretty",
79
0
    "--remotes=",
80
0
    "--remotes",
81
0
    "--glob=",
82
0
    "--sparse",
83
0
    "--tags=",
84
0
    "--tags",
85
0
    "--topo-order",
86
0
    "--date-order",
87
0
    "--unpacked",
88
0
    NULL
89
0
  };
90
0
  const char **p = rev_args;
91
92
  /* accept -<digit>, like traditional "head" */
93
0
  if ((*arg == '-') && isdigit(arg[1]))
94
0
    return 1;
95
96
0
  for (;;) {
97
0
    const char *str = *p++;
98
0
    int len;
99
0
    if (!str)
100
0
      return 0;
101
0
    len = strlen(str);
102
0
    if (!strcmp(arg, str) ||
103
0
        (str[len-1] == '=' && !strncmp(arg, str, len)))
104
0
      return 1;
105
0
  }
106
0
}
107
108
/* Output argument as a string, either SQ or normal */
109
static void show(const char *arg)
110
0
{
111
0
  if (output_sq) {
112
0
    int sq = '\'', ch;
113
114
0
    putchar(sq);
115
0
    while ((ch = *arg++)) {
116
0
      if (ch == sq)
117
0
        fputs("'\\'", stdout);
118
0
      putchar(ch);
119
0
    }
120
0
    putchar(sq);
121
0
    putchar(' ');
122
0
  }
123
0
  else
124
0
    puts(arg);
125
0
}
126
127
/* Like show(), but with a negation prefix according to type */
128
static void show_with_type(int type, const char *arg)
129
0
{
130
0
  if (type != show_type)
131
0
    putchar('^');
132
0
  show(arg);
133
0
}
134
135
/* Output a revision, only if filter allows it */
136
static void show_rev(int type, const struct object_id *oid, const char *name)
137
0
{
138
0
  if (!(filter & DO_REVS))
139
0
    return;
140
0
  def = NULL;
141
142
0
  if ((symbolic || abbrev_ref) && name) {
143
0
    if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
144
0
      struct object_id discard;
145
0
      char *full;
146
147
0
      switch (repo_dwim_ref(the_repository, name,
148
0
                strlen(name), &discard, &full,
149
0
                0)) {
150
0
      case 0:
151
        /*
152
         * Not found -- not a ref.  We could
153
         * emit "name" here, but symbolic-full
154
         * users are interested in finding the
155
         * refs spelled in full, and they would
156
         * need to filter non-refs if we did so.
157
         */
158
0
        break;
159
0
      case 1: /* happy */
160
0
        if (abbrev_ref) {
161
0
          char *old = full;
162
0
          full = shorten_unambiguous_ref(full,
163
0
            abbrev_ref_strict);
164
0
          free(old);
165
0
        }
166
0
        show_with_type(type, full);
167
0
        break;
168
0
      default: /* ambiguous */
169
0
        error("refname '%s' is ambiguous", name);
170
0
        break;
171
0
      }
172
0
      free(full);
173
0
    } else {
174
0
      show_with_type(type, name);
175
0
    }
176
0
  }
177
0
  else if (abbrev)
178
0
    show_with_type(type,
179
0
             repo_find_unique_abbrev(the_repository, oid, abbrev));
180
0
  else
181
0
    show_with_type(type, oid_to_hex(oid));
182
0
}
183
184
/* Output a flag, only if filter allows it. */
185
static int show_flag(const char *arg)
186
0
{
187
0
  if (!(filter & DO_FLAGS))
188
0
    return 0;
189
0
  if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV)) {
190
0
    show(arg);
191
0
    return 1;
192
0
  }
193
0
  return 0;
194
0
}
195
196
static int show_default(void)
197
0
{
198
0
  const char *s = def;
199
200
0
  if (s) {
201
0
    struct object_id oid;
202
203
0
    def = NULL;
204
0
    if (!repo_get_oid(the_repository, s, &oid)) {
205
0
      show_rev(NORMAL, &oid, s);
206
0
      return 1;
207
0
    }
208
0
  }
209
0
  return 0;
210
0
}
211
212
static int show_reference(const char *refname, const struct object_id *oid,
213
        int flag UNUSED, void *cb_data UNUSED)
214
0
{
215
0
  if (ref_excluded(&ref_excludes, refname))
216
0
    return 0;
217
0
  show_rev(NORMAL, oid, refname);
218
0
  return 0;
219
0
}
220
221
static int anti_reference(const char *refname, const struct object_id *oid,
222
        int flag UNUSED, void *cb_data UNUSED)
223
0
{
224
0
  show_rev(REVERSED, oid, refname);
225
0
  return 0;
226
0
}
227
228
static int show_abbrev(const struct object_id *oid, void *cb_data UNUSED)
229
0
{
230
0
  show_rev(NORMAL, oid, NULL);
231
0
  return 0;
232
0
}
233
234
static void show_datestring(const char *flag, const char *datestr)
235
0
{
236
0
  char *buffer;
237
238
  /* date handling requires both flags and revs */
239
0
  if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
240
0
    return;
241
0
  buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr));
242
0
  show(buffer);
243
0
  free(buffer);
244
0
}
245
246
static int show_file(const char *arg, int output_prefix)
247
0
{
248
0
  show_default();
249
0
  if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
250
0
    if (output_prefix) {
251
0
      const char *prefix = startup_info->prefix;
252
0
      char *fname = prefix_filename(prefix, arg);
253
0
      show(fname);
254
0
      free(fname);
255
0
    } else
256
0
      show(arg);
257
0
    return 1;
258
0
  }
259
0
  return 0;
260
0
}
261
262
static int try_difference(const char *arg)
263
0
{
264
0
  char *dotdot;
265
0
  struct object_id start_oid;
266
0
  struct object_id end_oid;
267
0
  const char *end;
268
0
  const char *start;
269
0
  int symmetric;
270
0
  static const char head_by_default[] = "HEAD";
271
272
0
  if (!(dotdot = strstr(arg, "..")))
273
0
    return 0;
274
0
  end = dotdot + 2;
275
0
  start = arg;
276
0
  symmetric = (*end == '.');
277
278
0
  *dotdot = 0;
279
0
  end += symmetric;
280
281
0
  if (!*end)
282
0
    end = head_by_default;
283
0
  if (dotdot == arg)
284
0
    start = head_by_default;
285
286
0
  if (start == head_by_default && end == head_by_default &&
287
0
      !symmetric) {
288
    /*
289
     * Just ".."?  That is not a range but the
290
     * pathspec for the parent directory.
291
     */
292
0
    *dotdot = '.';
293
0
    return 0;
294
0
  }
295
296
0
  if (!repo_get_oid_committish(the_repository, start, &start_oid) && !repo_get_oid_committish(the_repository, end, &end_oid)) {
297
0
    show_rev(NORMAL, &end_oid, end);
298
0
    show_rev(symmetric ? NORMAL : REVERSED, &start_oid, start);
299
0
    if (symmetric) {
300
0
      struct commit_list *exclude;
301
0
      struct commit *a, *b;
302
0
      a = lookup_commit_reference(the_repository, &start_oid);
303
0
      b = lookup_commit_reference(the_repository, &end_oid);
304
0
      if (!a || !b) {
305
0
        *dotdot = '.';
306
0
        return 0;
307
0
      }
308
0
      exclude = repo_get_merge_bases(the_repository, a, b);
309
0
      while (exclude) {
310
0
        struct commit *commit = pop_commit(&exclude);
311
0
        show_rev(REVERSED, &commit->object.oid, NULL);
312
0
      }
313
0
    }
314
0
    *dotdot = '.';
315
0
    return 1;
316
0
  }
317
0
  *dotdot = '.';
318
0
  return 0;
319
0
}
320
321
static int try_parent_shorthands(const char *arg)
322
0
{
323
0
  char *dotdot;
324
0
  struct object_id oid;
325
0
  struct commit *commit;
326
0
  struct commit_list *parents;
327
0
  int parent_number;
328
0
  int include_rev = 0;
329
0
  int include_parents = 0;
330
0
  int exclude_parent = 0;
331
332
0
  if ((dotdot = strstr(arg, "^!"))) {
333
0
    include_rev = 1;
334
0
    if (dotdot[2])
335
0
      return 0;
336
0
  } else if ((dotdot = strstr(arg, "^@"))) {
337
0
    include_parents = 1;
338
0
    if (dotdot[2])
339
0
      return 0;
340
0
  } else if ((dotdot = strstr(arg, "^-"))) {
341
0
    include_rev = 1;
342
0
    exclude_parent = 1;
343
344
0
    if (dotdot[2]) {
345
0
      char *end;
346
0
      exclude_parent = strtoul(dotdot + 2, &end, 10);
347
0
      if (*end != '\0' || !exclude_parent)
348
0
        return 0;
349
0
    }
350
0
  } else
351
0
    return 0;
352
353
0
  *dotdot = 0;
354
0
  if (repo_get_oid_committish(the_repository, arg, &oid) ||
355
0
      !(commit = lookup_commit_reference(the_repository, &oid))) {
356
0
    *dotdot = '^';
357
0
    return 0;
358
0
  }
359
360
0
  if (exclude_parent &&
361
0
      exclude_parent > commit_list_count(commit->parents)) {
362
0
    *dotdot = '^';
363
0
    return 0;
364
0
  }
365
366
0
  if (include_rev)
367
0
    show_rev(NORMAL, &oid, arg);
368
0
  for (parents = commit->parents, parent_number = 1;
369
0
       parents;
370
0
       parents = parents->next, parent_number++) {
371
0
    char *name = NULL;
372
373
0
    if (exclude_parent && parent_number != exclude_parent)
374
0
      continue;
375
376
0
    if (symbolic)
377
0
      name = xstrfmt("%s^%d", arg, parent_number);
378
0
    show_rev(include_parents ? NORMAL : REVERSED,
379
0
       &parents->item->object.oid, name);
380
0
    free(name);
381
0
  }
382
383
0
  *dotdot = '^';
384
0
  return 1;
385
0
}
386
387
static int parseopt_dump(const struct option *o, const char *arg, int unset)
388
0
{
389
0
  struct strbuf *parsed = o->value;
390
0
  if (unset)
391
0
    strbuf_addf(parsed, " --no-%s", o->long_name);
392
0
  else if (o->short_name && (o->long_name == NULL || !stuck_long))
393
0
    strbuf_addf(parsed, " -%c", o->short_name);
394
0
  else
395
0
    strbuf_addf(parsed, " --%s", o->long_name);
396
0
  if (arg) {
397
0
    if (!stuck_long)
398
0
      strbuf_addch(parsed, ' ');
399
0
    else if (o->long_name)
400
0
      strbuf_addch(parsed, '=');
401
0
    sq_quote_buf(parsed, arg);
402
0
  }
403
0
  return 0;
404
0
}
405
406
static const char *skipspaces(const char *s)
407
0
{
408
0
  while (isspace(*s))
409
0
    s++;
410
0
  return s;
411
0
}
412
413
static char *findspace(const char *s)
414
0
{
415
0
  for (; *s; s++)
416
0
    if (isspace(*s))
417
0
      return (char*)s;
418
0
  return NULL;
419
0
}
420
421
static int cmd_parseopt(int argc, const char **argv, const char *prefix)
422
0
{
423
0
  static int keep_dashdash = 0, stop_at_non_option = 0;
424
0
  static char const * const parseopt_usage[] = {
425
0
    N_("git rev-parse --parseopt [<options>] -- [<args>...]"),
426
0
    NULL
427
0
  };
428
0
  static struct option parseopt_opts[] = {
429
0
    OPT_BOOL(0, "keep-dashdash", &keep_dashdash,
430
0
          N_("keep the `--` passed as an arg")),
431
0
    OPT_BOOL(0, "stop-at-non-option", &stop_at_non_option,
432
0
          N_("stop parsing after the "
433
0
             "first non-option argument")),
434
0
    OPT_BOOL(0, "stuck-long", &stuck_long,
435
0
          N_("output in stuck long form")),
436
0
    OPT_END(),
437
0
  };
438
0
  static const char * const flag_chars = "*=?!";
439
440
0
  struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
441
0
  const char **usage = NULL;
442
0
  struct option *opts = NULL;
443
0
  int onb = 0, osz = 0, unb = 0, usz = 0;
444
445
0
  strbuf_addstr(&parsed, "set --");
446
0
  argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage,
447
0
                       PARSE_OPT_KEEP_DASHDASH);
448
0
  if (argc < 1 || strcmp(argv[0], "--"))
449
0
    usage_with_options(parseopt_usage, parseopt_opts);
450
451
  /* get the usage up to the first line with a -- on it */
452
0
  for (;;) {
453
0
    if (strbuf_getline(&sb, stdin) == EOF)
454
0
      die(_("premature end of input"));
455
0
    ALLOC_GROW(usage, unb + 1, usz);
456
0
    if (!strcmp("--", sb.buf)) {
457
0
      if (unb < 1)
458
0
        die(_("no usage string given before the `--' separator"));
459
0
      usage[unb] = NULL;
460
0
      break;
461
0
    }
462
0
    usage[unb++] = strbuf_detach(&sb, NULL);
463
0
  }
464
465
  /* parse: (<short>|<short>,<long>|<long>)[*=?!]*<arghint>? SP+ <help> */
466
0
  while (strbuf_getline(&sb, stdin) != EOF) {
467
0
    const char *s;
468
0
    char *help;
469
0
    struct option *o;
470
471
0
    if (!sb.len)
472
0
      continue;
473
474
0
    ALLOC_GROW(opts, onb + 1, osz);
475
0
    memset(opts + onb, 0, sizeof(opts[onb]));
476
477
0
    o = &opts[onb++];
478
0
    help = findspace(sb.buf);
479
0
    if (!help || sb.buf == help) {
480
0
      o->type = OPTION_GROUP;
481
0
      o->help = xstrdup(skipspaces(sb.buf));
482
0
      continue;
483
0
    }
484
485
0
    *help = '\0';
486
487
0
    o->type = OPTION_CALLBACK;
488
0
    o->help = xstrdup(skipspaces(help+1));
489
0
    o->value = &parsed;
490
0
    o->flags = PARSE_OPT_NOARG;
491
0
    o->callback = &parseopt_dump;
492
493
    /* name(s) */
494
0
    s = strpbrk(sb.buf, flag_chars);
495
0
    if (!s)
496
0
      s = help;
497
498
0
    if (s == sb.buf)
499
0
      die(_("missing opt-spec before option flags"));
500
501
0
    if (s - sb.buf == 1) /* short option only */
502
0
      o->short_name = *sb.buf;
503
0
    else if (sb.buf[1] != ',') /* long option only */
504
0
      o->long_name = xmemdupz(sb.buf, s - sb.buf);
505
0
    else {
506
0
      o->short_name = *sb.buf;
507
0
      o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
508
0
    }
509
510
    /* flags */
511
0
    while (s < help) {
512
0
      switch (*s++) {
513
0
      case '=':
514
0
        o->flags &= ~PARSE_OPT_NOARG;
515
0
        continue;
516
0
      case '?':
517
0
        o->flags &= ~PARSE_OPT_NOARG;
518
0
        o->flags |= PARSE_OPT_OPTARG;
519
0
        continue;
520
0
      case '!':
521
0
        o->flags |= PARSE_OPT_NONEG;
522
0
        continue;
523
0
      case '*':
524
0
        o->flags |= PARSE_OPT_HIDDEN;
525
0
        continue;
526
0
      }
527
0
      s--;
528
0
      break;
529
0
    }
530
531
0
    if (s < help)
532
0
      o->argh = xmemdupz(s, help - s);
533
0
  }
534
0
  strbuf_release(&sb);
535
536
  /* put an OPT_END() */
537
0
  ALLOC_GROW(opts, onb + 1, osz);
538
0
  memset(opts + onb, 0, sizeof(opts[onb]));
539
0
  argc = parse_options(argc, argv, prefix, opts, usage,
540
0
      (keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) |
541
0
      (stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
542
0
      PARSE_OPT_SHELL_EVAL);
543
544
0
  strbuf_addstr(&parsed, " --");
545
0
  sq_quote_argv(&parsed, argv);
546
0
  puts(parsed.buf);
547
0
  strbuf_release(&parsed);
548
0
  return 0;
549
0
}
550
551
static int cmd_sq_quote(int argc, const char **argv)
552
0
{
553
0
  struct strbuf buf = STRBUF_INIT;
554
555
0
  if (argc)
556
0
    sq_quote_argv(&buf, argv);
557
0
  printf("%s\n", buf.buf);
558
0
  strbuf_release(&buf);
559
560
0
  return 0;
561
0
}
562
563
static void die_no_single_rev(int quiet)
564
0
{
565
0
  if (quiet)
566
0
    exit(1);
567
0
  else
568
0
    die(_("Needed a single revision"));
569
0
}
570
571
static const char builtin_rev_parse_usage[] =
572
N_("git rev-parse --parseopt [<options>] -- [<args>...]\n"
573
   "   or: git rev-parse --sq-quote [<arg>...]\n"
574
   "   or: git rev-parse [<options>] [<arg>...]\n"
575
   "\n"
576
   "Run \"git rev-parse --parseopt -h\" for more information on the first usage.");
577
578
/*
579
 * Parse "opt" or "opt=<value>", setting value respectively to either
580
 * NULL or the string after "=".
581
 */
582
static int opt_with_value(const char *arg, const char *opt, const char **value)
583
0
{
584
0
  if (skip_prefix(arg, opt, &arg)) {
585
0
    if (!*arg) {
586
0
      *value = NULL;
587
0
      return 1;
588
0
    }
589
0
    if (*arg++ == '=') {
590
0
      *value = arg;
591
0
      return 1;
592
0
    }
593
0
  }
594
0
  return 0;
595
0
}
596
597
static void handle_ref_opt(const char *pattern, const char *prefix)
598
0
{
599
0
  if (pattern)
600
0
    for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
601
0
  else
602
0
    for_each_ref_in(prefix, show_reference, NULL);
603
0
  clear_ref_exclusions(&ref_excludes);
604
0
}
605
606
enum format_type {
607
  /* We would like a relative path. */
608
  FORMAT_RELATIVE,
609
  /* We would like a canonical absolute path. */
610
  FORMAT_CANONICAL,
611
  /* We would like the default behavior. */
612
  FORMAT_DEFAULT,
613
};
614
615
enum default_type {
616
  /* Our default is a relative path. */
617
  DEFAULT_RELATIVE,
618
  /* Our default is a relative path if there's a shared root. */
619
  DEFAULT_RELATIVE_IF_SHARED,
620
  /* Our default is a canonical absolute path. */
621
  DEFAULT_CANONICAL,
622
  /* Our default is not to modify the item. */
623
  DEFAULT_UNMODIFIED,
624
};
625
626
static void print_path(const char *path, const char *prefix, enum format_type format, enum default_type def)
627
0
{
628
0
  char *cwd = NULL;
629
  /*
630
   * We don't ever produce a relative path if prefix is NULL, so set the
631
   * prefix to the current directory so that we can produce a relative
632
   * path whenever possible.  If we're using RELATIVE_IF_SHARED mode, then
633
   * we want an absolute path unless the two share a common prefix, so don't
634
   * set it in that case, since doing so causes a relative path to always
635
   * be produced if possible.
636
   */
637
0
  if (!prefix && (format != FORMAT_DEFAULT || def != DEFAULT_RELATIVE_IF_SHARED))
638
0
    prefix = cwd = xgetcwd();
639
0
  if (format == FORMAT_DEFAULT && def == DEFAULT_UNMODIFIED) {
640
0
    puts(path);
641
0
  } else if (format == FORMAT_RELATIVE ||
642
0
      (format == FORMAT_DEFAULT && def == DEFAULT_RELATIVE)) {
643
    /*
644
     * In order for relative_path to work as expected, we need to
645
     * make sure that both paths are absolute paths.  If we don't,
646
     * we can end up with an unexpected absolute path that the user
647
     * didn't want.
648
     */
649
0
    struct strbuf buf = STRBUF_INIT, realbuf = STRBUF_INIT, prefixbuf = STRBUF_INIT;
650
0
    if (!is_absolute_path(path)) {
651
0
      strbuf_realpath_forgiving(&realbuf, path,  1);
652
0
      path = realbuf.buf;
653
0
    }
654
0
    if (!is_absolute_path(prefix)) {
655
0
      strbuf_realpath_forgiving(&prefixbuf, prefix, 1);
656
0
      prefix = prefixbuf.buf;
657
0
    }
658
0
    puts(relative_path(path, prefix, &buf));
659
0
    strbuf_release(&buf);
660
0
    strbuf_release(&realbuf);
661
0
    strbuf_release(&prefixbuf);
662
0
  } else if (format == FORMAT_DEFAULT && def == DEFAULT_RELATIVE_IF_SHARED) {
663
0
    struct strbuf buf = STRBUF_INIT;
664
0
    puts(relative_path(path, prefix, &buf));
665
0
    strbuf_release(&buf);
666
0
  } else {
667
0
    struct strbuf buf = STRBUF_INIT;
668
0
    strbuf_realpath_forgiving(&buf, path, 1);
669
0
    puts(buf.buf);
670
0
    strbuf_release(&buf);
671
0
  }
672
0
  free(cwd);
673
0
}
674
675
int cmd_rev_parse(int argc, const char **argv, const char *prefix)
676
0
{
677
0
  int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
678
0
  int did_repo_setup = 0;
679
0
  int has_dashdash = 0;
680
0
  int output_prefix = 0;
681
0
  struct object_id oid;
682
0
  unsigned int flags = 0;
683
0
  const char *name = NULL;
684
0
  struct object_context unused;
685
0
  struct strbuf buf = STRBUF_INIT;
686
0
  const int hexsz = the_hash_algo->hexsz;
687
0
  int seen_end_of_options = 0;
688
0
  enum format_type format = FORMAT_DEFAULT;
689
690
0
  if (argc > 1 && !strcmp("--parseopt", argv[1]))
691
0
    return cmd_parseopt(argc - 1, argv + 1, prefix);
692
693
0
  if (argc > 1 && !strcmp("--sq-quote", argv[1]))
694
0
    return cmd_sq_quote(argc - 2, argv + 2);
695
696
0
  if (argc > 1 && !strcmp("-h", argv[1]))
697
0
    usage(builtin_rev_parse_usage);
698
699
0
  for (i = 1; i < argc; i++) {
700
0
    if (!strcmp(argv[i], "--")) {
701
0
      has_dashdash = 1;
702
0
      break;
703
0
    }
704
0
  }
705
706
  /* No options; just report on whether we're in a git repo or not. */
707
0
  if (argc == 1) {
708
0
    setup_git_directory();
709
0
    git_config(git_default_config, NULL);
710
0
    return 0;
711
0
  }
712
713
0
  for (i = 1; i < argc; i++) {
714
0
    const char *arg = argv[i];
715
716
0
    if (as_is) {
717
0
      if (show_file(arg, output_prefix) && as_is < 2)
718
0
        verify_filename(prefix, arg, 0);
719
0
      continue;
720
0
    }
721
722
0
    if (!seen_end_of_options) {
723
0
      if (!strcmp(arg, "--local-env-vars")) {
724
0
        int i;
725
0
        for (i = 0; local_repo_env[i]; i++)
726
0
          printf("%s\n", local_repo_env[i]);
727
0
        continue;
728
0
      }
729
0
      if (!strcmp(arg, "--resolve-git-dir")) {
730
0
        const char *gitdir = argv[++i];
731
0
        if (!gitdir)
732
0
          die(_("--resolve-git-dir requires an argument"));
733
0
        gitdir = resolve_gitdir(gitdir);
734
0
        if (!gitdir)
735
0
          die(_("not a gitdir '%s'"), argv[i]);
736
0
        puts(gitdir);
737
0
        continue;
738
0
      }
739
0
    }
740
741
    /* The rest of the options require a git repository. */
742
0
    if (!did_repo_setup) {
743
0
      prefix = setup_git_directory();
744
0
      git_config(git_default_config, NULL);
745
0
      did_repo_setup = 1;
746
747
0
      prepare_repo_settings(the_repository);
748
0
      the_repository->settings.command_requires_full_index = 0;
749
0
    }
750
751
0
    if (!strcmp(arg, "--")) {
752
0
      as_is = 2;
753
      /* Pass on the "--" if we show anything but files.. */
754
0
      if (filter & (DO_FLAGS | DO_REVS))
755
0
        show_file(arg, 0);
756
0
      continue;
757
0
    }
758
759
0
    if (!seen_end_of_options && *arg == '-') {
760
0
      if (!strcmp(arg, "--git-path")) {
761
0
        if (!argv[i + 1])
762
0
          die(_("--git-path requires an argument"));
763
0
        strbuf_reset(&buf);
764
0
        print_path(git_path("%s", argv[i + 1]), prefix,
765
0
            format,
766
0
            DEFAULT_RELATIVE_IF_SHARED);
767
0
        i++;
768
0
        continue;
769
0
      }
770
0
      if (!strcmp(arg,"-n")) {
771
0
        if (++i >= argc)
772
0
          die(_("-n requires an argument"));
773
0
        if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
774
0
          show(arg);
775
0
          show(argv[i]);
776
0
        }
777
0
        continue;
778
0
      }
779
0
      if (starts_with(arg, "-n")) {
780
0
        if ((filter & DO_FLAGS) && (filter & DO_REVS))
781
0
          show(arg);
782
0
        continue;
783
0
      }
784
0
      if (opt_with_value(arg, "--path-format", &arg)) {
785
0
        if (!arg)
786
0
          die(_("--path-format requires an argument"));
787
0
        if (!strcmp(arg, "absolute")) {
788
0
          format = FORMAT_CANONICAL;
789
0
        } else if (!strcmp(arg, "relative")) {
790
0
          format = FORMAT_RELATIVE;
791
0
        } else {
792
0
          die(_("unknown argument to --path-format: %s"), arg);
793
0
        }
794
0
        continue;
795
0
      }
796
0
      if (!strcmp(arg, "--default")) {
797
0
        def = argv[++i];
798
0
        if (!def)
799
0
          die(_("--default requires an argument"));
800
0
        continue;
801
0
      }
802
0
      if (!strcmp(arg, "--prefix")) {
803
0
        prefix = argv[++i];
804
0
        if (!prefix)
805
0
          die(_("--prefix requires an argument"));
806
0
        startup_info->prefix = prefix;
807
0
        output_prefix = 1;
808
0
        continue;
809
0
      }
810
0
      if (!strcmp(arg, "--revs-only")) {
811
0
        filter &= ~DO_NOREV;
812
0
        continue;
813
0
      }
814
0
      if (!strcmp(arg, "--no-revs")) {
815
0
        filter &= ~DO_REVS;
816
0
        continue;
817
0
      }
818
0
      if (!strcmp(arg, "--flags")) {
819
0
        filter &= ~DO_NONFLAGS;
820
0
        continue;
821
0
      }
822
0
      if (!strcmp(arg, "--no-flags")) {
823
0
        filter &= ~DO_FLAGS;
824
0
        continue;
825
0
      }
826
0
      if (!strcmp(arg, "--verify")) {
827
0
        filter &= ~(DO_FLAGS|DO_NOREV);
828
0
        verify = 1;
829
0
        continue;
830
0
      }
831
0
      if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
832
0
        quiet = 1;
833
0
        flags |= GET_OID_QUIETLY;
834
0
        continue;
835
0
      }
836
0
      if (opt_with_value(arg, "--short", &arg)) {
837
0
        filter &= ~(DO_FLAGS|DO_NOREV);
838
0
        verify = 1;
839
0
        abbrev = DEFAULT_ABBREV;
840
0
        if (!arg)
841
0
          continue;
842
0
        abbrev = strtoul(arg, NULL, 10);
843
0
        if (abbrev < MINIMUM_ABBREV)
844
0
          abbrev = MINIMUM_ABBREV;
845
0
        else if (hexsz <= abbrev)
846
0
          abbrev = hexsz;
847
0
        continue;
848
0
      }
849
0
      if (!strcmp(arg, "--sq")) {
850
0
        output_sq = 1;
851
0
        continue;
852
0
      }
853
0
      if (!strcmp(arg, "--not")) {
854
0
        show_type ^= REVERSED;
855
0
        continue;
856
0
      }
857
0
      if (!strcmp(arg, "--symbolic")) {
858
0
        symbolic = SHOW_SYMBOLIC_ASIS;
859
0
        continue;
860
0
      }
861
0
      if (!strcmp(arg, "--symbolic-full-name")) {
862
0
        symbolic = SHOW_SYMBOLIC_FULL;
863
0
        continue;
864
0
      }
865
0
      if (opt_with_value(arg, "--abbrev-ref", &arg)) {
866
0
        abbrev_ref = 1;
867
0
        abbrev_ref_strict = warn_ambiguous_refs;
868
0
        if (arg) {
869
0
          if (!strcmp(arg, "strict"))
870
0
            abbrev_ref_strict = 1;
871
0
          else if (!strcmp(arg, "loose"))
872
0
            abbrev_ref_strict = 0;
873
0
          else
874
0
            die(_("unknown mode for --abbrev-ref: %s"),
875
0
                arg);
876
0
        }
877
0
        continue;
878
0
      }
879
0
      if (!strcmp(arg, "--all")) {
880
0
        for_each_ref(show_reference, NULL);
881
0
        clear_ref_exclusions(&ref_excludes);
882
0
        continue;
883
0
      }
884
0
      if (skip_prefix(arg, "--disambiguate=", &arg)) {
885
0
        repo_for_each_abbrev(the_repository, arg,
886
0
                 show_abbrev, NULL);
887
0
        continue;
888
0
      }
889
0
      if (!strcmp(arg, "--bisect")) {
890
0
        for_each_fullref_in("refs/bisect/bad", show_reference, NULL);
891
0
        for_each_fullref_in("refs/bisect/good", anti_reference, NULL);
892
0
        continue;
893
0
      }
894
0
      if (opt_with_value(arg, "--branches", &arg)) {
895
0
        if (ref_excludes.hidden_refs_configured)
896
0
          return error(_("--exclude-hidden cannot be used together with --branches"));
897
0
        handle_ref_opt(arg, "refs/heads/");
898
0
        continue;
899
0
      }
900
0
      if (opt_with_value(arg, "--tags", &arg)) {
901
0
        if (ref_excludes.hidden_refs_configured)
902
0
          return error(_("--exclude-hidden cannot be used together with --tags"));
903
0
        handle_ref_opt(arg, "refs/tags/");
904
0
        continue;
905
0
      }
906
0
      if (skip_prefix(arg, "--glob=", &arg)) {
907
0
        handle_ref_opt(arg, NULL);
908
0
        continue;
909
0
      }
910
0
      if (opt_with_value(arg, "--remotes", &arg)) {
911
0
        if (ref_excludes.hidden_refs_configured)
912
0
          return error(_("--exclude-hidden cannot be used together with --remotes"));
913
0
        handle_ref_opt(arg, "refs/remotes/");
914
0
        continue;
915
0
      }
916
0
      if (skip_prefix(arg, "--exclude=", &arg)) {
917
0
        add_ref_exclusion(&ref_excludes, arg);
918
0
        continue;
919
0
      }
920
0
      if (skip_prefix(arg, "--exclude-hidden=", &arg)) {
921
0
        exclude_hidden_refs(&ref_excludes, arg);
922
0
        continue;
923
0
      }
924
0
      if (!strcmp(arg, "--show-toplevel")) {
925
0
        const char *work_tree = get_git_work_tree();
926
0
        if (work_tree)
927
0
          print_path(work_tree, prefix, format, DEFAULT_UNMODIFIED);
928
0
        else
929
0
          die(_("this operation must be run in a work tree"));
930
0
        continue;
931
0
      }
932
0
      if (!strcmp(arg, "--show-superproject-working-tree")) {
933
0
        struct strbuf superproject = STRBUF_INIT;
934
0
        if (get_superproject_working_tree(&superproject))
935
0
          print_path(superproject.buf, prefix, format, DEFAULT_UNMODIFIED);
936
0
        strbuf_release(&superproject);
937
0
        continue;
938
0
      }
939
0
      if (!strcmp(arg, "--show-prefix")) {
940
0
        if (prefix)
941
0
          puts(prefix);
942
0
        else
943
0
          putchar('\n');
944
0
        continue;
945
0
      }
946
0
      if (!strcmp(arg, "--show-cdup")) {
947
0
        const char *pfx = prefix;
948
0
        if (!is_inside_work_tree()) {
949
0
          const char *work_tree =
950
0
            get_git_work_tree();
951
0
          if (work_tree)
952
0
            printf("%s\n", work_tree);
953
0
          continue;
954
0
        }
955
0
        while (pfx) {
956
0
          pfx = strchr(pfx, '/');
957
0
          if (pfx) {
958
0
            pfx++;
959
0
            printf("../");
960
0
          }
961
0
        }
962
0
        putchar('\n');
963
0
        continue;
964
0
      }
965
0
      if (!strcmp(arg, "--git-dir") ||
966
0
          !strcmp(arg, "--absolute-git-dir")) {
967
0
        const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
968
0
        char *cwd;
969
0
        int len;
970
0
        enum format_type wanted = format;
971
0
        if (arg[2] == 'g') { /* --git-dir */
972
0
          if (gitdir) {
973
0
            print_path(gitdir, prefix, format, DEFAULT_UNMODIFIED);
974
0
            continue;
975
0
          }
976
0
          if (!prefix) {
977
0
            print_path(".git", prefix, format, DEFAULT_UNMODIFIED);
978
0
            continue;
979
0
          }
980
0
        } else {   /* --absolute-git-dir */
981
0
          wanted = FORMAT_CANONICAL;
982
0
          if (!gitdir && !prefix)
983
0
            gitdir = ".git";
984
0
          if (gitdir) {
985
0
            struct strbuf realpath = STRBUF_INIT;
986
0
            strbuf_realpath(&realpath, gitdir, 1);
987
0
            puts(realpath.buf);
988
0
            strbuf_release(&realpath);
989
0
            continue;
990
0
          }
991
0
        }
992
0
        cwd = xgetcwd();
993
0
        len = strlen(cwd);
994
0
        strbuf_reset(&buf);
995
0
        strbuf_addf(&buf, "%s%s.git", cwd, len && cwd[len-1] != '/' ? "/" : "");
996
0
        free(cwd);
997
0
        print_path(buf.buf, prefix, wanted, DEFAULT_CANONICAL);
998
0
        continue;
999
0
      }
1000
0
      if (!strcmp(arg, "--git-common-dir")) {
1001
0
        print_path(get_git_common_dir(), prefix, format, DEFAULT_RELATIVE_IF_SHARED);
1002
0
        continue;
1003
0
      }
1004
0
      if (!strcmp(arg, "--is-inside-git-dir")) {
1005
0
        printf("%s\n", is_inside_git_dir() ? "true"
1006
0
            : "false");
1007
0
        continue;
1008
0
      }
1009
0
      if (!strcmp(arg, "--is-inside-work-tree")) {
1010
0
        printf("%s\n", is_inside_work_tree() ? "true"
1011
0
            : "false");
1012
0
        continue;
1013
0
      }
1014
0
      if (!strcmp(arg, "--is-bare-repository")) {
1015
0
        printf("%s\n", is_bare_repository() ? "true"
1016
0
            : "false");
1017
0
        continue;
1018
0
      }
1019
0
      if (!strcmp(arg, "--is-shallow-repository")) {
1020
0
        printf("%s\n",
1021
0
            is_repository_shallow(the_repository) ? "true"
1022
0
            : "false");
1023
0
        continue;
1024
0
      }
1025
0
      if (!strcmp(arg, "--shared-index-path")) {
1026
0
        if (repo_read_index(the_repository) < 0)
1027
0
          die(_("Could not read the index"));
1028
0
        if (the_index.split_index) {
1029
0
          const struct object_id *oid = &the_index.split_index->base_oid;
1030
0
          const char *path = git_path("sharedindex.%s", oid_to_hex(oid));
1031
0
          print_path(path, prefix, format, DEFAULT_RELATIVE);
1032
0
        }
1033
0
        continue;
1034
0
      }
1035
0
      if (skip_prefix(arg, "--since=", &arg)) {
1036
0
        show_datestring("--max-age=", arg);
1037
0
        continue;
1038
0
      }
1039
0
      if (skip_prefix(arg, "--after=", &arg)) {
1040
0
        show_datestring("--max-age=", arg);
1041
0
        continue;
1042
0
      }
1043
0
      if (skip_prefix(arg, "--before=", &arg)) {
1044
0
        show_datestring("--min-age=", arg);
1045
0
        continue;
1046
0
      }
1047
0
      if (skip_prefix(arg, "--until=", &arg)) {
1048
0
        show_datestring("--min-age=", arg);
1049
0
        continue;
1050
0
      }
1051
0
      if (opt_with_value(arg, "--show-object-format", &arg)) {
1052
0
        const char *val = arg ? arg : "storage";
1053
1054
0
        if (strcmp(val, "storage") &&
1055
0
            strcmp(val, "input") &&
1056
0
            strcmp(val, "output"))
1057
0
          die(_("unknown mode for --show-object-format: %s"),
1058
0
              arg);
1059
0
        puts(the_hash_algo->name);
1060
0
        continue;
1061
0
      }
1062
0
      if (!strcmp(arg, "--end-of-options")) {
1063
0
        seen_end_of_options = 1;
1064
0
        if (filter & (DO_FLAGS | DO_REVS))
1065
0
          show_file(arg, 0);
1066
0
        continue;
1067
0
      }
1068
0
      if (show_flag(arg) && verify)
1069
0
        die_no_single_rev(quiet);
1070
0
      continue;
1071
0
    }
1072
1073
    /* Not a flag argument */
1074
0
    if (try_difference(arg))
1075
0
      continue;
1076
0
    if (try_parent_shorthands(arg))
1077
0
      continue;
1078
0
    name = arg;
1079
0
    type = NORMAL;
1080
0
    if (*arg == '^') {
1081
0
      name++;
1082
0
      type = REVERSED;
1083
0
    }
1084
0
    if (!get_oid_with_context(the_repository, name,
1085
0
            flags, &oid, &unused)) {
1086
0
      if (verify)
1087
0
        revs_count++;
1088
0
      else
1089
0
        show_rev(type, &oid, name);
1090
0
      continue;
1091
0
    }
1092
0
    if (verify)
1093
0
      die_no_single_rev(quiet);
1094
0
    if (has_dashdash)
1095
0
      die(_("bad revision '%s'"), arg);
1096
0
    as_is = 1;
1097
0
    if (!show_file(arg, output_prefix))
1098
0
      continue;
1099
0
    verify_filename(prefix, arg, 1);
1100
0
  }
1101
0
  strbuf_release(&buf);
1102
0
  if (verify) {
1103
0
    if (revs_count == 1) {
1104
0
      show_rev(type, &oid, name);
1105
0
      return 0;
1106
0
    } else if (revs_count == 0 && show_default())
1107
0
      return 0;
1108
0
    die_no_single_rev(quiet);
1109
0
  } else
1110
0
    show_default();
1111
0
  return 0;
1112
0
}