Merge branch 'maint'
[gitweb.git] / builtin-tag.c
index 50cafc814d3774bf358288c23cafbf2cbcecf673..080e04a8fc6fc1fc3f10f59a4c1f1aa067b81aa1 100644 (file)
@@ -26,6 +26,7 @@ static char signingkey[1000];
 struct tag_filter {
        const char *pattern;
        int lines;
+       struct commit_list *with_commit;
 };
 
 #define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
@@ -42,6 +43,16 @@ static int show_reference(const char *refname, const unsigned char *sha1,
                char *buf, *sp, *eol;
                size_t len;
 
+               if (filter->with_commit) {
+                       struct commit *commit;
+
+                       commit = lookup_commit_reference_gently(sha1, 1);
+                       if (!commit)
+                               return 0;
+                       if (!is_descendant_of(commit, filter->with_commit))
+                               return 0;
+               }
+
                if (!filter->lines) {
                        printf("%s\n", refname);
                        return 0;
@@ -79,7 +90,8 @@ static int show_reference(const char *refname, const unsigned char *sha1,
        return 0;
 }
 
-static int list_tags(const char *pattern, int lines)
+static int list_tags(const char *pattern, int lines,
+                       struct commit_list *with_commit)
 {
        struct tag_filter filter;
 
@@ -88,6 +100,7 @@ static int list_tags(const char *pattern, int lines)
 
        filter.pattern = pattern;
        filter.lines = lines;
+       filter.with_commit = with_commit;
 
        for_each_tag_ref(show_reference, (void *) &filter);
 
@@ -125,7 +138,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
 static int delete_tag(const char *name, const char *ref,
                                const unsigned char *sha1)
 {
-       if (delete_ref(ref, sha1))
+       if (delete_ref(ref, sha1, 0))
                return 1;
        printf("Deleted tag '%s'\n", name);
        return 0;
@@ -253,6 +266,15 @@ static void write_tag_body(int fd, const unsigned char *sha1)
        free(buf);
 }
 
+static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result)
+{
+       if (sign && do_sign(buf) < 0)
+               return error("unable to sign the tag");
+       if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0)
+               return error("unable to write tag file");
+       return 0;
+}
+
 static void create_tag(const unsigned char *object, const char *tag,
                       struct strbuf *buf, int message, int sign,
                       unsigned char *prev, unsigned char *result)
@@ -260,6 +282,7 @@ static void create_tag(const unsigned char *object, const char *tag,
        enum object_type type;
        char header_buf[1024];
        int header_len;
+       char *path = NULL;
 
        type = sha1_object_info(object, NULL);
        if (type <= OBJ_NONE)
@@ -279,11 +302,10 @@ static void create_tag(const unsigned char *object, const char *tag,
                die("tag header too big.");
 
        if (!message) {
-               char *path;
                int fd;
 
                /* write the template message before editing: */
-               path = xstrdup(git_path("TAG_EDITMSG"));
+               path = git_pathdup("TAG_EDITMSG");
                fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
                if (fd < 0)
                        die("could not create file '%s': %s",
@@ -300,9 +322,6 @@ static void create_tag(const unsigned char *object, const char *tag,
                        "Please supply the message using either -m or -F option.\n");
                        exit(1);
                }
-
-               unlink(path);
-               free(path);
        }
 
        stripspace(buf, 1);
@@ -312,10 +331,16 @@ static void create_tag(const unsigned char *object, const char *tag,
 
        strbuf_insert(buf, 0, header_buf, header_len);
 
-       if (sign && do_sign(buf) < 0)
-               die("unable to sign the tag");
-       if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0)
-               die("unable to write tag file");
+       if (build_tag_object(buf, sign, result) < 0) {
+               if (path)
+                       fprintf(stderr, "The tag message has been left in %s\n",
+                               path);
+               exit(128);
+       }
+       if (path) {
+               unlink_or_warn(path);
+               free(path);
+       }
 }
 
 struct msg_arg {
@@ -338,7 +363,7 @@ static int parse_msg_arg(const struct option *opt, const char *arg, int unset)
 
 int cmd_tag(int argc, const char **argv, const char *prefix)
 {
-       struct strbuf buf;
+       struct strbuf buf = STRBUF_INIT;
        unsigned char object[20], prev[20];
        char ref[PATH_MAX];
        const char *object_ref, *tag;
@@ -348,10 +373,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                list = 0, delete = 0, verify = 0;
        const char *msgfile = NULL, *keyid = NULL;
        struct msg_arg msg = { 0, STRBUF_INIT };
+       struct commit_list *with_commit = NULL;
        struct option options[] = {
                OPT_BOOLEAN('l', NULL, &list, "list tag names"),
-               { OPTION_INTEGER, 'n', NULL, &lines, NULL,
-                               "print n lines of each tag message",
+               { OPTION_INTEGER, 'n', NULL, &lines, "n",
+                               "print <n> lines of each tag message",
                                PARSE_OPT_OPTARG, NULL, 1 },
                OPT_BOOLEAN('d', NULL, &delete, "delete tags"),
                OPT_BOOLEAN('v', NULL, &verify, "verify tags"),
@@ -361,18 +387,25 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                                        "annotated tag, needs a message"),
                OPT_CALLBACK('m', NULL, &msg, "msg",
                             "message for the tag", parse_msg_arg),
-               OPT_STRING('F', NULL, &msgfile, "file", "message in a file"),
+               OPT_FILENAME('F', NULL, &msgfile, "message in a file"),
                OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
                OPT_STRING('u', NULL, &keyid, "key-id",
                                        "use another key to sign the tag"),
                OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
+
+               OPT_GROUP("Tag listing options"),
+               {
+                       OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
+                       "print only tags that contain the commit",
+                       PARSE_OPT_LASTARG_DEFAULT,
+                       parse_opt_with_commit, (intptr_t)"HEAD",
+               },
                OPT_END()
        };
 
        git_config(git_tag_config, NULL);
 
-       argc = parse_options(argc, argv, options, git_tag_usage, 0);
-       msgfile = parse_options_fix_filename(prefix, msgfile);
+       argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
 
        if (keyid) {
                sign = 1;
@@ -390,15 +423,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (list + delete + verify > 1)
                usage_with_options(git_tag_usage, options);
        if (list)
-               return list_tags(argv[0], lines == -1 ? 0 : lines);
+               return list_tags(argv[0], lines == -1 ? 0 : lines,
+                                with_commit);
        if (lines != -1)
                die("-n option is only allowed with -l.");
+       if (with_commit)
+               die("--contains option is only allowed with -l.");
        if (delete)
                return for_each_tag_name(argv, delete_tag);
        if (verify)
                return for_each_tag_name(argv, verify_tag);
 
-       strbuf_init(&buf, 0);
        if (msg.given || msgfile) {
                if (msg.given && msgfile)
                        die("only one -F or -m option is allowed.");