216629fb2ce0a3b8e2ef457c9f3b823ab12a4b0f
   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 "config.h"
  11#include "builtin.h"
  12#include "refs.h"
  13#include "tag.h"
  14#include "run-command.h"
  15#include "parse-options.h"
  16#include "diff.h"
  17#include "revision.h"
  18#include "gpg-interface.h"
  19#include "sha1-array.h"
  20#include "column.h"
  21#include "ref-filter.h"
  22
  23static const char * const git_tag_usage[] = {
  24        N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
  25        N_("git tag -d <tagname>..."),
  26        N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]"
  27                "\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
  28        N_("git tag -v [--format=<format>] <tagname>..."),
  29        NULL
  30};
  31
  32static unsigned int colopts;
  33static int force_sign_annotate;
  34
  35static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
  36{
  37        struct ref_array array;
  38        char *to_free = NULL;
  39        int i;
  40
  41        memset(&array, 0, sizeof(array));
  42
  43        if (filter->lines == -1)
  44                filter->lines = 0;
  45
  46        if (!format) {
  47                if (filter->lines) {
  48                        to_free = xstrfmt("%s %%(contents:lines=%d)",
  49                                          "%(align:15)%(refname:lstrip=2)%(end)",
  50                                          filter->lines);
  51                        format = to_free;
  52                } else
  53                        format = "%(refname:lstrip=2)";
  54        }
  55
  56        if (verify_ref_format(format))
  57                die(_("unable to parse format string"));
  58        filter->with_commit_tag_algo = 1;
  59        filter_refs(&array, filter, FILTER_REFS_TAGS);
  60        ref_array_sort(sorting, &array);
  61
  62        for (i = 0; i < array.nr; i++)
  63                show_ref_array_item(array.items[i], format, 0);
  64        ref_array_clear(&array);
  65        free(to_free);
  66
  67        return 0;
  68}
  69
  70typedef int (*each_tag_name_fn)(const char *name, const char *ref,
  71                                const struct object_id *oid, const void *cb_data);
  72
  73static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
  74                             const void *cb_data)
  75{
  76        const char **p;
  77        struct strbuf ref = STRBUF_INIT;
  78        int had_error = 0;
  79        struct object_id oid;
  80
  81        for (p = argv; *p; p++) {
  82                strbuf_reset(&ref);
  83                strbuf_addf(&ref, "refs/tags/%s", *p);
  84                if (read_ref(ref.buf, oid.hash)) {
  85                        error(_("tag '%s' not found."), *p);
  86                        had_error = 1;
  87                        continue;
  88                }
  89                if (fn(*p, ref.buf, &oid, cb_data))
  90                        had_error = 1;
  91        }
  92        strbuf_release(&ref);
  93        return had_error;
  94}
  95
  96static int delete_tag(const char *name, const char *ref,
  97                      const struct object_id *oid, const void *cb_data)
  98{
  99        if (delete_ref(NULL, ref, oid->hash, 0))
 100                return 1;
 101        printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
 102        return 0;
 103}
 104
 105static int verify_tag(const char *name, const char *ref,
 106                      const struct object_id *oid, const void *cb_data)
 107{
 108        int flags;
 109        const char *fmt_pretty = cb_data;
 110        flags = GPG_VERIFY_VERBOSE;
 111
 112        if (fmt_pretty)
 113                flags = GPG_VERIFY_OMIT_STATUS;
 114
 115        if (gpg_verify_tag(oid->hash, name, flags))
 116                return -1;
 117
 118        if (fmt_pretty)
 119                pretty_print_ref(name, oid->hash, fmt_pretty);
 120
 121        return 0;
 122}
 123
 124static int do_sign(struct strbuf *buffer)
 125{
 126        return sign_buffer(buffer, buffer, get_signing_key());
 127}
 128
 129static const char tag_template[] =
 130        N_("\nWrite a message for tag:\n  %s\n"
 131        "Lines starting with '%c' will be ignored.\n");
 132
 133static const char tag_template_nocleanup[] =
 134        N_("\nWrite a message for tag:\n  %s\n"
 135        "Lines starting with '%c' will be kept; you may remove them"
 136        " yourself if you want to.\n");
 137
 138/* Parse arg given and add it the ref_sorting array */
 139static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)
 140{
 141        struct ref_sorting *s;
 142        int len;
 143
 144        s = xcalloc(1, sizeof(*s));
 145        s->next = *sorting_tail;
 146        *sorting_tail = s;
 147
 148        if (*arg == '-') {
 149                s->reverse = 1;
 150                arg++;
 151        }
 152        if (skip_prefix(arg, "version:", &arg) ||
 153            skip_prefix(arg, "v:", &arg))
 154                s->version = 1;
 155
 156        len = strlen(arg);
 157        s->atom = parse_ref_filter_atom(arg, arg+len);
 158
 159        return 0;
 160}
 161
 162static int git_tag_config(const char *var, const char *value, void *cb)
 163{
 164        int status;
 165        struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
 166
 167        if (!strcmp(var, "tag.sort")) {
 168                if (!value)
 169                        return config_error_nonbool(var);
 170                parse_sorting_string(value, sorting_tail);
 171                return 0;
 172        }
 173
 174        status = git_gpg_config(var, value, cb);
 175        if (status)
 176                return status;
 177        if (!strcmp(var, "tag.forcesignannotated")) {
 178                force_sign_annotate = git_config_bool(var, value);
 179                return 0;
 180        }
 181
 182        if (starts_with(var, "column."))
 183                return git_column_config(var, value, "tag", &colopts);
 184        return git_default_config(var, value, cb);
 185}
 186
 187static void write_tag_body(int fd, const struct object_id *oid)
 188{
 189        unsigned long size;
 190        enum object_type type;
 191        char *buf, *sp;
 192
 193        buf = read_sha1_file(oid->hash, &type, &size);
 194        if (!buf)
 195                return;
 196        /* skip header */
 197        sp = strstr(buf, "\n\n");
 198
 199        if (!sp || !size || type != OBJ_TAG) {
 200                free(buf);
 201                return;
 202        }
 203        sp += 2; /* skip the 2 LFs */
 204        write_or_die(fd, sp, parse_signature(sp, buf + size - sp));
 205
 206        free(buf);
 207}
 208
 209static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
 210{
 211        if (sign && do_sign(buf) < 0)
 212                return error(_("unable to sign the tag"));
 213        if (write_sha1_file(buf->buf, buf->len, tag_type, result->hash) < 0)
 214                return error(_("unable to write tag file"));
 215        return 0;
 216}
 217
 218struct create_tag_options {
 219        unsigned int message_given:1;
 220        unsigned int sign;
 221        enum {
 222                CLEANUP_NONE,
 223                CLEANUP_SPACE,
 224                CLEANUP_ALL
 225        } cleanup_mode;
 226};
 227
 228static void create_tag(const struct object_id *object, const char *tag,
 229                       struct strbuf *buf, struct create_tag_options *opt,
 230                       struct object_id *prev, struct object_id *result)
 231{
 232        enum object_type type;
 233        struct strbuf header = STRBUF_INIT;
 234        char *path = NULL;
 235
 236        type = sha1_object_info(object->hash, NULL);
 237        if (type <= OBJ_NONE)
 238            die(_("bad object type."));
 239
 240        strbuf_addf(&header,
 241                    "object %s\n"
 242                    "type %s\n"
 243                    "tag %s\n"
 244                    "tagger %s\n\n",
 245                    oid_to_hex(object),
 246                    typename(type),
 247                    tag,
 248                    git_committer_info(IDENT_STRICT));
 249
 250        if (!opt->message_given) {
 251                int fd;
 252
 253                /* write the template message before editing: */
 254                path = git_pathdup("TAG_EDITMSG");
 255                fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 256                if (fd < 0)
 257                        die_errno(_("could not create file '%s'"), path);
 258
 259                if (!is_null_oid(prev)) {
 260                        write_tag_body(fd, prev);
 261                } else {
 262                        struct strbuf buf = STRBUF_INIT;
 263                        strbuf_addch(&buf, '\n');
 264                        if (opt->cleanup_mode == CLEANUP_ALL)
 265                                strbuf_commented_addf(&buf, _(tag_template), tag, comment_line_char);
 266                        else
 267                                strbuf_commented_addf(&buf, _(tag_template_nocleanup), tag, comment_line_char);
 268                        write_or_die(fd, buf.buf, buf.len);
 269                        strbuf_release(&buf);
 270                }
 271                close(fd);
 272
 273                if (launch_editor(path, buf, NULL)) {
 274                        fprintf(stderr,
 275                        _("Please supply the message using either -m or -F option.\n"));
 276                        exit(1);
 277                }
 278        }
 279
 280        if (opt->cleanup_mode != CLEANUP_NONE)
 281                strbuf_stripspace(buf, opt->cleanup_mode == CLEANUP_ALL);
 282
 283        if (!opt->message_given && !buf->len)
 284                die(_("no tag message?"));
 285
 286        strbuf_insert(buf, 0, header.buf, header.len);
 287        strbuf_release(&header);
 288
 289        if (build_tag_object(buf, opt->sign, result) < 0) {
 290                if (path)
 291                        fprintf(stderr, _("The tag message has been left in %s\n"),
 292                                path);
 293                exit(128);
 294        }
 295        if (path) {
 296                unlink_or_warn(path);
 297                free(path);
 298        }
 299}
 300
 301static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
 302{
 303        enum object_type type;
 304        struct commit *c;
 305        char *buf;
 306        unsigned long size;
 307        int subject_len = 0;
 308        const char *subject_start;
 309
 310        char *rla = getenv("GIT_REFLOG_ACTION");
 311        if (rla) {
 312                strbuf_addstr(sb, rla);
 313        } else {
 314                strbuf_addstr(sb, "tag: tagging ");
 315                strbuf_add_unique_abbrev(sb, oid->hash, DEFAULT_ABBREV);
 316        }
 317
 318        strbuf_addstr(sb, " (");
 319        type = sha1_object_info(oid->hash, NULL);
 320        switch (type) {
 321        default:
 322                strbuf_addstr(sb, "object of unknown type");
 323                break;
 324        case OBJ_COMMIT:
 325                if ((buf = read_sha1_file(oid->hash, &type, &size)) != NULL) {
 326                        subject_len = find_commit_subject(buf, &subject_start);
 327                        strbuf_insert(sb, sb->len, subject_start, subject_len);
 328                } else {
 329                        strbuf_addstr(sb, "commit object");
 330                }
 331                free(buf);
 332
 333                if ((c = lookup_commit_reference(oid)) != NULL)
 334                        strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT)));
 335                break;
 336        case OBJ_TREE:
 337                strbuf_addstr(sb, "tree object");
 338                break;
 339        case OBJ_BLOB:
 340                strbuf_addstr(sb, "blob object");
 341                break;
 342        case OBJ_TAG:
 343                strbuf_addstr(sb, "other tag object");
 344                break;
 345        }
 346        strbuf_addch(sb, ')');
 347}
 348
 349struct msg_arg {
 350        int given;
 351        struct strbuf buf;
 352};
 353
 354static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 355{
 356        struct msg_arg *msg = opt->value;
 357
 358        if (!arg)
 359                return -1;
 360        if (msg->buf.len)
 361                strbuf_addstr(&(msg->buf), "\n\n");
 362        strbuf_addstr(&(msg->buf), arg);
 363        msg->given = 1;
 364        return 0;
 365}
 366
 367static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
 368{
 369        if (name[0] == '-')
 370                return -1;
 371
 372        strbuf_reset(sb);
 373        strbuf_addf(sb, "refs/tags/%s", name);
 374
 375        return check_refname_format(sb->buf, 0);
 376}
 377
 378int cmd_tag(int argc, const char **argv, const char *prefix)
 379{
 380        struct strbuf buf = STRBUF_INIT;
 381        struct strbuf ref = STRBUF_INIT;
 382        struct strbuf reflog_msg = STRBUF_INIT;
 383        struct object_id object, prev;
 384        const char *object_ref, *tag;
 385        struct create_tag_options opt;
 386        char *cleanup_arg = NULL;
 387        int create_reflog = 0;
 388        int annotate = 0, force = 0;
 389        int cmdmode = 0, create_tag_object = 0;
 390        const char *msgfile = NULL, *keyid = NULL;
 391        struct msg_arg msg = { 0, STRBUF_INIT };
 392        struct ref_transaction *transaction;
 393        struct strbuf err = STRBUF_INIT;
 394        struct ref_filter filter;
 395        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
 396        const char *format = NULL;
 397        int icase = 0;
 398        struct option options[] = {
 399                OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
 400                { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
 401                                N_("print <n> lines of each tag message"),
 402                                PARSE_OPT_OPTARG, NULL, 1 },
 403                OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'),
 404                OPT_CMDMODE('v', "verify", &cmdmode, N_("verify tags"), 'v'),
 405
 406                OPT_GROUP(N_("Tag creation options")),
 407                OPT_BOOL('a', "annotate", &annotate,
 408                                        N_("annotated tag, needs a message")),
 409                OPT_CALLBACK('m', "message", &msg, N_("message"),
 410                             N_("tag message"), parse_msg_arg),
 411                OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
 412                OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
 413                OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
 414                        N_("how to strip spaces and #comments from message")),
 415                OPT_STRING('u', "local-user", &keyid, N_("key-id"),
 416                                        N_("use another key to sign the tag")),
 417                OPT__FORCE(&force, N_("replace the tag if exists")),
 418                OPT_BOOL(0, "create-reflog", &create_reflog, N_("create a reflog")),
 419
 420                OPT_GROUP(N_("Tag listing options")),
 421                OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
 422                OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
 423                OPT_NO_CONTAINS(&filter.no_commit, N_("print only tags that don't contain the commit")),
 424                OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
 425                OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
 426                OPT_MERGED(&filter, N_("print only tags that are merged")),
 427                OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
 428                OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
 429                             N_("field name to sort on"), &parse_opt_ref_sorting),
 430                {
 431                        OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
 432                        N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
 433                        parse_opt_object_name, (intptr_t) "HEAD"
 434                },
 435                OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
 436                OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
 437                OPT_END()
 438        };
 439
 440        setup_ref_filter_porcelain_msg();
 441
 442        git_config(git_tag_config, sorting_tail);
 443
 444        memset(&opt, 0, sizeof(opt));
 445        memset(&filter, 0, sizeof(filter));
 446        filter.lines = -1;
 447
 448        argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
 449
 450        if (keyid) {
 451                opt.sign = 1;
 452                set_signing_key(keyid);
 453        }
 454        create_tag_object = (opt.sign || annotate || msg.given || msgfile);
 455
 456        if (!cmdmode) {
 457                if (argc == 0)
 458                        cmdmode = 'l';
 459                else if (filter.with_commit || filter.no_commit ||
 460                         filter.points_at.nr || filter.merge_commit ||
 461                         filter.lines != -1)
 462                        cmdmode = 'l';
 463        }
 464
 465        if ((create_tag_object || force) && (cmdmode != 0))
 466                usage_with_options(git_tag_usage, options);
 467
 468        finalize_colopts(&colopts, -1);
 469        if (cmdmode == 'l' && filter.lines != -1) {
 470                if (explicitly_enable_column(colopts))
 471                        die(_("--column and -n are incompatible"));
 472                colopts = 0;
 473        }
 474        if (!sorting)
 475                sorting = ref_default_sorting();
 476        sorting->ignore_case = icase;
 477        filter.ignore_case = icase;
 478        if (cmdmode == 'l') {
 479                int ret;
 480                if (column_active(colopts)) {
 481                        struct column_options copts;
 482                        memset(&copts, 0, sizeof(copts));
 483                        copts.padding = 2;
 484                        run_column_filter(colopts, &copts);
 485                }
 486                filter.name_patterns = argv;
 487                ret = list_tags(&filter, sorting, format);
 488                if (column_active(colopts))
 489                        stop_column_filter();
 490                return ret;
 491        }
 492        if (filter.lines != -1)
 493                die(_("-n option is only allowed in list mode"));
 494        if (filter.with_commit)
 495                die(_("--contains option is only allowed in list mode"));
 496        if (filter.no_commit)
 497                die(_("--no-contains option is only allowed in list mode"));
 498        if (filter.points_at.nr)
 499                die(_("--points-at option is only allowed in list mode"));
 500        if (filter.merge_commit)
 501                die(_("--merged and --no-merged options are only allowed in list mode"));
 502        if (cmdmode == 'd')
 503                return for_each_tag_name(argv, delete_tag, NULL);
 504        if (cmdmode == 'v') {
 505                if (format && verify_ref_format(format))
 506                        usage_with_options(git_tag_usage, options);
 507                return for_each_tag_name(argv, verify_tag, format);
 508        }
 509
 510        if (msg.given || msgfile) {
 511                if (msg.given && msgfile)
 512                        die(_("only one -F or -m option is allowed."));
 513                if (msg.given)
 514                        strbuf_addbuf(&buf, &(msg.buf));
 515                else {
 516                        if (!strcmp(msgfile, "-")) {
 517                                if (strbuf_read(&buf, 0, 1024) < 0)
 518                                        die_errno(_("cannot read '%s'"), msgfile);
 519                        } else {
 520                                if (strbuf_read_file(&buf, msgfile, 1024) < 0)
 521                                        die_errno(_("could not open or read '%s'"),
 522                                                msgfile);
 523                        }
 524                }
 525        }
 526
 527        tag = argv[0];
 528
 529        object_ref = argc == 2 ? argv[1] : "HEAD";
 530        if (argc > 2)
 531                die(_("too many params"));
 532
 533        if (get_oid(object_ref, &object))
 534                die(_("Failed to resolve '%s' as a valid ref."), object_ref);
 535
 536        if (strbuf_check_tag_ref(&ref, tag))
 537                die(_("'%s' is not a valid tag name."), tag);
 538
 539        if (read_ref(ref.buf, prev.hash))
 540                oidclr(&prev);
 541        else if (!force)
 542                die(_("tag '%s' already exists"), tag);
 543
 544        opt.message_given = msg.given || msgfile;
 545
 546        if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
 547                opt.cleanup_mode = CLEANUP_ALL;
 548        else if (!strcmp(cleanup_arg, "verbatim"))
 549                opt.cleanup_mode = CLEANUP_NONE;
 550        else if (!strcmp(cleanup_arg, "whitespace"))
 551                opt.cleanup_mode = CLEANUP_SPACE;
 552        else
 553                die(_("Invalid cleanup mode %s"), cleanup_arg);
 554
 555        create_reflog_msg(&object, &reflog_msg);
 556
 557        if (create_tag_object) {
 558                if (force_sign_annotate && !annotate)
 559                        opt.sign = 1;
 560                create_tag(&object, tag, &buf, &opt, &prev, &object);
 561        }
 562
 563        transaction = ref_transaction_begin(&err);
 564        if (!transaction ||
 565            ref_transaction_update(transaction, ref.buf, object.hash, prev.hash,
 566                                   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
 567                                   reflog_msg.buf, &err) ||
 568            ref_transaction_commit(transaction, &err))
 569                die("%s", err.buf);
 570        ref_transaction_free(transaction);
 571        if (force && !is_null_oid(&prev) && oidcmp(&prev, &object))
 572                printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev.hash, DEFAULT_ABBREV));
 573
 574        strbuf_release(&err);
 575        strbuf_release(&buf);
 576        strbuf_release(&ref);
 577        strbuf_release(&reflog_msg);
 578        return 0;
 579}