rev-parse: introduce --exclude=<glob> to tame wildcards
[alt-git.git] / builtin / rev-parse.c
blobf52f8048bb7522792312e036a0ed6e92a48c0880
1 /*
2 * rev-parse.c
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"
15 #define DO_REVS 1
16 #define DO_NOREV 2
17 #define DO_FLAGS 4
18 #define DO_NONFLAGS 8
19 static int filter = ~0;
21 static const char *def;
23 #define NORMAL 0
24 #define REVERSED 1
25 static int show_type = NORMAL;
27 #define SHOW_SYMBOLIC_ASIS 1
28 #define SHOW_SYMBOLIC_FULL 2
29 static int symbolic;
30 static int abbrev;
31 static int abbrev_ref;
32 static int abbrev_ref_strict;
33 static int output_sq;
35 static struct string_list *ref_excludes;
38 * Some arguments are relevant "revision" arguments,
39 * others are about output format or other details.
40 * This sorts it all out.
42 static int is_rev_argument(const char *arg)
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
75 const char **p = rev_args;
77 /* accept -<digit>, like traditional "head" */
78 if ((*arg == '-') && isdigit(arg[1]))
79 return 1;
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;
93 /* Output argument as a string, either SQ or normal */
94 static void show(const char *arg)
96 if (output_sq) {
97 int sq = '\'', ch;
99 putchar(sq);
100 while ((ch = *arg++)) {
101 if (ch == sq)
102 fputs("'\\'", stdout);
103 putchar(ch);
105 putchar(sq);
106 putchar(' ');
108 else
109 puts(arg);
112 /* Like show(), but with a negation prefix according to type */
113 static void show_with_type(int type, const char *arg)
115 if (type != show_type)
116 putchar('^');
117 show(arg);
120 /* Output a revision, only if filter allows it */
121 static void show_rev(int type, const unsigned char *sha1, const char *name)
123 if (!(filter & DO_REVS))
124 return;
125 def = NULL;
127 if ((symbolic || abbrev_ref) && name) {
128 if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
129 unsigned char discard[20];
130 char *full;
132 switch (dwim_ref(name, strlen(name), discard, &full)) {
133 case 0:
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.
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;
152 } else {
153 show_with_type(type, name);
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));
162 /* Output a flag, only if filter allows it. */
163 static int show_flag(const char *arg)
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;
171 return 0;
174 static int show_default(void)
176 const char *s = def;
178 if (s) {
179 unsigned char sha1[20];
181 def = NULL;
182 if (!get_sha1(s, sha1)) {
183 show_rev(NORMAL, sha1, s);
184 return 1;
187 return 0;
190 static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
192 if (ref_excluded(ref_excludes, refname))
193 return 0;
194 show_rev(NORMAL, sha1, refname);
195 return 0;
198 static int anti_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
200 show_rev(REVERSED, sha1, refname);
201 return 0;
204 static int show_abbrev(const unsigned char *sha1, void *cb_data)
206 show_rev(NORMAL, sha1, NULL);
207 return 0;
210 static void show_datestring(const char *flag, const char *datestr)
212 static char buffer[100];
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);
221 static int show_file(const char *arg, int output_prefix)
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;
234 return 0;
237 static int try_difference(const char *arg)
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";
247 if (!(dotdot = strstr(arg, "..")))
248 return 0;
249 next = dotdot + 2;
250 this = arg;
251 symmetric = (*next == '.');
253 *dotdot = 0;
254 next += symmetric;
256 if (!*next)
257 next = head_by_default;
258 if (dotdot == arg)
259 this = head_by_default;
261 if (this == head_by_default && next == head_by_default &&
262 !symmetric) {
264 * Just ".."? That is not a range but the
265 * pathspec for the parent directory.
267 *dotdot = '.';
268 return 0;
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;
288 return 1;
290 *dotdot = '.';
291 return 0;
294 static int try_parent_shorthands(const char *arg)
296 char *dotdot;
297 unsigned char sha1[20];
298 struct commit *commit;
299 struct commit_list *parents;
300 int parents_only;
302 if ((dotdot = strstr(arg, "^!")))
303 parents_only = 0;
304 else if ((dotdot = strstr(arg, "^@")))
305 parents_only = 1;
307 if (!dotdot || dotdot[2])
308 return 0;
310 *dotdot = 0;
311 if (get_sha1_committish(arg, sha1))
312 return 0;
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);
321 return 1;
324 static int parseopt_dump(const struct option *o, const char *arg, int unset)
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);
337 return 0;
340 static const char *skipspaces(const char *s)
342 while (isspace(*s))
343 s++;
344 return s;
347 static int cmd_parseopt(int argc, const char **argv, const char *prefix)
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
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(),
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;
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);
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;
385 usage[unb++] = strbuf_detach(&sb, NULL);
388 /* parse: (<short>|<short>,<long>|<long>)[=?]? SP+ <help> */
389 while (strbuf_getline(&sb, stdin, '\n') != EOF) {
390 const char *s;
391 struct option *o;
393 if (!sb.len)
394 continue;
396 ALLOC_GROW(opts, onb + 1, osz);
397 memset(opts + onb, 0, sizeof(opts[onb]));
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;
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;
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);
439 strbuf_release(&sb);
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);
449 strbuf_addf(&parsed, " --");
450 sq_quote_argv(&parsed, argv, 0);
451 puts(parsed.buf);
452 return 0;
455 static int cmd_sq_quote(int argc, const char **argv)
457 struct strbuf buf = STRBUF_INIT;
459 if (argc)
460 sq_quote_argv(&buf, argv, 0);
461 printf("%s\n", buf.buf);
462 strbuf_release(&buf);
464 return 0;
467 static void die_no_single_rev(int quiet)
469 if (quiet)
470 exit(1);
471 else
472 die("Needed a single revision");
475 static const char builtin_rev_parse_usage[] =
476 N_("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.");
482 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
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;
489 if (argc > 1 && !strcmp("--parseopt", argv[1]))
490 return cmd_parseopt(argc - 1, argv + 1, prefix);
492 if (argc > 1 && !strcmp("--sq-quote", argv[1]))
493 return cmd_sq_quote(argc - 2, argv + 2);
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;
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;
510 if (argc > 1 && !strcmp("-h", argv[1]))
511 usage(builtin_rev_parse_usage);
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];
518 if (as_is) {
519 if (show_file(arg, output_prefix) && as_is < 2)
520 verify_filename(prefix, arg, 0);
521 continue;
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]);
530 continue;
532 if (!prefixcmp(arg, "-n")) {
533 if ((filter & DO_FLAGS) && (filter & DO_REVS))
534 show(arg);
535 continue;
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;
546 if (!strcmp(arg, "--default")) {
547 def = argv[i+1];
548 i++;
549 continue;
551 if (!strcmp(arg, "--prefix")) {
552 prefix = argv[i+1];
553 startup_info->prefix = prefix;
554 output_prefix = 1;
555 i++;
556 continue;
558 if (!strcmp(arg, "--revs-only")) {
559 filter &= ~DO_NOREV;
560 continue;
562 if (!strcmp(arg, "--no-revs")) {
563 filter &= ~DO_REVS;
564 continue;
566 if (!strcmp(arg, "--flags")) {
567 filter &= ~DO_NONFLAGS;
568 continue;
570 if (!strcmp(arg, "--no-flags")) {
571 filter &= ~DO_FLAGS;
572 continue;
574 if (!strcmp(arg, "--verify")) {
575 filter &= ~(DO_FLAGS|DO_NOREV);
576 verify = 1;
577 continue;
579 if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
580 quiet = 1;
581 continue;
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;
596 if (!strcmp(arg, "--sq")) {
597 output_sq = 1;
598 continue;
600 if (!strcmp(arg, "--not")) {
601 show_type ^= REVERSED;
602 continue;
604 if (!strcmp(arg, "--symbolic")) {
605 symbolic = SHOW_SYMBOLIC_ASIS;
606 continue;
608 if (!strcmp(arg, "--symbolic-full-name")) {
609 symbolic = SHOW_SYMBOLIC_FULL;
610 continue;
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);
624 continue;
626 if (!strcmp(arg, "--all")) {
627 for_each_ref(show_reference, NULL);
628 continue;
630 if (!prefixcmp(arg, "--disambiguate=")) {
631 for_each_abbrev(arg + 15, show_abbrev, NULL);
632 continue;
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;
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;
645 if (!strcmp(arg, "--branches")) {
646 for_each_branch_ref(show_reference, NULL);
647 clear_ref_exclusion(&ref_excludes);
648 continue;
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;
656 if (!strcmp(arg, "--tags")) {
657 for_each_tag_ref(show_reference, NULL);
658 clear_ref_exclusion(&ref_excludes);
659 continue;
661 if (!prefixcmp(arg, "--glob=")) {
662 for_each_glob_ref(show_reference, arg + 7, NULL);
663 clear_ref_exclusion(&ref_excludes);
664 continue;
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;
672 if (!strcmp(arg, "--remotes")) {
673 for_each_remote_ref(show_reference, NULL);
674 clear_ref_exclusion(&ref_excludes);
675 continue;
677 if (!prefixcmp(arg, "--exclude=")) {
678 add_ref_exclusion(&ref_excludes, arg + 10);
679 continue;
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;
687 if (!strcmp(arg, "--show-prefix")) {
688 if (prefix)
689 puts(prefix);
690 else
691 putchar('\n');
692 continue;
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;
703 while (pfx) {
704 pfx = strchr(pfx, '/');
705 if (pfx) {
706 pfx++;
707 printf("../");
710 putchar('\n');
711 continue;
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;
721 if (!prefix) {
722 puts(".git");
723 continue;
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;
731 if (!strcmp(arg, "--is-inside-git-dir")) {
732 printf("%s\n", is_inside_git_dir() ? "true"
733 : "false");
734 continue;
736 if (!strcmp(arg, "--is-inside-work-tree")) {
737 printf("%s\n", is_inside_work_tree() ? "true"
738 : "false");
739 continue;
741 if (!strcmp(arg, "--is-bare-repository")) {
742 printf("%s\n", is_bare_repository() ? "true"
743 : "false");
744 continue;
746 if (!prefixcmp(arg, "--since=")) {
747 show_datestring("--max-age=", arg+8);
748 continue;
750 if (!prefixcmp(arg, "--after=")) {
751 show_datestring("--max-age=", arg+8);
752 continue;
754 if (!prefixcmp(arg, "--before=")) {
755 show_datestring("--min-age=", arg+9);
756 continue;
758 if (!prefixcmp(arg, "--until=")) {
759 show_datestring("--min-age=", arg+8);
760 continue;
762 if (show_flag(arg) && verify)
763 die_no_single_rev(quiet);
764 continue;
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;
778 if (!get_sha1(name, sha1)) {
779 if (verify)
780 revs_count++;
781 else
782 show_rev(type, sha1, name);
783 continue;
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);
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;