Merge branch 'tg/stash-push-fixup' into maint
[gitweb.git] / builtin / tag.c
index e40c4a96763a371b104a5bf47e6d839ed93e8ef5..bdf1e88e93a61b6cbe840f4c8dd4563c1e13e337 100644 (file)
@@ -22,7 +22,7 @@
 static const char * const git_tag_usage[] = {
        N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
        N_("git tag -d <tagname>..."),
-       N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]"
+       N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]"
                "\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
        N_("git tag -v [--format=<format>] <tagname>..."),
        NULL
@@ -45,11 +45,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
        if (!format) {
                if (filter->lines) {
                        to_free = xstrfmt("%s %%(contents:lines=%d)",
-                                         "%(align:15)%(refname:strip=2)%(end)",
+                                         "%(align:15)%(refname:lstrip=2)%(end)",
                                          filter->lines);
                        format = to_free;
                } else
-                       format = "%(refname:strip=2)";
+                       format = "%(refname:lstrip=2)";
        }
 
        verify_ref_format(format);
@@ -72,32 +72,29 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
                             const void *cb_data)
 {
        const char **p;
-       char ref[PATH_MAX];
+       struct strbuf ref = STRBUF_INIT;
        int had_error = 0;
        unsigned char sha1[20];
 
        for (p = argv; *p; p++) {
-               if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
-                                       >= sizeof(ref)) {
-                       error(_("tag name too long: %.*s..."), 50, *p);
-                       had_error = 1;
-                       continue;
-               }
-               if (read_ref(ref, sha1)) {
+               strbuf_reset(&ref);
+               strbuf_addf(&ref, "refs/tags/%s", *p);
+               if (read_ref(ref.buf, sha1)) {
                        error(_("tag '%s' not found."), *p);
                        had_error = 1;
                        continue;
                }
-               if (fn(*p, ref, sha1, cb_data))
+               if (fn(*p, ref.buf, sha1, cb_data))
                        had_error = 1;
        }
+       strbuf_release(&ref);
        return had_error;
 }
 
 static int delete_tag(const char *name, const char *ref,
                      const unsigned char *sha1, const void *cb_data)
 {
-       if (delete_ref(ref, sha1, 0))
+       if (delete_ref(NULL, ref, sha1, 0))
                return 1;
        printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(sha1, DEFAULT_ABBREV));
        return 0;
@@ -231,26 +228,22 @@ static void create_tag(const unsigned char *object, const char *tag,
                       unsigned char *prev, unsigned char *result)
 {
        enum object_type type;
-       char header_buf[1024];
-       int header_len;
+       struct strbuf header = STRBUF_INIT;
        char *path = NULL;
 
        type = sha1_object_info(object, NULL);
        if (type <= OBJ_NONE)
            die(_("bad object type."));
 
-       header_len = snprintf(header_buf, sizeof(header_buf),
-                         "object %s\n"
-                         "type %s\n"
-                         "tag %s\n"
-                         "tagger %s\n\n",
-                         sha1_to_hex(object),
-                         typename(type),
-                         tag,
-                         git_committer_info(IDENT_STRICT));
-
-       if (header_len > sizeof(header_buf) - 1)
-               die(_("tag header too big."));
+       strbuf_addf(&header,
+                   "object %s\n"
+                   "type %s\n"
+                   "tag %s\n"
+                   "tagger %s\n\n",
+                   sha1_to_hex(object),
+                   typename(type),
+                   tag,
+                   git_committer_info(IDENT_STRICT));
 
        if (!opt->message_given) {
                int fd;
@@ -288,7 +281,8 @@ static void create_tag(const unsigned char *object, const char *tag,
        if (!opt->message_given && !buf->len)
                die(_("no tag message?"));
 
-       strbuf_insert(buf, 0, header_buf, header_len);
+       strbuf_insert(buf, 0, header.buf, header.len);
+       strbuf_release(&header);
 
        if (build_tag_object(buf, opt->sign, result) < 0) {
                if (path)
@@ -302,6 +296,54 @@ static void create_tag(const unsigned char *object, const char *tag,
        }
 }
 
+static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb)
+{
+       enum object_type type;
+       struct commit *c;
+       char *buf;
+       unsigned long size;
+       int subject_len = 0;
+       const char *subject_start;
+
+       char *rla = getenv("GIT_REFLOG_ACTION");
+       if (rla) {
+               strbuf_addstr(sb, rla);
+       } else {
+               strbuf_addstr(sb, "tag: tagging ");
+               strbuf_add_unique_abbrev(sb, sha1, DEFAULT_ABBREV);
+       }
+
+       strbuf_addstr(sb, " (");
+       type = sha1_object_info(sha1, NULL);
+       switch (type) {
+       default:
+               strbuf_addstr(sb, "object of unknown type");
+               break;
+       case OBJ_COMMIT:
+               if ((buf = read_sha1_file(sha1, &type, &size)) != NULL) {
+                       subject_len = find_commit_subject(buf, &subject_start);
+                       strbuf_insert(sb, sb->len, subject_start, subject_len);
+               } else {
+                       strbuf_addstr(sb, "commit object");
+               }
+               free(buf);
+
+               if ((c = lookup_commit_reference(sha1)) != NULL)
+                       strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT)));
+               break;
+       case OBJ_TREE:
+               strbuf_addstr(sb, "tree object");
+               break;
+       case OBJ_BLOB:
+               strbuf_addstr(sb, "blob object");
+               break;
+       case OBJ_TAG:
+               strbuf_addstr(sb, "other tag object");
+               break;
+       }
+       strbuf_addch(sb, ')');
+}
+
 struct msg_arg {
        int given;
        struct strbuf buf;
@@ -335,6 +377,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 {
        struct strbuf buf = STRBUF_INIT;
        struct strbuf ref = STRBUF_INIT;
+       struct strbuf reflog_msg = STRBUF_INIT;
        unsigned char object[20], prev[20];
        const char *object_ref, *tag;
        struct create_tag_options opt;
@@ -375,20 +418,25 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT_GROUP(N_("Tag listing options")),
                OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
                OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
+               OPT_NO_CONTAINS(&filter.no_commit, N_("print only tags that don't contain the commit")),
                OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
+               OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
                OPT_MERGED(&filter, N_("print only tags that are merged")),
                OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
                OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
                             N_("field name to sort on"), &parse_opt_ref_sorting),
                {
                        OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
-                       N_("print only tags of the object"), 0, parse_opt_object_name
+                       N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
+                       parse_opt_object_name, (intptr_t) "HEAD"
                },
                OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
                OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
                OPT_END()
        };
 
+       setup_ref_filter_porcelain_msg();
+
        git_config(git_tag_config, sorting_tail);
 
        memset(&opt, 0, sizeof(opt));
@@ -403,8 +451,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        }
        create_tag_object = (opt.sign || annotate || msg.given || msgfile);
 
-       if (argc == 0 && !cmdmode)
-               cmdmode = 'l';
+       if (!cmdmode) {
+               if (argc == 0)
+                       cmdmode = 'l';
+               else if (filter.with_commit || filter.no_commit ||
+                        filter.points_at.nr || filter.merge_commit ||
+                        filter.lines != -1)
+                       cmdmode = 'l';
+       }
 
        if ((create_tag_object || force) && (cmdmode != 0))
                usage_with_options(git_tag_usage, options);
@@ -434,13 +488,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                return ret;
        }
        if (filter.lines != -1)
-               die(_("-n option is only allowed with -l."));
+               die(_("-n option is only allowed in list mode"));
        if (filter.with_commit)
-               die(_("--contains option is only allowed with -l."));
+               die(_("--contains option is only allowed in list mode"));
+       if (filter.no_commit)
+               die(_("--no-contains option is only allowed in list mode"));
        if (filter.points_at.nr)
-               die(_("--points-at option is only allowed with -l."));
+               die(_("--points-at option is only allowed in list mode"));
        if (filter.merge_commit)
-               die(_("--merged and --no-merged option are only allowed with -l"));
+               die(_("--merged and --no-merged options are only allowed in list mode"));
        if (cmdmode == 'd')
                return for_each_tag_name(argv, delete_tag, NULL);
        if (cmdmode == 'v') {
@@ -494,6 +550,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        else
                die(_("Invalid cleanup mode %s"), cleanup_arg);
 
+       create_reflog_msg(object, &reflog_msg);
+
        if (create_tag_object) {
                if (force_sign_annotate && !annotate)
                        opt.sign = 1;
@@ -504,7 +562,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (!transaction ||
            ref_transaction_update(transaction, ref.buf, object, prev,
                                   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
-                                  NULL, &err) ||
+                                  reflog_msg.buf, &err) ||
            ref_transaction_commit(transaction, &err))
                die("%s", err.buf);
        ref_transaction_free(transaction);
@@ -514,5 +572,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        strbuf_release(&err);
        strbuf_release(&buf);
        strbuf_release(&ref);
+       strbuf_release(&reflog_msg);
        return 0;
 }