Coverage Report

Created: 2024-09-08 06:23

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