builtin / tag.con commit update-ref.c: extract a new function, parse_next_sha1() (3afcc46)
   1/*
   2 * Builtin "git tag"
   3 *
   4 * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
   5 *                    Carlos Rica <jasampler@gmail.com>
   6 * Based on git-tag.sh and mktag.c by Linus Torvalds.
   7 */
   8
   9#include "cache.h"
  10#include "builtin.h"
  11#include "refs.h"
  12#include "tag.h"
  13#include "run-command.h"
  14#include "parse-options.h"
  15#include "diff.h"
  16#include "revision.h"
  17#include "gpg-interface.h"
  18#include "sha1-array.h"
  19#include "column.h"
  20
  21static const char * const git_tag_usage[] = {
  22        N_("git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]"),
  23        N_("git tag -d <tagname>..."),
  24        N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>] "
  25                "\n\t\t[<pattern>...]"),
  26        N_("git tag -v <tagname>..."),
  27        NULL
  28};
  29
  30#define STRCMP_SORT     0       /* must be zero */
  31#define VERCMP_SORT     1
  32#define SORT_MASK       0x7fff
  33#define REVERSE_SORT    0x8000
  34
  35struct tag_filter {
  36        const char **patterns;
  37        int lines;
  38        int sort;
  39        struct string_list tags;
  40        struct commit_list *with_commit;
  41};
  42
  43static struct sha1_array points_at;
  44static unsigned int colopts;
  45
  46static int match_pattern(const char **patterns, const char *ref)
  47{
  48        /* no pattern means match everything */
  49        if (!*patterns)
  50                return 1;
  51        for (; *patterns; patterns++)
  52                if (!wildmatch(*patterns, ref, 0, NULL))
  53                        return 1;
  54        return 0;
  55}
  56
  57static const unsigned char *match_points_at(const char *refname,
  58                                            const unsigned char *sha1)
  59{
  60        const unsigned char *tagged_sha1 = NULL;
  61        struct object *obj;
  62
  63        if (sha1_array_lookup(&points_at, sha1) >= 0)
  64                return sha1;
  65        obj = parse_object(sha1);
  66        if (!obj)
  67                die(_("malformed object at '%s'"), refname);
  68        if (obj->type == OBJ_TAG)
  69                tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
  70        if (tagged_sha1 && sha1_array_lookup(&points_at, tagged_sha1) >= 0)
  71                return tagged_sha1;
  72        return NULL;
  73}
  74
  75static int in_commit_list(const struct commit_list *want, struct commit *c)
  76{
  77        for (; want; want = want->next)
  78                if (!hashcmp(want->item->object.sha1, c->object.sha1))
  79                        return 1;
  80        return 0;
  81}
  82
  83static int contains_recurse(struct commit *candidate,
  84                            const struct commit_list *want)
  85{
  86        struct commit_list *p;
  87
  88        /* was it previously marked as containing a want commit? */
  89        if (candidate->object.flags & TMP_MARK)
  90                return 1;
  91        /* or marked as not possibly containing a want commit? */
  92        if (candidate->object.flags & UNINTERESTING)
  93                return 0;
  94        /* or are we it? */
  95        if (in_commit_list(want, candidate))
  96                return 1;
  97
  98        if (parse_commit(candidate) < 0)
  99                return 0;
 100
 101        /* Otherwise recurse and mark ourselves for future traversals. */
 102        for (p = candidate->parents; p; p = p->next) {
 103                if (contains_recurse(p->item, want)) {
 104                        candidate->object.flags |= TMP_MARK;
 105                        return 1;
 106                }
 107        }
 108        candidate->object.flags |= UNINTERESTING;
 109        return 0;
 110}
 111
 112static int contains(struct commit *candidate, const struct commit_list *want)
 113{
 114        return contains_recurse(candidate, want);
 115}
 116
 117static void show_tag_lines(const unsigned char *sha1, int lines)
 118{
 119        int i;
 120        unsigned long size;
 121        enum object_type type;
 122        char *buf, *sp, *eol;
 123        size_t len;
 124
 125        buf = read_sha1_file(sha1, &type, &size);
 126        if (!buf)
 127                die_errno("unable to read object %s", sha1_to_hex(sha1));
 128        if (type != OBJ_COMMIT && type != OBJ_TAG)
 129                goto free_return;
 130        if (!size)
 131                die("an empty %s object %s?",
 132                    typename(type), sha1_to_hex(sha1));
 133
 134        /* skip header */
 135        sp = strstr(buf, "\n\n");
 136        if (!sp)
 137                goto free_return;
 138
 139        /* only take up to "lines" lines, and strip the signature from a tag */
 140        if (type == OBJ_TAG)
 141                size = parse_signature(buf, size);
 142        for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
 143                if (i)
 144                        printf("\n    ");
 145                eol = memchr(sp, '\n', size - (sp - buf));
 146                len = eol ? eol - sp : size - (sp - buf);
 147                fwrite(sp, len, 1, stdout);
 148                if (!eol)
 149                        break;
 150                sp = eol + 1;
 151        }
 152free_return:
 153        free(buf);
 154}
 155
 156static int show_reference(const char *refname, const unsigned char *sha1,
 157                          int flag, void *cb_data)
 158{
 159        struct tag_filter *filter = cb_data;
 160
 161        if (match_pattern(filter->patterns, refname)) {
 162                if (filter->with_commit) {
 163                        struct commit *commit;
 164
 165                        commit = lookup_commit_reference_gently(sha1, 1);
 166                        if (!commit)
 167                                return 0;
 168                        if (!contains(commit, filter->with_commit))
 169                                return 0;
 170                }
 171
 172                if (points_at.nr && !match_points_at(refname, sha1))
 173                        return 0;
 174
 175                if (!filter->lines) {
 176                        if (filter->sort)
 177                                string_list_append(&filter->tags, refname);
 178                        else
 179                                printf("%s\n", refname);
 180                        return 0;
 181                }
 182                printf("%-15s ", refname);
 183                show_tag_lines(sha1, filter->lines);
 184                putchar('\n');
 185        }
 186
 187        return 0;
 188}
 189
 190static int sort_by_version(const void *a_, const void *b_)
 191{
 192        const struct string_list_item *a = a_;
 193        const struct string_list_item *b = b_;
 194        return versioncmp(a->string, b->string);
 195}
 196
 197static int list_tags(const char **patterns, int lines,
 198                     struct commit_list *with_commit, int sort)
 199{
 200        struct tag_filter filter;
 201
 202        filter.patterns = patterns;
 203        filter.lines = lines;
 204        filter.sort = sort;
 205        filter.with_commit = with_commit;
 206        memset(&filter.tags, 0, sizeof(filter.tags));
 207        filter.tags.strdup_strings = 1;
 208
 209        for_each_tag_ref(show_reference, (void *) &filter);
 210        if (sort) {
 211                int i;
 212                if ((sort & SORT_MASK) == VERCMP_SORT)
 213                        qsort(filter.tags.items, filter.tags.nr,
 214                              sizeof(struct string_list_item), sort_by_version);
 215                if (sort & REVERSE_SORT)
 216                        for (i = filter.tags.nr - 1; i >= 0; i--)
 217                                printf("%s\n", filter.tags.items[i].string);
 218                else
 219                        for (i = 0; i < filter.tags.nr; i++)
 220                                printf("%s\n", filter.tags.items[i].string);
 221                string_list_clear(&filter.tags, 0);
 222        }
 223        return 0;
 224}
 225
 226typedef int (*each_tag_name_fn)(const char *name, const char *ref,
 227                                const unsigned char *sha1);
 228
 229static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
 230{
 231        const char **p;
 232        char ref[PATH_MAX];
 233        int had_error = 0;
 234        unsigned char sha1[20];
 235
 236        for (p = argv; *p; p++) {
 237                if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
 238                                        >= sizeof(ref)) {
 239                        error(_("tag name too long: %.*s..."), 50, *p);
 240                        had_error = 1;
 241                        continue;
 242                }
 243                if (read_ref(ref, sha1)) {
 244                        error(_("tag '%s' not found."), *p);
 245                        had_error = 1;
 246                        continue;
 247                }
 248                if (fn(*p, ref, sha1))
 249                        had_error = 1;
 250        }
 251        return had_error;
 252}
 253
 254static int delete_tag(const char *name, const char *ref,
 255                                const unsigned char *sha1)
 256{
 257        if (delete_ref(ref, sha1, 0))
 258                return 1;
 259        printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(sha1, DEFAULT_ABBREV));
 260        return 0;
 261}
 262
 263static int verify_tag(const char *name, const char *ref,
 264                                const unsigned char *sha1)
 265{
 266        const char *argv_verify_tag[] = {"verify-tag",
 267                                        "-v", "SHA1_HEX", NULL};
 268        argv_verify_tag[2] = sha1_to_hex(sha1);
 269
 270        if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD))
 271                return error(_("could not verify the tag '%s'"), name);
 272        return 0;
 273}
 274
 275static int do_sign(struct strbuf *buffer)
 276{
 277        return sign_buffer(buffer, buffer, get_signing_key());
 278}
 279
 280static const char tag_template[] =
 281        N_("\nWrite a tag message\n"
 282        "Lines starting with '%c' will be ignored.\n");
 283
 284static const char tag_template_nocleanup[] =
 285        N_("\nWrite a tag message\n"
 286        "Lines starting with '%c' will be kept; you may remove them"
 287        " yourself if you want to.\n");
 288
 289static int git_tag_config(const char *var, const char *value, void *cb)
 290{
 291        int status = git_gpg_config(var, value, cb);
 292        if (status)
 293                return status;
 294        if (starts_with(var, "column."))
 295                return git_column_config(var, value, "tag", &colopts);
 296        return git_default_config(var, value, cb);
 297}
 298
 299static void write_tag_body(int fd, const unsigned char *sha1)
 300{
 301        unsigned long size;
 302        enum object_type type;
 303        char *buf, *sp;
 304
 305        buf = read_sha1_file(sha1, &type, &size);
 306        if (!buf)
 307                return;
 308        /* skip header */
 309        sp = strstr(buf, "\n\n");
 310
 311        if (!sp || !size || type != OBJ_TAG) {
 312                free(buf);
 313                return;
 314        }
 315        sp += 2; /* skip the 2 LFs */
 316        write_or_die(fd, sp, parse_signature(sp, buf + size - sp));
 317
 318        free(buf);
 319}
 320
 321static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result)
 322{
 323        if (sign && do_sign(buf) < 0)
 324                return error(_("unable to sign the tag"));
 325        if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0)
 326                return error(_("unable to write tag file"));
 327        return 0;
 328}
 329
 330struct create_tag_options {
 331        unsigned int message_given:1;
 332        unsigned int sign;
 333        enum {
 334                CLEANUP_NONE,
 335                CLEANUP_SPACE,
 336                CLEANUP_ALL
 337        } cleanup_mode;
 338};
 339
 340static void create_tag(const unsigned char *object, const char *tag,
 341                       struct strbuf *buf, struct create_tag_options *opt,
 342                       unsigned char *prev, unsigned char *result)
 343{
 344        enum object_type type;
 345        char header_buf[1024];
 346        int header_len;
 347        char *path = NULL;
 348
 349        type = sha1_object_info(object, NULL);
 350        if (type <= OBJ_NONE)
 351            die(_("bad object type."));
 352
 353        header_len = snprintf(header_buf, sizeof(header_buf),
 354                          "object %s\n"
 355                          "type %s\n"
 356                          "tag %s\n"
 357                          "tagger %s\n\n",
 358                          sha1_to_hex(object),
 359                          typename(type),
 360                          tag,
 361                          git_committer_info(IDENT_STRICT));
 362
 363        if (header_len > sizeof(header_buf) - 1)
 364                die(_("tag header too big."));
 365
 366        if (!opt->message_given) {
 367                int fd;
 368
 369                /* write the template message before editing: */
 370                path = git_pathdup("TAG_EDITMSG");
 371                fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 372                if (fd < 0)
 373                        die_errno(_("could not create file '%s'"), path);
 374
 375                if (!is_null_sha1(prev)) {
 376                        write_tag_body(fd, prev);
 377                } else {
 378                        struct strbuf buf = STRBUF_INIT;
 379                        strbuf_addch(&buf, '\n');
 380                        if (opt->cleanup_mode == CLEANUP_ALL)
 381                                strbuf_commented_addf(&buf, _(tag_template), comment_line_char);
 382                        else
 383                                strbuf_commented_addf(&buf, _(tag_template_nocleanup), comment_line_char);
 384                        write_or_die(fd, buf.buf, buf.len);
 385                        strbuf_release(&buf);
 386                }
 387                close(fd);
 388
 389                if (launch_editor(path, buf, NULL)) {
 390                        fprintf(stderr,
 391                        _("Please supply the message using either -m or -F option.\n"));
 392                        exit(1);
 393                }
 394        }
 395
 396        if (opt->cleanup_mode != CLEANUP_NONE)
 397                stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
 398
 399        if (!opt->message_given && !buf->len)
 400                die(_("no tag message?"));
 401
 402        strbuf_insert(buf, 0, header_buf, header_len);
 403
 404        if (build_tag_object(buf, opt->sign, result) < 0) {
 405                if (path)
 406                        fprintf(stderr, _("The tag message has been left in %s\n"),
 407                                path);
 408                exit(128);
 409        }
 410        if (path) {
 411                unlink_or_warn(path);
 412                free(path);
 413        }
 414}
 415
 416struct msg_arg {
 417        int given;
 418        struct strbuf buf;
 419};
 420
 421static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 422{
 423        struct msg_arg *msg = opt->value;
 424
 425        if (!arg)
 426                return -1;
 427        if (msg->buf.len)
 428                strbuf_addstr(&(msg->buf), "\n\n");
 429        strbuf_addstr(&(msg->buf), arg);
 430        msg->given = 1;
 431        return 0;
 432}
 433
 434static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
 435{
 436        if (name[0] == '-')
 437                return -1;
 438
 439        strbuf_reset(sb);
 440        strbuf_addf(sb, "refs/tags/%s", name);
 441
 442        return check_refname_format(sb->buf, 0);
 443}
 444
 445static int parse_opt_points_at(const struct option *opt __attribute__((unused)),
 446                        const char *arg, int unset)
 447{
 448        unsigned char sha1[20];
 449
 450        if (unset) {
 451                sha1_array_clear(&points_at);
 452                return 0;
 453        }
 454        if (!arg)
 455                return error(_("switch 'points-at' requires an object"));
 456        if (get_sha1(arg, sha1))
 457                return error(_("malformed object name '%s'"), arg);
 458        sha1_array_append(&points_at, sha1);
 459        return 0;
 460}
 461
 462static int parse_opt_sort(const struct option *opt, const char *arg, int unset)
 463{
 464        int *sort = opt->value;
 465        int flags = 0;
 466
 467        if (*arg == '-') {
 468                flags |= REVERSE_SORT;
 469                arg++;
 470        }
 471        if (starts_with(arg, "version:")) {
 472                *sort = VERCMP_SORT;
 473                arg += 8;
 474        } else if (starts_with(arg, "v:")) {
 475                *sort = VERCMP_SORT;
 476                arg += 2;
 477        } else
 478                *sort = STRCMP_SORT;
 479        if (strcmp(arg, "refname"))
 480                die(_("unsupported sort specification %s"), arg);
 481        *sort |= flags;
 482        return 0;
 483}
 484
 485int cmd_tag(int argc, const char **argv, const char *prefix)
 486{
 487        struct strbuf buf = STRBUF_INIT;
 488        struct strbuf ref = STRBUF_INIT;
 489        unsigned char object[20], prev[20];
 490        const char *object_ref, *tag;
 491        struct ref_lock *lock;
 492        struct create_tag_options opt;
 493        char *cleanup_arg = NULL;
 494        int annotate = 0, force = 0, lines = -1;
 495        int cmdmode = 0, sort = 0;
 496        const char *msgfile = NULL, *keyid = NULL;
 497        struct msg_arg msg = { 0, STRBUF_INIT };
 498        struct commit_list *with_commit = NULL;
 499        struct option options[] = {
 500                OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
 501                { OPTION_INTEGER, 'n', NULL, &lines, N_("n"),
 502                                N_("print <n> lines of each tag message"),
 503                                PARSE_OPT_OPTARG, NULL, 1 },
 504                OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'),
 505                OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'),
 506
 507                OPT_GROUP(N_("Tag creation options")),
 508                OPT_BOOL('a', "annotate", &annotate,
 509                                        N_("annotated tag, needs a message")),
 510                OPT_CALLBACK('m', "message", &msg, N_("message"),
 511                             N_("tag message"), parse_msg_arg),
 512                OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
 513                OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
 514                OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
 515                        N_("how to strip spaces and #comments from message")),
 516                OPT_STRING('u', "local-user", &keyid, N_("key id"),
 517                                        N_("use another key to sign the tag")),
 518                OPT__FORCE(&force, N_("replace the tag if exists")),
 519                OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
 520                {
 521                        OPTION_CALLBACK, 0, "sort", &sort, N_("type"), N_("sort tags"),
 522                        PARSE_OPT_NONEG, parse_opt_sort
 523                },
 524
 525                OPT_GROUP(N_("Tag listing options")),
 526                {
 527                        OPTION_CALLBACK, 0, "contains", &with_commit, N_("commit"),
 528                        N_("print only tags that contain the commit"),
 529                        PARSE_OPT_LASTARG_DEFAULT,
 530                        parse_opt_with_commit, (intptr_t)"HEAD",
 531                },
 532                {
 533                        OPTION_CALLBACK, 0, "with", &with_commit, N_("commit"),
 534                        N_("print only tags that contain the commit"),
 535                        PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
 536                        parse_opt_with_commit, (intptr_t)"HEAD",
 537                },
 538                {
 539                        OPTION_CALLBACK, 0, "points-at", NULL, N_("object"),
 540                        N_("print only tags of the object"), 0, parse_opt_points_at
 541                },
 542                OPT_END()
 543        };
 544
 545        git_config(git_tag_config, NULL);
 546
 547        memset(&opt, 0, sizeof(opt));
 548
 549        argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
 550
 551        if (keyid) {
 552                opt.sign = 1;
 553                set_signing_key(keyid);
 554        }
 555        if (opt.sign)
 556                annotate = 1;
 557        if (argc == 0 && !cmdmode)
 558                cmdmode = 'l';
 559
 560        if ((annotate || msg.given || msgfile || force) && (cmdmode != 0))
 561                usage_with_options(git_tag_usage, options);
 562
 563        finalize_colopts(&colopts, -1);
 564        if (cmdmode == 'l' && lines != -1) {
 565                if (explicitly_enable_column(colopts))
 566                        die(_("--column and -n are incompatible"));
 567                colopts = 0;
 568        }
 569        if (cmdmode == 'l') {
 570                int ret;
 571                if (column_active(colopts)) {
 572                        struct column_options copts;
 573                        memset(&copts, 0, sizeof(copts));
 574                        copts.padding = 2;
 575                        run_column_filter(colopts, &copts);
 576                }
 577                if (lines != -1 && sort)
 578                        die(_("--sort and -n are incompatible"));
 579                ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, sort);
 580                if (column_active(colopts))
 581                        stop_column_filter();
 582                return ret;
 583        }
 584        if (lines != -1)
 585                die(_("-n option is only allowed with -l."));
 586        if (with_commit)
 587                die(_("--contains option is only allowed with -l."));
 588        if (points_at.nr)
 589                die(_("--points-at option is only allowed with -l."));
 590        if (cmdmode == 'd')
 591                return for_each_tag_name(argv, delete_tag);
 592        if (cmdmode == 'v')
 593                return for_each_tag_name(argv, verify_tag);
 594
 595        if (msg.given || msgfile) {
 596                if (msg.given && msgfile)
 597                        die(_("only one -F or -m option is allowed."));
 598                annotate = 1;
 599                if (msg.given)
 600                        strbuf_addbuf(&buf, &(msg.buf));
 601                else {
 602                        if (!strcmp(msgfile, "-")) {
 603                                if (strbuf_read(&buf, 0, 1024) < 0)
 604                                        die_errno(_("cannot read '%s'"), msgfile);
 605                        } else {
 606                                if (strbuf_read_file(&buf, msgfile, 1024) < 0)
 607                                        die_errno(_("could not open or read '%s'"),
 608                                                msgfile);
 609                        }
 610                }
 611        }
 612
 613        tag = argv[0];
 614
 615        object_ref = argc == 2 ? argv[1] : "HEAD";
 616        if (argc > 2)
 617                die(_("too many params"));
 618
 619        if (get_sha1(object_ref, object))
 620                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
 621
 622        if (strbuf_check_tag_ref(&ref, tag))
 623                die(_("'%s' is not a valid tag name."), tag);
 624
 625        if (read_ref(ref.buf, prev))
 626                hashclr(prev);
 627        else if (!force)
 628                die(_("tag '%s' already exists"), tag);
 629
 630        opt.message_given = msg.given || msgfile;
 631
 632        if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
 633                opt.cleanup_mode = CLEANUP_ALL;
 634        else if (!strcmp(cleanup_arg, "verbatim"))
 635                opt.cleanup_mode = CLEANUP_NONE;
 636        else if (!strcmp(cleanup_arg, "whitespace"))
 637                opt.cleanup_mode = CLEANUP_SPACE;
 638        else
 639                die(_("Invalid cleanup mode %s"), cleanup_arg);
 640
 641        if (annotate)
 642                create_tag(object, tag, &buf, &opt, prev, object);
 643
 644        lock = lock_any_ref_for_update(ref.buf, prev, 0, NULL);
 645        if (!lock)
 646                die(_("%s: cannot lock the ref"), ref.buf);
 647        if (write_ref_sha1(lock, object, NULL) < 0)
 648                die(_("%s: cannot update the ref"), ref.buf);
 649        if (force && !is_null_sha1(prev) && hashcmp(prev, object))
 650                printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
 651
 652        strbuf_release(&buf);
 653        strbuf_release(&ref);
 654        return 0;
 655}