builtin / tag.con commit Revert clock-skew based attempt to optimize tag --contains traversal (c6d72c4)
   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
  18static const char * const git_tag_usage[] = {
  19        "git tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
  20        "git tag -d <tagname>...",
  21        "git tag -l [-n[<num>]] [<pattern>]",
  22        "git tag -v <tagname>...",
  23        NULL
  24};
  25
  26static char signingkey[1000];
  27
  28struct tag_filter {
  29        const char *pattern;
  30        int lines;
  31        struct commit_list *with_commit;
  32};
  33
  34#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
  35
  36static int in_commit_list(const struct commit_list *want, struct commit *c)
  37{
  38        for (; want; want = want->next)
  39                if (!hashcmp(want->item->object.sha1, c->object.sha1))
  40                        return 1;
  41        return 0;
  42}
  43
  44static int contains_recurse(struct commit *candidate,
  45                            const struct commit_list *want)
  46{
  47        struct commit_list *p;
  48
  49        /* was it previously marked as containing a want commit? */
  50        if (candidate->object.flags & TMP_MARK)
  51                return 1;
  52        /* or marked as not possibly containing a want commit? */
  53        if (candidate->object.flags & UNINTERESTING)
  54                return 0;
  55        /* or are we it? */
  56        if (in_commit_list(want, candidate))
  57                return 1;
  58
  59        if (parse_commit(candidate) < 0)
  60                return 0;
  61
  62        /* Otherwise recurse and mark ourselves for future traversals. */
  63        for (p = candidate->parents; p; p = p->next) {
  64                if (contains_recurse(p->item, want)) {
  65                        candidate->object.flags |= TMP_MARK;
  66                        return 1;
  67                }
  68        }
  69        candidate->object.flags |= UNINTERESTING;
  70        return 0;
  71}
  72
  73static int contains(struct commit *candidate, const struct commit_list *want)
  74{
  75        return contains_recurse(candidate, want);
  76}
  77
  78static int show_reference(const char *refname, const unsigned char *sha1,
  79                          int flag, void *cb_data)
  80{
  81        struct tag_filter *filter = cb_data;
  82
  83        if (!fnmatch(filter->pattern, refname, 0)) {
  84                int i;
  85                unsigned long size;
  86                enum object_type type;
  87                char *buf, *sp, *eol;
  88                size_t len;
  89
  90                if (filter->with_commit) {
  91                        struct commit *commit;
  92
  93                        commit = lookup_commit_reference_gently(sha1, 1);
  94                        if (!commit)
  95                                return 0;
  96                        if (!contains(commit, filter->with_commit))
  97                                return 0;
  98                }
  99
 100                if (!filter->lines) {
 101                        printf("%s\n", refname);
 102                        return 0;
 103                }
 104                printf("%-15s ", refname);
 105
 106                buf = read_sha1_file(sha1, &type, &size);
 107                if (!buf || !size)
 108                        return 0;
 109
 110                /* skip header */
 111                sp = strstr(buf, "\n\n");
 112                if (!sp) {
 113                        free(buf);
 114                        return 0;
 115                }
 116                /* only take up to "lines" lines, and strip the signature */
 117                for (i = 0, sp += 2;
 118                                i < filter->lines && sp < buf + size &&
 119                                prefixcmp(sp, PGP_SIGNATURE "\n");
 120                                i++) {
 121                        if (i)
 122                                printf("\n    ");
 123                        eol = memchr(sp, '\n', size - (sp - buf));
 124                        len = eol ? eol - sp : size - (sp - buf);
 125                        fwrite(sp, len, 1, stdout);
 126                        if (!eol)
 127                                break;
 128                        sp = eol + 1;
 129                }
 130                putchar('\n');
 131                free(buf);
 132        }
 133
 134        return 0;
 135}
 136
 137static int list_tags(const char *pattern, int lines,
 138                        struct commit_list *with_commit)
 139{
 140        struct tag_filter filter;
 141
 142        if (pattern == NULL)
 143                pattern = "*";
 144
 145        filter.pattern = pattern;
 146        filter.lines = lines;
 147        filter.with_commit = with_commit;
 148
 149        for_each_tag_ref(show_reference, (void *) &filter);
 150
 151        return 0;
 152}
 153
 154typedef int (*each_tag_name_fn)(const char *name, const char *ref,
 155                                const unsigned char *sha1);
 156
 157static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
 158{
 159        const char **p;
 160        char ref[PATH_MAX];
 161        int had_error = 0;
 162        unsigned char sha1[20];
 163
 164        for (p = argv; *p; p++) {
 165                if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
 166                                        >= sizeof(ref)) {
 167                        error("tag name too long: %.*s...", 50, *p);
 168                        had_error = 1;
 169                        continue;
 170                }
 171                if (!resolve_ref(ref, sha1, 1, NULL)) {
 172                        error("tag '%s' not found.", *p);
 173                        had_error = 1;
 174                        continue;
 175                }
 176                if (fn(*p, ref, sha1))
 177                        had_error = 1;
 178        }
 179        return had_error;
 180}
 181
 182static int delete_tag(const char *name, const char *ref,
 183                                const unsigned char *sha1)
 184{
 185        if (delete_ref(ref, sha1, 0))
 186                return 1;
 187        printf("Deleted tag '%s' (was %s)\n", name, find_unique_abbrev(sha1, DEFAULT_ABBREV));
 188        return 0;
 189}
 190
 191static int verify_tag(const char *name, const char *ref,
 192                                const unsigned char *sha1)
 193{
 194        const char *argv_verify_tag[] = {"verify-tag",
 195                                        "-v", "SHA1_HEX", NULL};
 196        argv_verify_tag[2] = sha1_to_hex(sha1);
 197
 198        if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD))
 199                return error("could not verify the tag '%s'", name);
 200        return 0;
 201}
 202
 203static int do_sign(struct strbuf *buffer)
 204{
 205        struct child_process gpg;
 206        const char *args[4];
 207        char *bracket;
 208        int len;
 209        int i, j;
 210
 211        if (!*signingkey) {
 212                if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
 213                                sizeof(signingkey)) > sizeof(signingkey) - 1)
 214                        return error("committer info too long.");
 215                bracket = strchr(signingkey, '>');
 216                if (bracket)
 217                        bracket[1] = '\0';
 218        }
 219
 220        /* When the username signingkey is bad, program could be terminated
 221         * because gpg exits without reading and then write gets SIGPIPE. */
 222        signal(SIGPIPE, SIG_IGN);
 223
 224        memset(&gpg, 0, sizeof(gpg));
 225        gpg.argv = args;
 226        gpg.in = -1;
 227        gpg.out = -1;
 228        args[0] = "gpg";
 229        args[1] = "-bsau";
 230        args[2] = signingkey;
 231        args[3] = NULL;
 232
 233        if (start_command(&gpg))
 234                return error("could not run gpg.");
 235
 236        if (write_in_full(gpg.in, buffer->buf, buffer->len) != buffer->len) {
 237                close(gpg.in);
 238                close(gpg.out);
 239                finish_command(&gpg);
 240                return error("gpg did not accept the tag data");
 241        }
 242        close(gpg.in);
 243        len = strbuf_read(buffer, gpg.out, 1024);
 244        close(gpg.out);
 245
 246        if (finish_command(&gpg) || !len || len < 0)
 247                return error("gpg failed to sign the tag");
 248
 249        /* Strip CR from the line endings, in case we are on Windows. */
 250        for (i = j = 0; i < buffer->len; i++)
 251                if (buffer->buf[i] != '\r') {
 252                        if (i != j)
 253                                buffer->buf[j] = buffer->buf[i];
 254                        j++;
 255                }
 256        strbuf_setlen(buffer, j);
 257
 258        return 0;
 259}
 260
 261static const char tag_template[] =
 262        "\n"
 263        "#\n"
 264        "# Write a tag message\n"
 265        "#\n";
 266
 267static void set_signingkey(const char *value)
 268{
 269        if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey))
 270                die("signing key value too long (%.10s...)", value);
 271}
 272
 273static int git_tag_config(const char *var, const char *value, void *cb)
 274{
 275        if (!strcmp(var, "user.signingkey")) {
 276                if (!value)
 277                        return config_error_nonbool(var);
 278                set_signingkey(value);
 279                return 0;
 280        }
 281
 282        return git_default_config(var, value, cb);
 283}
 284
 285static void write_tag_body(int fd, const unsigned char *sha1)
 286{
 287        unsigned long size;
 288        enum object_type type;
 289        char *buf, *sp, *eob;
 290        size_t len;
 291
 292        buf = read_sha1_file(sha1, &type, &size);
 293        if (!buf)
 294                return;
 295        /* skip header */
 296        sp = strstr(buf, "\n\n");
 297
 298        if (!sp || !size || type != OBJ_TAG) {
 299                free(buf);
 300                return;
 301        }
 302        sp += 2; /* skip the 2 LFs */
 303        eob = strstr(sp, "\n" PGP_SIGNATURE "\n");
 304        if (eob)
 305                len = eob - sp;
 306        else
 307                len = buf + size - sp;
 308        write_or_die(fd, sp, len);
 309
 310        free(buf);
 311}
 312
 313static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result)
 314{
 315        if (sign && do_sign(buf) < 0)
 316                return error("unable to sign the tag");
 317        if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0)
 318                return error("unable to write tag file");
 319        return 0;
 320}
 321
 322static void create_tag(const unsigned char *object, const char *tag,
 323                       struct strbuf *buf, int message, int sign,
 324                       unsigned char *prev, unsigned char *result)
 325{
 326        enum object_type type;
 327        char header_buf[1024];
 328        int header_len;
 329        char *path = NULL;
 330
 331        type = sha1_object_info(object, NULL);
 332        if (type <= OBJ_NONE)
 333            die("bad object type.");
 334
 335        header_len = snprintf(header_buf, sizeof(header_buf),
 336                          "object %s\n"
 337                          "type %s\n"
 338                          "tag %s\n"
 339                          "tagger %s\n\n",
 340                          sha1_to_hex(object),
 341                          typename(type),
 342                          tag,
 343                          git_committer_info(IDENT_ERROR_ON_NO_NAME));
 344
 345        if (header_len > sizeof(header_buf) - 1)
 346                die("tag header too big.");
 347
 348        if (!message) {
 349                int fd;
 350
 351                /* write the template message before editing: */
 352                path = git_pathdup("TAG_EDITMSG");
 353                fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 354                if (fd < 0)
 355                        die_errno("could not create file '%s'", path);
 356
 357                if (!is_null_sha1(prev))
 358                        write_tag_body(fd, prev);
 359                else
 360                        write_or_die(fd, tag_template, strlen(tag_template));
 361                close(fd);
 362
 363                if (launch_editor(path, buf, NULL)) {
 364                        fprintf(stderr,
 365                        "Please supply the message using either -m or -F option.\n");
 366                        exit(1);
 367                }
 368        }
 369
 370        stripspace(buf, 1);
 371
 372        if (!message && !buf->len)
 373                die("no tag message?");
 374
 375        strbuf_insert(buf, 0, header_buf, header_len);
 376
 377        if (build_tag_object(buf, sign, result) < 0) {
 378                if (path)
 379                        fprintf(stderr, "The tag message has been left in %s\n",
 380                                path);
 381                exit(128);
 382        }
 383        if (path) {
 384                unlink_or_warn(path);
 385                free(path);
 386        }
 387}
 388
 389struct msg_arg {
 390        int given;
 391        struct strbuf buf;
 392};
 393
 394static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 395{
 396        struct msg_arg *msg = opt->value;
 397
 398        if (!arg)
 399                return -1;
 400        if (msg->buf.len)
 401                strbuf_addstr(&(msg->buf), "\n\n");
 402        strbuf_addstr(&(msg->buf), arg);
 403        msg->given = 1;
 404        return 0;
 405}
 406
 407int cmd_tag(int argc, const char **argv, const char *prefix)
 408{
 409        struct strbuf buf = STRBUF_INIT;
 410        unsigned char object[20], prev[20];
 411        char ref[PATH_MAX];
 412        const char *object_ref, *tag;
 413        struct ref_lock *lock;
 414
 415        int annotate = 0, sign = 0, force = 0, lines = -1,
 416                list = 0, delete = 0, verify = 0;
 417        const char *msgfile = NULL, *keyid = NULL;
 418        struct msg_arg msg = { 0, STRBUF_INIT };
 419        struct commit_list *with_commit = NULL;
 420        struct option options[] = {
 421                OPT_BOOLEAN('l', NULL, &list, "list tag names"),
 422                { OPTION_INTEGER, 'n', NULL, &lines, "n",
 423                                "print <n> lines of each tag message",
 424                                PARSE_OPT_OPTARG, NULL, 1 },
 425                OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
 426                OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
 427
 428                OPT_GROUP("Tag creation options"),
 429                OPT_BOOLEAN('a', NULL, &annotate,
 430                                        "annotated tag, needs a message"),
 431                OPT_CALLBACK('m', NULL, &msg, "msg",
 432                             "message for the tag", parse_msg_arg),
 433                OPT_FILENAME('F', NULL, &msgfile, "message in a file"),
 434                OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
 435                OPT_STRING('u', NULL, &keyid, "key-id",
 436                                        "use another key to sign the tag"),
 437                OPT_BOOLEAN('f', "force", &force, "replace the tag if exists"),
 438
 439                OPT_GROUP("Tag listing options"),
 440                {
 441                        OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
 442                        "print only tags that contain the commit",
 443                        PARSE_OPT_LASTARG_DEFAULT,
 444                        parse_opt_with_commit, (intptr_t)"HEAD",
 445                },
 446                OPT_END()
 447        };
 448
 449        git_config(git_tag_config, NULL);
 450
 451        argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
 452
 453        if (keyid) {
 454                sign = 1;
 455                set_signingkey(keyid);
 456        }
 457        if (sign)
 458                annotate = 1;
 459        if (argc == 0 && !(delete || verify))
 460                list = 1;
 461
 462        if ((annotate || msg.given || msgfile || force) &&
 463            (list || delete || verify))
 464                usage_with_options(git_tag_usage, options);
 465
 466        if (list + delete + verify > 1)
 467                usage_with_options(git_tag_usage, options);
 468        if (list)
 469                return list_tags(argv[0], lines == -1 ? 0 : lines,
 470                                 with_commit);
 471        if (lines != -1)
 472                die("-n option is only allowed with -l.");
 473        if (with_commit)
 474                die("--contains option is only allowed with -l.");
 475        if (delete)
 476                return for_each_tag_name(argv, delete_tag);
 477        if (verify)
 478                return for_each_tag_name(argv, verify_tag);
 479
 480        if (msg.given || msgfile) {
 481                if (msg.given && msgfile)
 482                        die("only one -F or -m option is allowed.");
 483                annotate = 1;
 484                if (msg.given)
 485                        strbuf_addbuf(&buf, &(msg.buf));
 486                else {
 487                        if (!strcmp(msgfile, "-")) {
 488                                if (strbuf_read(&buf, 0, 1024) < 0)
 489                                        die_errno("cannot read '%s'", msgfile);
 490                        } else {
 491                                if (strbuf_read_file(&buf, msgfile, 1024) < 0)
 492                                        die_errno("could not open or read '%s'",
 493                                                msgfile);
 494                        }
 495                }
 496        }
 497
 498        tag = argv[0];
 499
 500        object_ref = argc == 2 ? argv[1] : "HEAD";
 501        if (argc > 2)
 502                die("too many params");
 503
 504        if (get_sha1(object_ref, object))
 505                die("Failed to resolve '%s' as a valid ref.", object_ref);
 506
 507        if (snprintf(ref, sizeof(ref), "refs/tags/%s", tag) > sizeof(ref) - 1)
 508                die("tag name too long: %.*s...", 50, tag);
 509        if (check_ref_format(ref))
 510                die("'%s' is not a valid tag name.", tag);
 511
 512        if (!resolve_ref(ref, prev, 1, NULL))
 513                hashclr(prev);
 514        else if (!force)
 515                die("tag '%s' already exists", tag);
 516
 517        if (annotate)
 518                create_tag(object, tag, &buf, msg.given || msgfile,
 519                           sign, prev, object);
 520
 521        lock = lock_any_ref_for_update(ref, prev, 0);
 522        if (!lock)
 523                die("%s: cannot lock the ref", ref);
 524        if (write_ref_sha1(lock, object, NULL) < 0)
 525                die("%s: cannot update the ref", ref);
 526        if (force && hashcmp(prev, object))
 527                printf("Updated tag '%s' (was %s)\n", tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
 528
 529        strbuf_release(&buf);
 530        return 0;
 531}