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