1/*
2 * rev-parse.c
3 *
4 * Copyright (C) Linus Torvalds, 2005
5 */
6#include "cache.h"
7#include "commit.h"
8#include "refs.h"
9#include "quote.h"
10#include "builtin.h"
11#include "parse-options.h"
12#include "diff.h"
13#include "revision.h"
14
15#define DO_REVS 1
16#define DO_NOREV 2
17#define DO_FLAGS 4
18#define DO_NONFLAGS 8
19static int filter = ~0;
20
21static const char *def;
22
23#define NORMAL 0
24#define REVERSED 1
25static int show_type = NORMAL;
26
27#define SHOW_SYMBOLIC_ASIS 1
28#define SHOW_SYMBOLIC_FULL 2
29static int symbolic;
30static int abbrev;
31static int abbrev_ref;
32static int abbrev_ref_strict;
33static int output_sq;
34
35static struct string_list *ref_excludes;
36
37/*
38 * Some arguments are relevant "revision" arguments,
39 * others are about output format or other details.
40 * This sorts it all out.
41 */
42static int is_rev_argument(const char *arg)
43{
44 static const char *rev_args[] = {
45 "--all",
46 "--bisect",
47 "--dense",
48 "--branches=",
49 "--branches",
50 "--header",
51 "--ignore-missing",
52 "--max-age=",
53 "--max-count=",
54 "--min-age=",
55 "--no-merges",
56 "--min-parents=",
57 "--no-min-parents",
58 "--max-parents=",
59 "--no-max-parents",
60 "--objects",
61 "--objects-edge",
62 "--parents",
63 "--pretty",
64 "--remotes=",
65 "--remotes",
66 "--glob=",
67 "--sparse",
68 "--tags=",
69 "--tags",
70 "--topo-order",
71 "--date-order",
72 "--unpacked",
73 NULL
74 };
75 const char **p = rev_args;
76
77 /* accept -<digit>, like traditional "head" */
78 if ((*arg == '-') && isdigit(arg[1]))
79 return 1;
80
81 for (;;) {
82 const char *str = *p++;
83 int len;
84 if (!str)
85 return 0;
86 len = strlen(str);
87 if (!strcmp(arg, str) ||
88 (str[len-1] == '=' && !strncmp(arg, str, len)))
89 return 1;
90 }
91}
92
93/* Output argument as a string, either SQ or normal */
94static void show(const char *arg)
95{
96 if (output_sq) {
97 int sq = '\'', ch;
98
99 putchar(sq);
100 while ((ch = *arg++)) {
101 if (ch == sq)
102 fputs("'\\'", stdout);
103 putchar(ch);
104 }
105 putchar(sq);
106 putchar(' ');
107 }
108 else
109 puts(arg);
110}
111
112/* Like show(), but with a negation prefix according to type */
113static void show_with_type(int type, const char *arg)
114{
115 if (type != show_type)
116 putchar('^');
117 show(arg);
118}
119
120/* Output a revision, only if filter allows it */
121static void show_rev(int type, const unsigned char *sha1, const char *name)
122{
123 if (!(filter & DO_REVS))
124 return;
125 def = NULL;
126
127 if ((symbolic || abbrev_ref) && name) {
128 if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
129 unsigned char discard[20];
130 char *full;
131
132 switch (dwim_ref(name, strlen(name), discard, &full)) {
133 case 0:
134 /*
135 * Not found -- not a ref. We could
136 * emit "name" here, but symbolic-full
137 * users are interested in finding the
138 * refs spelled in full, and they would
139 * need to filter non-refs if we did so.
140 */
141 break;
142 case 1: /* happy */
143 if (abbrev_ref)
144 full = shorten_unambiguous_ref(full,
145 abbrev_ref_strict);
146 show_with_type(type, full);
147 break;
148 default: /* ambiguous */
149 error("refname '%s' is ambiguous", name);
150 break;
151 }
152 } else {
153 show_with_type(type, name);
154 }
155 }
156 else if (abbrev)
157 show_with_type(type, find_unique_abbrev(sha1, abbrev));
158 else
159 show_with_type(type, sha1_to_hex(sha1));
160}
161
162/* Output a flag, only if filter allows it. */
163static int show_flag(const char *arg)
164{
165 if (!(filter & DO_FLAGS))
166 return 0;
167 if (filter & (is_rev_argument(arg) ? DO_REVS : DO_NOREV)) {
168 show(arg);
169 return 1;
170 }
171 return 0;
172}
173
174static int show_default(void)
175{
176 const char *s = def;
177
178 if (s) {
179 unsigned char sha1[20];
180
181 def = NULL;
182 if (!get_sha1(s, sha1)) {
183 show_rev(NORMAL, sha1, s);
184 return 1;
185 }
186 }
187 return 0;
188}
189
190static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
191{
192 if (ref_excluded(ref_excludes, refname))
193 return 0;
194 show_rev(NORMAL, sha1, refname);
195 return 0;
196}
197
198static int anti_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
199{
200 show_rev(REVERSED, sha1, refname);
201 return 0;
202}
203
204static int show_abbrev(const unsigned char *sha1, void *cb_data)
205{
206 show_rev(NORMAL, sha1, NULL);
207 return 0;
208}
209
210static void show_datestring(const char *flag, const char *datestr)
211{
212 static char buffer[100];
213
214 /* date handling requires both flags and revs */
215 if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
216 return;
217 snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
218 show(buffer);
219}
220
221static int show_file(const char *arg, int output_prefix)
222{
223 show_default();
224 if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
225 if (output_prefix) {
226 const char *prefix = startup_info->prefix;
227 show(prefix_filename(prefix,
228 prefix ? strlen(prefix) : 0,
229 arg));
230 } else
231 show(arg);
232 return 1;
233 }
234 return 0;
235}
236
237static int try_difference(const char *arg)
238{
239 char *dotdot;
240 unsigned char sha1[20];
241 unsigned char end[20];
242 const char *next;
243 const char *this;
244 int symmetric;
245 static const char head_by_default[] = "HEAD";
246
247 if (!(dotdot = strstr(arg, "..")))
248 return 0;
249 next = dotdot + 2;
250 this = arg;
251 symmetric = (*next == '.');
252
253 *dotdot = 0;
254 next += symmetric;
255
256 if (!*next)
257 next = head_by_default;
258 if (dotdot == arg)
259 this = head_by_default;
260
261 if (this == head_by_default && next == head_by_default &&
262 !symmetric) {
263 /*
264 * Just ".."? That is not a range but the
265 * pathspec for the parent directory.
266 */
267 *dotdot = '.';
268 return 0;
269 }
270
271 if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) {
272 show_rev(NORMAL, end, next);
273 show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
274 if (symmetric) {
275 struct commit_list *exclude;
276 struct commit *a, *b;
277 a = lookup_commit_reference(sha1);
278 b = lookup_commit_reference(end);
279 exclude = get_merge_bases(a, b, 1);
280 while (exclude) {
281 struct commit_list *n = exclude->next;
282 show_rev(REVERSED,
283 exclude->item->object.sha1,NULL);
284 free(exclude);
285 exclude = n;
286 }
287 }
288 return 1;
289 }
290 *dotdot = '.';
291 return 0;
292}
293
294static int try_parent_shorthands(const char *arg)
295{
296 char *dotdot;
297 unsigned char sha1[20];
298 struct commit *commit;
299 struct commit_list *parents;
300 int parents_only;
301
302 if ((dotdot = strstr(arg, "^!")))
303 parents_only = 0;
304 else if ((dotdot = strstr(arg, "^@")))
305 parents_only = 1;
306
307 if (!dotdot || dotdot[2])
308 return 0;
309
310 *dotdot = 0;
311 if (get_sha1_committish(arg, sha1))
312 return 0;
313
314 if (!parents_only)
315 show_rev(NORMAL, sha1, arg);
316 commit = lookup_commit_reference(sha1);
317 for (parents = commit->parents; parents; parents = parents->next)
318 show_rev(parents_only ? NORMAL : REVERSED,
319 parents->item->object.sha1, arg);
320
321 return 1;
322}
323
324static int parseopt_dump(const struct option *o, const char *arg, int unset)
325{
326 struct strbuf *parsed = o->value;
327 if (unset)
328 strbuf_addf(parsed, " --no-%s", o->long_name);
329 else if (o->short_name)
330 strbuf_addf(parsed, " -%c", o->short_name);
331 else
332 strbuf_addf(parsed, " --%s", o->long_name);
333 if (arg) {
334 strbuf_addch(parsed, ' ');
335 sq_quote_buf(parsed, arg);
336 }
337 return 0;
338}
339
340static const char *skipspaces(const char *s)
341{
342 while (isspace(*s))
343 s++;
344 return s;
345}
346
347static int cmd_parseopt(int argc, const char **argv, const char *prefix)
348{
349 static int keep_dashdash = 0, stop_at_non_option = 0;
350 static char const * const parseopt_usage[] = {
351 N_("git rev-parse --parseopt [options] -- [<args>...]"),
352 NULL
353 };
354 static struct option parseopt_opts[] = {
355 OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
356 N_("keep the `--` passed as an arg")),
357 OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
358 N_("stop parsing after the "
359 "first non-option argument")),
360 OPT_END(),
361 };
362
363 struct strbuf sb = STRBUF_INIT, parsed = STRBUF_INIT;
364 const char **usage = NULL;
365 struct option *opts = NULL;
366 int onb = 0, osz = 0, unb = 0, usz = 0;
367
368 strbuf_addstr(&parsed, "set --");
369 argc = parse_options(argc, argv, prefix, parseopt_opts, parseopt_usage,
370 PARSE_OPT_KEEP_DASHDASH);
371 if (argc < 1 || strcmp(argv[0], "--"))
372 usage_with_options(parseopt_usage, parseopt_opts);
373
374 /* get the usage up to the first line with a -- on it */
375 for (;;) {
376 if (strbuf_getline(&sb, stdin, '\n') == EOF)
377 die("premature end of input");
378 ALLOC_GROW(usage, unb + 1, usz);
379 if (!strcmp("--", sb.buf)) {
380 if (unb < 1)
381 die("no usage string given before the `--' separator");
382 usage[unb] = NULL;
383 break;
384 }
385 usage[unb++] = strbuf_detach(&sb, NULL);
386 }
387
388 /* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */
389 while (strbuf_getline(&sb, stdin, '\n') != EOF) {
390 const char *s;
391 struct option *o;
392
393 if (!sb.len)
394 continue;
395
396 ALLOC_GROW(opts, onb + 1, osz);
397 memset(opts + onb, 0, sizeof(opts[onb]));
398
399 o = &opts[onb++];
400 s = strchr(sb.buf, ' ');
401 if (!s || *sb.buf == ' ') {
402 o->type = OPTION_GROUP;
403 o->help = xstrdup(skipspaces(sb.buf));
404 continue;
405 }
406
407 o->type = OPTION_CALLBACK;
408 o->help = xstrdup(skipspaces(s));
409 o->value = &parsed;
410 o->flags = PARSE_OPT_NOARG;
411 o->callback = &parseopt_dump;
412 while (s > sb.buf && strchr("*=?!", s[-1])) {
413 switch (*--s) {
414 case '=':
415 o->flags &= ~PARSE_OPT_NOARG;
416 break;
417 case '?':
418 o->flags &= ~PARSE_OPT_NOARG;
419 o->flags |= PARSE_OPT_OPTARG;
420 break;
421 case '!':
422 o->flags |= PARSE_OPT_NONEG;
423 break;
424 case '*':
425 o->flags |= PARSE_OPT_HIDDEN;
426 break;
427 }
428 }
429
430 if (s - sb.buf == 1) /* short option only */
431 o->short_name = *sb.buf;
432 else if (sb.buf[1] != ',') /* long option only */
433 o->long_name = xmemdupz(sb.buf, s - sb.buf);
434 else {
435 o->short_name = *sb.buf;
436 o->long_name = xmemdupz(sb.buf + 2, s - sb.buf - 2);
437 }
438 }
439 strbuf_release(&sb);
440
441 /* put an OPT_END() */
442 ALLOC_GROW(opts, onb + 1, osz);
443 memset(opts + onb, 0, sizeof(opts[onb]));
444 argc = parse_options(argc, argv, prefix, opts, usage,
445 (keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0) |
446 (stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0) |
447 PARSE_OPT_SHELL_EVAL);
448
449 strbuf_addf(&parsed, " --");
450 sq_quote_argv(&parsed, argv, 0);
451 puts(parsed.buf);
452 return 0;
453}
454
455static int cmd_sq_quote(int argc, const char **argv)
456{
457 struct strbuf buf = STRBUF_INIT;
458
459 if (argc)
460 sq_quote_argv(&buf, argv, 0);
461 printf("%s\n", buf.buf);
462 strbuf_release(&buf);
463
464 return 0;
465}
466
467static void die_no_single_rev(int quiet)
468{
469 if (quiet)
470 exit(1);
471 else
472 die("Needed a single revision");
473}
474
475static const char builtin_rev_parse_usage[] =
476N_("git rev-parse --parseopt [options] -- [<args>...]\n"
477 " or: git rev-parse --sq-quote [<arg>...]\n"
478 " or: git rev-parse [options] [<arg>...]\n"
479 "\n"
480 "Run \"git rev-parse --parseopt -h\" for more information on the first usage.");
481
482int cmd_rev_parse(int argc, const char **argv, const char *prefix)
483{
484 int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
485 int output_prefix = 0;
486 unsigned char sha1[20];
487 const char *name = NULL;
488
489 if (argc > 1 && !strcmp("--parseopt", argv[1]))
490 return cmd_parseopt(argc - 1, argv + 1, prefix);
491
492 if (argc > 1 && !strcmp("--sq-quote", argv[1]))
493 return cmd_sq_quote(argc - 2, argv + 2);
494
495 if (argc == 2 && !strcmp("--local-env-vars", argv[1])) {
496 int i;
497 for (i = 0; local_repo_env[i]; i++)
498 printf("%s\n", local_repo_env[i]);
499 return 0;
500 }
501
502 if (argc > 2 && !strcmp(argv[1], "--resolve-git-dir")) {
503 const char *gitdir = resolve_gitdir(argv[2]);
504 if (!gitdir)
505 die("not a gitdir '%s'", argv[2]);
506 puts(gitdir);
507 return 0;
508 }
509
510 if (argc > 1 && !strcmp("-h", argv[1]))
511 usage(builtin_rev_parse_usage);
512
513 prefix = setup_git_directory();
514 git_config(git_default_config, NULL);
515 for (i = 1; i < argc; i++) {
516 const char *arg = argv[i];
517
518 if (as_is) {
519 if (show_file(arg, output_prefix) && as_is < 2)
520 verify_filename(prefix, arg, 0);
521 continue;
522 }
523 if (!strcmp(arg,"-n")) {
524 if (++i >= argc)
525 die("-n requires an argument");
526 if ((filter & DO_FLAGS) && (filter & DO_REVS)) {
527 show(arg);
528 show(argv[i]);
529 }
530 continue;
531 }
532 if (!prefixcmp(arg, "-n")) {
533 if ((filter & DO_FLAGS) && (filter & DO_REVS))
534 show(arg);
535 continue;
536 }
537
538 if (*arg == '-') {
539 if (!strcmp(arg, "--")) {
540 as_is = 2;
541 /* Pass on the "--" if we show anything but files.. */
542 if (filter & (DO_FLAGS | DO_REVS))
543 show_file(arg, 0);
544 continue;
545 }
546 if (!strcmp(arg, "--default")) {
547 def = argv[i+1];
548 i++;
549 continue;
550 }
551 if (!strcmp(arg, "--prefix")) {
552 prefix = argv[i+1];
553 startup_info->prefix = prefix;
554 output_prefix = 1;
555 i++;
556 continue;
557 }
558 if (!strcmp(arg, "--revs-only")) {
559 filter &= ~DO_NOREV;
560 continue;
561 }
562 if (!strcmp(arg, "--no-revs")) {
563 filter &= ~DO_REVS;
564 continue;
565 }
566 if (!strcmp(arg, "--flags")) {
567 filter &= ~DO_NONFLAGS;
568 continue;
569 }
570 if (!strcmp(arg, "--no-flags")) {
571 filter &= ~DO_FLAGS;
572 continue;
573 }
574 if (!strcmp(arg, "--verify")) {
575 filter &= ~(DO_FLAGS|DO_NOREV);
576 verify = 1;
577 continue;
578 }
579 if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
580 quiet = 1;
581 continue;
582 }
583 if (!strcmp(arg, "--short") ||
584 !prefixcmp(arg, "--short=")) {
585 filter &= ~(DO_FLAGS|DO_NOREV);
586 verify = 1;
587 abbrev = DEFAULT_ABBREV;
588 if (arg[7] == '=')
589 abbrev = strtoul(arg + 8, NULL, 10);
590 if (abbrev < MINIMUM_ABBREV)
591 abbrev = MINIMUM_ABBREV;
592 else if (40 <= abbrev)
593 abbrev = 40;
594 continue;
595 }
596 if (!strcmp(arg, "--sq")) {
597 output_sq = 1;
598 continue;
599 }
600 if (!strcmp(arg, "--not")) {
601 show_type ^= REVERSED;
602 continue;
603 }
604 if (!strcmp(arg, "--symbolic")) {
605 symbolic = SHOW_SYMBOLIC_ASIS;
606 continue;
607 }
608 if (!strcmp(arg, "--symbolic-full-name")) {
609 symbolic = SHOW_SYMBOLIC_FULL;
610 continue;
611 }
612 if (!prefixcmp(arg, "--abbrev-ref") &&
613 (!arg[12] || arg[12] == '=')) {
614 abbrev_ref = 1;
615 abbrev_ref_strict = warn_ambiguous_refs;
616 if (arg[12] == '=') {
617 if (!strcmp(arg + 13, "strict"))
618 abbrev_ref_strict = 1;
619 else if (!strcmp(arg + 13, "loose"))
620 abbrev_ref_strict = 0;
621 else
622 die("unknown mode for %s", arg);
623 }
624 continue;
625 }
626 if (!strcmp(arg, "--all")) {
627 for_each_ref(show_reference, NULL);
628 continue;
629 }
630 if (!prefixcmp(arg, "--disambiguate=")) {
631 for_each_abbrev(arg + 15, show_abbrev, NULL);
632 continue;
633 }
634 if (!strcmp(arg, "--bisect")) {
635 for_each_ref_in("refs/bisect/bad", show_reference, NULL);
636 for_each_ref_in("refs/bisect/good", anti_reference, NULL);
637 continue;
638 }
639 if (!prefixcmp(arg, "--branches=")) {
640 for_each_glob_ref_in(show_reference, arg + 11,
641 "refs/heads/", NULL);
642 clear_ref_exclusion(&ref_excludes);
643 continue;
644 }
645 if (!strcmp(arg, "--branches")) {
646 for_each_branch_ref(show_reference, NULL);
647 clear_ref_exclusion(&ref_excludes);
648 continue;
649 }
650 if (!prefixcmp(arg, "--tags=")) {
651 for_each_glob_ref_in(show_reference, arg + 7,
652 "refs/tags/", NULL);
653 clear_ref_exclusion(&ref_excludes);
654 continue;
655 }
656 if (!strcmp(arg, "--tags")) {
657 for_each_tag_ref(show_reference, NULL);
658 clear_ref_exclusion(&ref_excludes);
659 continue;
660 }
661 if (!prefixcmp(arg, "--glob=")) {
662 for_each_glob_ref(show_reference, arg + 7, NULL);
663 clear_ref_exclusion(&ref_excludes);
664 continue;
665 }
666 if (!prefixcmp(arg, "--remotes=")) {
667 for_each_glob_ref_in(show_reference, arg + 10,
668 "refs/remotes/", NULL);
669 clear_ref_exclusion(&ref_excludes);
670 continue;
671 }
672 if (!strcmp(arg, "--remotes")) {
673 for_each_remote_ref(show_reference, NULL);
674 clear_ref_exclusion(&ref_excludes);
675 continue;
676 }
677 if (!prefixcmp(arg, "--exclude=")) {
678 add_ref_exclusion(&ref_excludes, arg + 10);
679 continue;
680 }
681 if (!strcmp(arg, "--show-toplevel")) {
682 const char *work_tree = get_git_work_tree();
683 if (work_tree)
684 puts(work_tree);
685 continue;
686 }
687 if (!strcmp(arg, "--show-prefix")) {
688 if (prefix)
689 puts(prefix);
690 else
691 putchar('\n');
692 continue;
693 }
694 if (!strcmp(arg, "--show-cdup")) {
695 const char *pfx = prefix;
696 if (!is_inside_work_tree()) {
697 const char *work_tree =
698 get_git_work_tree();
699 if (work_tree)
700 printf("%s\n", work_tree);
701 continue;
702 }
703 while (pfx) {
704 pfx = strchr(pfx, '/');
705 if (pfx) {
706 pfx++;
707 printf("../");
708 }
709 }
710 putchar('\n');
711 continue;
712 }
713 if (!strcmp(arg, "--git-dir")) {
714 const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
715 static char cwd[PATH_MAX];
716 int len;
717 if (gitdir) {
718 puts(gitdir);
719 continue;
720 }
721 if (!prefix) {
722 puts(".git");
723 continue;
724 }
725 if (!getcwd(cwd, PATH_MAX))
726 die_errno("unable to get current working directory");
727 len = strlen(cwd);
728 printf("%s%s.git\n", cwd, len && cwd[len-1] != '/' ? "/" : "");
729 continue;
730 }
731 if (!strcmp(arg, "--is-inside-git-dir")) {
732 printf("%s\n", is_inside_git_dir() ? "true"
733 : "false");
734 continue;
735 }
736 if (!strcmp(arg, "--is-inside-work-tree")) {
737 printf("%s\n", is_inside_work_tree() ? "true"
738 : "false");
739 continue;
740 }
741 if (!strcmp(arg, "--is-bare-repository")) {
742 printf("%s\n", is_bare_repository() ? "true"
743 : "false");
744 continue;
745 }
746 if (!prefixcmp(arg, "--since=")) {
747 show_datestring("--max-age=", arg+8);
748 continue;
749 }
750 if (!prefixcmp(arg, "--after=")) {
751 show_datestring("--max-age=", arg+8);
752 continue;
753 }
754 if (!prefixcmp(arg, "--before=")) {
755 show_datestring("--min-age=", arg+9);
756 continue;
757 }
758 if (!prefixcmp(arg, "--until=")) {
759 show_datestring("--min-age=", arg+8);
760 continue;
761 }
762 if (show_flag(arg) && verify)
763 die_no_single_rev(quiet);
764 continue;
765 }
766
767 /* Not a flag argument */
768 if (try_difference(arg))
769 continue;
770 if (try_parent_shorthands(arg))
771 continue;
772 name = arg;
773 type = NORMAL;
774 if (*arg == '^') {
775 name++;
776 type = REVERSED;
777 }
778 if (!get_sha1(name, sha1)) {
779 if (verify)
780 revs_count++;
781 else
782 show_rev(type, sha1, name);
783 continue;
784 }
785 if (verify)
786 die_no_single_rev(quiet);
787 as_is = 1;
788 if (!show_file(arg, output_prefix))
789 continue;
790 verify_filename(prefix, arg, 1);
791 }
792 if (verify) {
793 if (revs_count == 1) {
794 show_rev(type, sha1, name);
795 return 0;
796 } else if (revs_count == 0 && show_default())
797 return 0;
798 die_no_single_rev(quiet);
799 } else
800 show_default();
801 return 0;
802}